Compare commits

...

366 Commits

Author SHA1 Message Date
jomjol
dfe2c466bc Merge branch 'rolling' 2022-09-29 19:08:24 +02:00
jomjol
bb924531ef v12.0.0 2022-09-29 19:07:18 +02:00
github-actions
6e24b6fa88 Update Changelog.md for release 2022-09-29 17:02:32 +00:00
jomjol
d9a7c197fc v12.0.0 2022-09-29 18:38:06 +02:00
jomjol
14d30bae9d Merge pull request #1111 from jomjol/rolling
v12.0.0
2022-09-29 18:36:32 +02:00
jomjol
97ef1904bb v12.0.0 2022-09-29 18:24:59 +02:00
George Ruinelli
0856aea069 moved issue template to right place 2022-09-28 13:07:43 +02:00
jomjol
73e9253cb7 Merge pull request #1104 from jomjol/delete-old-ota-page
Delete ota_page_old.html
2022-09-28 12:35:19 +02:00
jomjol
663e97db97 Merge pull request #1103 from jomjol/added-issue-templates
Added issue templates
2022-09-28 12:34:52 +02:00
CaCO3
8b64266602 Update bug_report.md 2022-09-28 12:09:20 +02:00
CaCO3
e697cc5ec8 Addjusted time notes in OTA page 2022-09-28 09:43:08 +02:00
CaCO3
2c4d5a42d8 Delete ota_page_old.html 2022-09-28 09:42:05 +02:00
CaCO3
2f2ec23a53 Create general_question.md 2022-09-28 08:40:52 +02:00
CaCO3
3ad72f39a6 Create bug_report.md 2022-09-28 08:39:55 +02:00
jomjol
88a074dfa9 Rolling 20220928 2022-09-28 07:16:56 +02:00
jomjol
8c5f956dfe Merge pull request #1100 from jomjol/formating
Update Changelog.md
2022-09-28 06:58:21 +02:00
jomjol
505eed8767 Merge pull request #1099 from caco3/improve-ota-page
updated OTA page
2022-09-28 06:57:25 +02:00
CaCO3
cc9a3d3551 Update Changelog.md 2022-09-27 23:48:24 +02:00
CaCO3
70b743e364 . 2022-09-27 23:10:31 +02:00
CaCO3
6307b7f278 added reload note 2022-09-27 23:06:45 +02:00
CaCO3
ed2ee7ad90 updated OTA page, added progress counter, made async, removed reboot button 2022-09-27 22:56:00 +02:00
jomjol
26789f4187 Merge pull request #1098 from jomjol/update
Update Changelog.md
2022-09-27 22:29:07 +02:00
jomjol
923fc20ca5 Merge branch 'rolling' into update 2022-09-27 22:29:02 +02:00
jomjol
6808a37b69 Merge pull request #1097 from haverland/rolling
Add description for update from rolling.
2022-09-27 22:25:27 +02:00
CaCO3
f996e65a4d Update Changelog.md 2022-09-27 20:53:51 +02:00
CaCO3
0168f6b9b7 Update Changelog.md 2022-09-27 20:52:10 +02:00
CaCO3
e6b8643124 Update Changelog.md 2022-09-27 20:48:05 +02:00
Frank Haverland
69a92712ef Merge branch 'jomjol:rolling' into rolling 2022-09-27 20:11:22 +02:00
Frank Haverland
93804cec3f fix numbering 2022-09-27 20:11:11 +02:00
Frank Haverland
8cd430efc7 add description to update rolling and a hint if anything bricks. 2022-09-27 20:06:54 +02:00
jomjol
8175c946f2 Rolling 20220927 2022-09-27 18:23:35 +02:00
jomjol
0eb70b6208 Merge pull request #1086 from haverland/rolling
Add file length check before upload ota
2022-09-27 18:13:03 +02:00
jomjol
5ac3378479 Merge pull request #1085 from caco3/cleanup-sdkconfig
Restructure sdkconfig
2022-09-27 18:10:36 +02:00
Frank Haverland
356b208f33 change max file characters to 100 2022-09-27 16:30:41 +02:00
Frank Haverland
924f03595b add number of max characters to the failure dialog in upload check 2022-09-27 16:23:11 +02:00
Frank Haverland
90595968b2 update release notes to update from 11.3.1 2022-09-26 23:19:09 +02:00
Frank Haverland
96a59ca429 add html.zip for update from 11.3.1 2022-09-26 23:04:25 +02:00
Frank Haverland
b76d6cd987 fix links 2022-09-26 22:42:56 +02:00
Frank Haverland
98b7224c65 Add suggestion on firefox 2022-09-26 22:39:29 +02:00
Frank Haverland
ece73f63c8 Merge branch 'jomjol:rolling' into rolling 2022-09-26 22:30:59 +02:00
Frank Haverland
b281ae9583 add check if filename longer than 30 characters,
fixed handling after error dialog
2022-09-26 22:29:48 +02:00
CaCO3
6af7e1edd2 Merge branch 'cleanup-sdkconfig' of https://github.com/caco3/AI-on-the-edge-device into cleanup-sdkconfig 2022-09-26 21:53:25 +02:00
CaCO3
f04ca2bdde We should not edit sdkconfig.esp32cam as it is an autogenerated file! Instead we can do our condfiguration in sdkconfig.defaults.
This commit moves all customization to sdkconfig.defaults. The generated sdkconfig.esp32cam is identical to what we had before.
2022-09-26 21:53:22 +02:00
CaCO3
9007d8c484 delet old files 2022-09-26 21:53:22 +02:00
CaCO3
d8a7e7a39d Merge branch 'rolling' of https://github.com/caco3/AI-on-the-edge-device into rolling
# Conflicts:
#	.github/workflows/build.yaml
2022-09-26 21:53:07 +02:00
CaCO3
ee13581475 We should not edit sdkconfig.esp32cam as it is an autogenerated file! Instead we can do our condfiguration in sdkconfig.defaults.
This commit moves all customization to sdkconfig.defaults. The generated sdkconfig.esp32cam is identical to what we had before.
2022-09-26 21:50:44 +02:00
CaCO3
8c86a3013a delet old files 2022-09-26 21:22:26 +02:00
jomjol
8aab81acc7 Merge pull request #1084 from haverland/rolling
Fix uploading on ota
2022-09-26 21:02:41 +02:00
Frank Haverland
8707190d16 Merge branch 'jomjol:rolling' into rolling 2022-09-26 20:08:48 +02:00
Frank Haverland
aa72cab84f reduce SCRATCH_BUFSIZE to fix upload errors 2022-09-26 19:57:59 +02:00
jomjol
29607a8617 Merge branch 'rolling' of https://github.com/jomjol/AI-on-the-edge-device into rolling 2022-09-26 18:19:20 +02:00
jomjol
2239ab9019 Update server_ota.cpp 2022-09-26 18:19:09 +02:00
jomjol
86ea8a8e26 Merge pull request #1077 from haverland/rolling
Cleanup build/release pipeline
2022-09-25 21:02:57 +02:00
CaCO3
bfa8d8e691 cleanup 2022-09-25 20:57:21 +02:00
Frank Haverland
dc788b14eb Merge branch 'jomjol:rolling' into rolling 2022-09-25 20:38:39 +02:00
Frank Haverland
228a87038e Link to the installtion guide in firmware-Readme. It will packed into the initial-setup.zip 2022-09-25 19:43:30 +02:00
jomjol
d11ee2a4cf Rolling 20220925 2022-09-25 19:39:10 +02:00
Frank Haverland
b38c940dd3 run release only on tagging 2022-09-25 19:33:58 +02:00
Frank Haverland
6a22d355eb Merge branch 'jomjol:rolling' into rolling 2022-09-25 19:26:38 +02:00
Frank Haverland
a3a46fb0bb remove release creation artefact from pack-for-fresh-install 2022-09-25 19:22:13 +02:00
Frank Haverland
452339a1f0 fix indent 2022-09-25 19:05:16 +02:00
Frank Haverland
5e93f1e8bd use caches from changer 2022-09-25 18:59:48 +02:00
Frank Haverland
2e4023cfd6 pack for fresh install now cleaner part of build 2022-09-25 18:46:35 +02:00
Frank Haverland
12c748534e cleanup initial-setup is now part of the release 2022-09-25 18:36:15 +02:00
jomjol
8275bdbc25 Merge pull request #1074 from haverland/rolling
Release creation like CaCo3's update-ota2
2022-09-25 18:27:28 +02:00
jomjol
0ffaf99e10 Merge pull request #1073 from caco3/improve-ota
Improve ota
2022-09-25 18:23:41 +02:00
Frank Haverland
b1b76feeb0 fix extracting directories 2022-09-25 17:56:04 +02:00
Frank Haverland
265616ff80 Merge branch 'master' into rolling 2022-09-25 17:22:41 +02:00
Frank Haverland
89c65f6226 add debug, changed upload size to 8MB 2022-09-25 17:22:24 +02:00
Frank Haverland
6634b36f4a new entries for the next release 2022-09-25 17:19:32 +02:00
github-actions
6c433d83fc Update Changelog.md for release 2022-09-25 15:06:17 +00:00
Frank Haverland
a9d77a0005 fix path 2022-09-25 16:40:53 +02:00
CaCO3
efb35b1aa7 corrected max upload size 2022-09-25 16:21:12 +02:00
Frank Haverland
6f9d21fc21 fix initial release.zip 2022-09-25 16:15:40 +02:00
Frank Haverland
f7df03f0c0 Merge branch 'master' of https://github.com/haverland/AI-on-the-edge-device 2022-09-25 16:14:22 +02:00
Frank Haverland
e92ff43e09 fix html.zip should part of inital setup.zip as part of sd-card 2022-09-25 16:14:12 +02:00
github-actions
592a52ef0b Update Changelog.md for release 2022-09-25 14:06:55 +00:00
CaCO3
4e19066e26 catch opening non-existing file for read on OTA 2022-09-25 15:59:29 +02:00
Frank Haverland
8e39e83c38 use new cache after pack-for-OTA-v2 2022-09-25 15:45:35 +02:00
CaCO3
78900defad fix printf missing \n 2022-09-25 15:31:43 +02:00
CaCO3
d0b78e7c73 fix javascript messages 2022-09-25 15:31:24 +02:00
Frank Haverland
d3675f269d fix removal 2022-09-25 15:27:41 +02:00
Frank Haverland
64d25f416a debug 2022-09-25 15:16:09 +02:00
Frank Haverland
3f8b38a1ce fix delete firmware folder 2022-09-25 14:49:53 +02:00
Frank Haverland
5979acc31e fix firmware folder 2022-09-25 14:35:24 +02:00
Frank Haverland
7397927b77 use cache 2022-09-25 14:30:53 +02:00
Frank Haverland
0d67282c00 use update.zip from branch-action 2022-09-25 14:05:41 +02:00
jomjol
f9939023c6 Rolling 20220925 2022-09-25 08:49:47 +02:00
jomjol
922d76dace Merge pull request #1067 from caco3/update-pipeline-split-jobs2
Split the jobs to speed it up. Renamed artifacts, updated instructions on OTA page
2022-09-25 08:49:17 +02:00
CaCO3
02bf674539 . 2022-09-25 00:02:36 +02:00
CaCO3
4ad315b3f7 . 2022-09-24 23:57:31 +02:00
CaCO3
342fa0465c . 2022-09-24 23:54:04 +02:00
CaCO3
2866badc04 . 2022-09-24 23:51:31 +02:00
CaCO3
60194d8db8 Split the jobs to speed it up. Renamed artifacts, updated instructions on OTA page 2022-09-24 23:38:19 +02:00
jomjol
165745bb7d Merge pull request #1065 from caco3/fix-submodule-rebase-conflict
fixed rebase conflicts
2022-09-24 22:38:21 +02:00
CaCO3
fc4f3eebb6 fixed rebase conflicts 2022-09-24 22:32:01 +02:00
jomjol
8a6bd97ec2 Merge pull request #1060 from caco3/replace-components-by-submodules
Replace components by submodules
2022-09-24 22:22:57 +02:00
CaCO3
c4ee48ad1e Merge branch 'replace-components-by-submodules' of https://github.com/caco3/AI-on-the-edge-device into replace-components-by-submodules
# Conflicts:
#	code/components/esp32-camera-master
#	code/components/esp32-camera-master/.github/workflows/build.yml
#	code/components/esp32-camera-master/.github/workflows/upload_component.yml
#	code/components/esp32-camera-master/CMakeLists.txt
#	code/components/esp32-camera-master/Kconfig
#	code/components/esp32-camera-master/README.md
#	code/components/esp32-camera-master/conversions/esp_jpg_decode.c
#	code/components/esp32-camera-master/conversions/jpge.cpp
#	code/components/esp32-camera-master/conversions/to_bmp.c
#	code/components/esp32-camera-master/conversions/to_jpg.cpp
#	code/components/esp32-camera-master/driver/cam_hal.c
#	code/components/esp32-camera-master/driver/esp_camera.c
#	code/components/esp32-camera-master/driver/include/esp_camera.h
#	code/components/esp32-camera-master/driver/include/sensor.h
#	code/components/esp32-camera-master/driver/private_include/sccb.h
#	code/components/esp32-camera-master/driver/sccb.c
#	code/components/esp32-camera-master/driver/sensor.c
#	code/components/esp32-camera-master/examples/main/take_picture.c
#	code/components/esp32-camera-master/idf_component.yml
#	code/components/esp32-camera-master/sensors/bf20a6.c
#	code/components/esp32-camera-master/sensors/gc0308.c
#	code/components/esp32-camera-master/sensors/private_include/bf20a6.h
#	code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h
#	code/components/esp32-camera-master/sensors/private_include/ov5640_settings.h
#	code/components/esp32-camera-master/sensors/private_include/sc030iot.h
#	code/components/esp32-camera-master/sensors/private_include/sc101iot.h
#	code/components/esp32-camera-master/target/esp32/ll_cam.c
#	code/components/esp32-camera-master/target/esp32s2/ll_cam.c
#	code/components/esp32-camera-master/target/esp32s3/ll_cam.c
#	code/components/esp32-camera-master/target/private_include/ll_cam.h
#	code/components/esp32-camera-master/test/test_camera.c
#	code/components/tflite-lib/CMakeLists.txt
#	code/components/tflite-lib/tensorflow/lite/builtin_ops.h
#	code/components/tflite-lib/tensorflow/lite/c/common.cc
#	code/components/tflite-lib/tensorflow/lite/c/common.h
#	code/components/tflite-lib/tensorflow/lite/context_util.h
#	code/components/tflite-lib/tensorflow/lite/core/api/flatbuffer_conversions.cc
#	code/components/tflite-lib/tensorflow/lite/core/api/flatbuffer_conversions.h
#	code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.cc
#	code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/hard_swish.h
#	code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/mul.h
#	code/components/tflite-lib/tensorflow/lite/micro/all_ops_resolver.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/add.h
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/add_n.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/arg_min_max.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/batch_to_space_nd.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/comparisons.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/concatenation.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/cumsum.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/depth_to_space.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/depthwise_conv.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/div.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/elementwise.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/elu.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/esp_nn/conv.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/esp_nn/depthwise_conv.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/esp_nn/softmax.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/exp.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/expand_dims.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/fill.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/floor_div.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/floor_mod.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/fully_connected.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/gather.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/gather_nd.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_runner.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_runner.h
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_util.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_util.h
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/l2_pool_2d.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/l2norm.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/log_softmax.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/lstm_eval.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/maximum_minimum.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/micro_ops.h
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/micro_tensor_utils.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/mul.h
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/neg.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/pack.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/pad.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/pooling.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/pooling.h
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/prelu.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/reduce_common.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/resize_bilinear.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/resize_nearest_neighbor.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/shape.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/slice.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/softmax.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/space_to_batch_nd.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/space_to_depth.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/split_v.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/squeeze.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/strided_slice.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/svdf.h
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/tanh.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/transpose.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/transpose_conv.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/unidirectional_sequence_lstm_test_config.h
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/unpack.cc
#	code/components/tflite-lib/tensorflow/lite/micro/kernels/zeros_like.cc
#	code/components/tflite-lib/tensorflow/lite/micro/micro_allocation_info.cc
#	code/components/tflite-lib/tensorflow/lite/micro/micro_allocator.cc
#	code/components/tflite-lib/tensorflow/lite/micro/micro_allocator.h
#	code/components/tflite-lib/tensorflow/lite/micro/micro_interpreter.cc
#	code/components/tflite-lib/tensorflow/lite/micro/micro_interpreter.h
#	code/components/tflite-lib/tensorflow/lite/micro/micro_mutable_op_resolver.h
#	code/components/tflite-lib/tensorflow/lite/micro/micro_profiler.cc
#	code/components/tflite-lib/tensorflow/lite/micro/micro_profiler.h
#	code/components/tflite-lib/tensorflow/lite/micro/recording_micro_allocator.cc
#	code/components/tflite-lib/tensorflow/lite/schema/schema_generated.h
#	code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/base.h
#	code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/flatbuffer_builder.h
#	code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/flatbuffers.h
#	code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/flexbuffers.h
#	code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/stl_emulation.h
#	code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/table.h
#	code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/util.h
#	code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/vector.h
#	code/components/tflite-lib/third_party/flatbuffers/include/flatbuffers/verifier.h
2022-09-24 22:17:07 +02:00
CaCO3
3e5aa77ff5 rebase 2022-09-24 22:14:02 +02:00
CaCO3
b45b0f6a2a . 2022-09-24 22:06:05 +02:00
CaCO3
fc77c0fdf1 fix actions 2022-09-24 22:06:05 +02:00
CaCO3
bf1e96a303 added readme 2022-09-24 22:06:05 +02:00
CaCO3
86ba3e9bf4 autogenerated file got update => maybe we also can remove it? 2022-09-24 22:06:05 +02:00
CaCO3
d0fb1fc0f1 removed zip files 2022-09-24 22:06:05 +02:00
CaCO3
8bf4939ac1 added tflite-lib (resp. tflite-micro-esp-examples) again as submodule (master) 2022-09-24 22:06:05 +02:00
CaCO3
75a653a5c7 removed tflite-lib 2022-09-24 22:05:58 +02:00
CaCO3
e713ffa52d autogenerated file got update => maybe we also can remove it? 2022-09-24 22:05:06 +02:00
CaCO3
abc3efc16e added esp-nn again as submodule (master) 2022-09-24 22:05:06 +02:00
CaCO3
25e20f9299 removed esp-nn 2022-09-24 22:04:52 +02:00
CaCO3
f2effc4253 autogenerated file got update => maybe we also can remove it? 2022-09-24 22:04:52 +02:00
CaCO3
fb03dba60c remove esp32-camera-master files 2022-09-24 22:04:32 +02:00
jomjol
68e57d5ec4 Rolling 20220924 2022-09-24 21:24:50 +02:00
jomjol
a1691a77cf Merge pull request #1059 from caco3/remove-autogenerated-version-files3
remove autogenerated version files
2022-09-24 06:38:10 +02:00
jomjol
aa5651a464 Merge pull request #1055 from caco3/improve-prev-value-page
Improve prev value page
2022-09-24 06:33:29 +02:00
jomjol
0a79fb6196 Merge pull request #1052 from caco3/re-aranged-menus3
moved the index-configure.html menus to the index.html page
2022-09-24 06:32:38 +02:00
CaCO3
7e108d707a . 2022-09-23 23:11:33 +02:00
CaCO3
fb850fb040 fix actions 2022-09-23 23:08:31 +02:00
CaCO3
95292b8213 added readme 2022-09-23 23:04:50 +02:00
CaCO3
e59dca4bec autogenerated file got update => maybe we also can remove it? 2022-09-23 21:55:51 +02:00
CaCO3
86e47c722d removed zip files 2022-09-23 21:55:39 +02:00
CaCO3
5a1127d2b9 added tflite-lib (resp. tflite-micro-esp-examples) again as submodule (master) 2022-09-23 21:52:24 +02:00
CaCO3
b1d4df4309 removed tflite-lib 2022-09-23 21:29:17 +02:00
CaCO3
f6f70776d9 autogenerated file got update => maybe we also can remove it? 2022-09-23 21:20:32 +02:00
CaCO3
570777db7e added esp-nn again as submodule (master) 2022-09-23 21:20:18 +02:00
CaCO3
b06b42f0e9 removed esp-nn 2022-09-23 21:17:44 +02:00
CaCO3
8a170a7e16 autogenerated file got update => maybe we also can remove it? 2022-09-23 21:17:01 +02:00
CaCO3
5e95634173 added esp32-camera-master again as submodule (version 2.0.2) 2022-09-23 21:14:41 +02:00
CaCO3
84cc3490a1 remove esp32-camera-master files 2022-09-23 21:11:03 +02:00
CaCO3
ee724d372d remove autogenerated version files 2022-09-23 21:02:56 +02:00
George Ruinelli
711578d0a2 fix closing of menu on mobile devices 2022-09-23 00:02:50 +02:00
George Ruinelli
1956416fb6 set current value als preValue input default 2022-09-22 23:12:46 +02:00
George Ruinelli
1ec1f4f75f remove default entries 2022-09-22 23:06:33 +02:00
George Ruinelli
0fb192d79e fixed width 2022-09-22 22:54:01 +02:00
George Ruinelli
19847652a9 close menu on click 2022-09-22 22:49:57 +02:00
jomjol
d807334141 Merge branch 'pr/1044' into rolling 2022-09-22 18:42:17 +02:00
George Ruinelli
a08a233484 . 2022-09-22 00:07:38 +02:00
George Ruinelli
7fc9676385 . 2022-09-22 00:00:54 +02:00
Cristian
7d8cdc79f2 Reversed the changes in the directory of the esp32-cam: /components/esp32-camera-master upon request from jomjol 2022-09-21 23:42:02 +02:00
Cristian
6938299b72 Few typing corrections and translations 2022-09-21 23:36:14 +02:00
Cristian
6794091919 Merge branch 'master' of https://github.com/jomjol/AI-on-the-edge-device into texts-typing-corrections 2022-09-21 23:34:50 +02:00
George Ruinelli
6631ebc12a . 2022-09-21 23:02:12 +02:00
jomjol
efc800c223 Rolling 20220921 2022-09-21 21:49:09 +02:00
jomjol
6756b6d741 Merge pull request #1042 from haverland/rolling
auto release creation and build artifact upload and release notes
2022-09-21 20:03:05 +02:00
jomjol
3138e36137 Merge pull request #1048 from jomjol/added-firmware-readme
re-added empty firmware folder with a readme pointing to the releases page
2022-09-21 19:51:50 +02:00
George Ruinelli
b3b1c18ff5 . 2022-09-21 17:51:53 +02:00
George Ruinelli
1c9cd46d88 . 2022-09-21 17:51:14 +02:00
George Ruinelli
f4074fb939 . 2022-09-21 17:48:29 +02:00
CaCO3
7d286337ce Update README.md 2022-09-21 16:55:43 +02:00
CaCO3
81f92b8b0f re-added empty firmware folder with a readme pointing to the releases page 2022-09-21 16:53:39 +02:00
Cristian
01337ddcbf Few typing corrections and translations 2022-09-21 09:37:15 +02:00
Frank Haverland
3fb545f869 remove notebook 2022-09-21 00:08:28 +02:00
Frank Haverland
04e59482b5 Merge branch 'master' into rolling 2022-09-21 00:00:06 +02:00
github-actions
50acd26804 Update Changelog.md for release 2022-09-20 21:56:39 +00:00
Frank Haverland
1bc8e698b4 Merge branch 'master' of https://github.com/haverland/AI-on-the-edge-device 2022-09-20 23:49:08 +02:00
Frank Haverland
46b8b67fed create full update.zip 2022-09-20 23:48:51 +02:00
Frank Haverland
84a653b5d5 example created 2022-09-20 23:43:47 +02:00
github-actions
dee7212531 Update Changelog.md for release 2022-09-20 21:38:40 +00:00
Frank Haverland
50b0593648 only zips from dist, Changelog like keepachangelog 2022-09-20 23:31:09 +02:00
Frank Haverland
4c786406c3 enable firmware creation 2022-09-20 23:12:29 +02:00
Frank Haverland
6fed12e9ad fix 2022-09-20 23:06:24 +02:00
Frank Haverland
11216350c4 Revert "Update Changelog.md for release"
This reverts commit 577dbdeb7c.
2022-09-20 22:57:09 +02:00
github-actions
577dbdeb7c Update Changelog.md for release 2022-09-20 20:54:52 +00:00
Frank Haverland
b75f77b31c Merge branch 'master' of https://github.com/haverland/AI-on-the-edge-device 2022-09-20 22:52:37 +02:00
Frank Haverland
2776e1b127 add release createion and changelog to releasenotes 2022-09-20 22:52:34 +02:00
jomjol
562cc4352b Rolling 20220919 2022-09-19 22:07:16 +02:00
jomjol
013d5a99a8 Merge pull request #1036 from caco3/update-readme
Update Landing Page on Github
2022-09-19 22:06:10 +02:00
jomjol
fbe7bb90c2 Merge pull request #1038 from jomjol/update-FeautreRequest.md
Update FeatureRequest.md
2022-09-19 22:02:51 +02:00
CaCO3
660326af42 Update FeatureRequest.md 2022-09-19 21:08:48 +02:00
CaCO3
80aa2dfc73 fix links 2022-09-19 20:37:31 +02:00
CaCO3
316d8f252a fix links 2022-09-19 20:36:19 +02:00
CaCO3
28a39d8dfc fix tools readme 2022-09-19 20:34:32 +02:00
CaCO3
3fc6c194e5 updated main landing page 2022-09-19 20:33:04 +02:00
jomjol
0e85b40eba Delete firmware directory 2022-09-19 20:30:49 +02:00
jomjol
d479c8d44e Rolling 20220919 2022-09-19 20:29:05 +02:00
jomjol
6b6e677f8b Delete firmware directory 2022-09-19 20:28:39 +02:00
jomjol
45c9914efa Merge pull request #1033 from caco3/auto-add-version
Auto add version
2022-09-19 20:03:56 +02:00
jomjol
a4299cab89 Merge pull request #1031 from haverland/rolling
Change link to Choosing the model in configuration page
2022-09-19 20:01:46 +02:00
Frank Haverland
67e4ee4aca Revert "Commit from GitHub Actions (Build and Pack)"
This reverts commit e1fc44d44a.
2022-09-19 19:50:42 +02:00
Frank Haverland
85750c9453 Revert "Commit from GitHub Actions (Build and Pack)"
This reverts commit 26949d8083.
2022-09-19 19:50:35 +02:00
Frank Haverland
d8b4b3f2cf Revert "commit build artifacts"
This reverts commit 5aa4714d05.
2022-09-19 19:49:24 +02:00
Frank Haverland
fedaef07eb Revert "fix indent"
This reverts commit b65d9e5a46.
2022-09-19 19:47:46 +02:00
Frank Haverland
9ae9a82cca Revert "fix path"
This reverts commit bf8589f9ec.
2022-09-19 19:47:43 +02:00
Frank Haverland
37fec1821f Revert "fix path"
This reverts commit 32e5c484ab.
2022-09-19 19:47:40 +02:00
Frank Haverland
c4e69bc036 Revert "discard version files"
This reverts commit 1c80c2108d.
2022-09-19 19:47:35 +02:00
Frank Haverland
7dd14282c6 Revert "fix double entry in html"
This reverts commit b6a571a90c.
2022-09-19 19:47:13 +02:00
Frank Haverland
7e72cc3d1b Revert change checkDigitConsistency (Issue #1028 and #1029) 2022-09-19 19:47:06 +02:00
Frank Haverland
8e2da52d82 Revert "Commit from GitHub Actions (Build and Pack)"
This reverts commit e38c861cb5.
2022-09-19 19:45:13 +02:00
github-actions
e38c861cb5 Commit from GitHub Actions (Build and Pack) 2022-09-19 17:43:35 +00:00
Frank Haverland
b6a571a90c fix double entry in html 2022-09-19 19:36:41 +02:00
github-actions
26949d8083 Commit from GitHub Actions (Build and Pack) 2022-09-19 17:21:23 +00:00
Frank Haverland
1c80c2108d discard version files 2022-09-19 19:13:51 +02:00
github-actions
e1fc44d44a Commit from GitHub Actions (Build and Pack) 2022-09-19 16:34:49 +00:00
Frank Haverland
32e5c484ab fix path 2022-09-19 18:28:03 +02:00
Frank Haverland
bf8589f9ec fix path 2022-09-19 18:27:31 +02:00
Frank Haverland
b65d9e5a46 fix indent 2022-09-19 18:05:41 +02:00
Frank Haverland
5aa4714d05 commit build artifacts 2022-09-19 18:03:55 +02:00
CaCO3
dd8500052e also set HTML version by cmake 2022-09-19 08:22:44 +02:00
CaCO3
2d68f44a40 show version in index pages 2022-09-18 23:25:57 +02:00
CaCO3
39ec90895e removed GIT_BASE_BRANCH and instead use the autogenerated values in info.html 2022-09-18 22:37:16 +02:00
CaCO3
3d1d41e36b remove PROJECT_VER. The compiler then automatically uses git to fetch the information, eg. "v11.3.0-12-g46dfc75-dirty" 2022-09-18 22:23:23 +02:00
CaCO3
46dfc75724 show version on startup in loag and on console 2022-09-18 22:16:49 +02:00
CaCO3
906915e058 add include guard 2022-09-18 22:14:21 +02:00
Frank Haverland
85418c678d Change link to Choosing the model in configuration page 2022-09-18 21:57:14 +02:00
jomjol
81e06c45d3 Rolling 20220918 2022-09-18 20:48:12 +02:00
jomjol
645fbf0231 Merge branch 'rolling' of https://github.com/jomjol/AI-on-the-edge-device into rolling 2022-09-18 18:20:46 +02:00
jomjol
5d10dba3c7 Merge pull request #1030 from haverland/rolling
Revert change in 11.3.1 on checkDigitConsistency  and add a fast forward transition range
2022-09-18 18:16:05 +02:00
Frank Haverland
78806c081b Create special handling for fast foreward, because it's not at the beginning of the Transition 2022-09-18 17:56:56 +02:00
Frank Haverland
3f659ccf7d Revert change in 11.3.1 on checkDigitConsistency (Issue #1028 and #1029) 2022-09-18 17:01:32 +02:00
jomjol
6cd52a3cdc v11.3.1 2022-09-17 21:19:24 +02:00
jomjol
a1f675419a v11.3.1 2022-09-17 21:04:55 +02:00
jomjol
9087c1b4e1 Create partitions.bin 2022-09-17 21:00:55 +02:00
jomjol
9418d55a2f Merge branch 'rolling' 2022-09-17 20:56:41 +02:00
jomjol
87076df23f v11.3.1 2022-09-17 20:56:14 +02:00
Frank Haverland
aba9603021 Merge branch 'master' of https://github.com/haverland/AI-on-the-edge-device 2022-09-17 16:26:58 +02:00
Frank Haverland
086aa3134b Merge branch 'master' of https://github.com/haverland/AI-on-the-edge-device 2022-09-17 16:24:26 +02:00
CaCO3
010691f3e5 Merge pull request #1027 from caco3/remove-11.3.0-binaries
Remove 11.3.0 binaries as there are some issues with them
2022-09-17 16:19:59 +02:00
CaCO3
12d9099543 Delete main.yml 2022-09-17 16:19:37 +02:00
Frank Haverland
c9b7f8d901 Merge branch 'jomjol:rolling' into rolling 2022-09-17 16:19:35 +02:00
CaCO3
0372f82f2e removed binaries as tehre are some issues with them 2022-09-17 16:13:24 +02:00
CaCO3
18be9afbe1 Merge branch 'jomjol:master' into master 2022-09-17 16:10:35 +02:00
jomjol
36f5618376 Rolling 20220917 2022-09-17 10:16:31 +02:00
jomjol
03542368d7 v11.3.0 2022-09-17 10:11:31 +02:00
jomjol
36a02d1658 Merge branch 'rolling' 2022-09-17 09:25:00 +02:00
jomjol
c65182b568 Prepare v11.3.0 2022-09-17 09:24:41 +02:00
Frank Haverland
e5bd824506 Merge branch 'rolling' of https://github.com/haverland/AI-on-the-edge-device into rolling 2022-09-16 18:46:45 +02:00
jomjol
10fbb610c3 Rolling 20220916 2022-09-16 17:56:51 +02:00
Frank Haverland
b8ab58a196 Testcases for #950 and #1020 2022-09-16 17:38:34 +02:00
jomjol
fa59b5ba6c Merge pull request #1019 from PLCHome/master
Communication error behind an HTTPS proxy
2022-09-16 16:46:29 +02:00
ChrisHanuta
67d47abde2 Communication error behind an HTTPS proxy
The device always requests via HTTP, not HTTPS
2022-09-16 10:18:40 +02:00
jomjol
042ff18e65 Merge pull request #1006 from haverland/rolling
fix handling in DigitalUebergangsbereichVorgaenger
2022-09-15 21:13:37 +02:00
Frank Haverland
3008952240 Merge branch 'jomjol:rolling' into rolling 2022-09-15 19:14:59 +02:00
jomjol
aef28c7128 Merge pull request #1014 from caco3/update-pipeline-artifacts-packaging
Update pipeline artifacts packaging
2022-09-14 20:34:25 +02:00
CaCO3
9c5dfe65c9 enable python prep again 2022-09-13 22:04:05 +02:00
CaCO3
b12cdd673b build several artifacts
See concept in https://github.com/jomjol/AI-on-the-edge-device/discussions/999#discussioncomment-3617232
2022-09-13 22:02:51 +02:00
Frank Haverland
8247580372 Merge branch 'jomjol:rolling' into rolling 2022-09-11 22:45:40 +02:00
Frank Haverland
d7c7537aa9 #921 #994 - fix handling in DigitalUebergangsbereichVorgaenger, reduced DigitalUebergangsbereichVorgaenger =0.7
added serial-debug in postprocessing (not enabled by default)
added test-case for #994
fixed checkDigitConsistency (pre value before analog lost)
added debug to proof need of double for NUMBERS.Value (see testcase 32289.4198)
2022-09-11 22:44:35 +02:00
CaCO3
ad05a1d4fa Create main.yml 2022-09-10 23:49:42 +02:00
jomjol
7bd819ad96 Merge branch 'rolling' of https://github.com/jomjol/AI-on-the-edge-device into rolling 2022-09-10 23:29:25 +02:00
jomjol
64b472d6bc Rolling 20220910 2022-09-10 23:29:19 +02:00
CaCO3
3d7a906cce set version of html in pipeline
Update build.yaml
2022-09-10 22:55:56 +02:00
CaCO3
8c4a5a957a Update build.yaml
set version of html in pipeline
2022-09-10 22:53:36 +02:00
CaCO3
21874473cc Merge pull request #1000 from caco3/fix-pipeline
Fix pipeline
2022-09-10 21:46:00 +02:00
CaCO3
8aa99b34d6 Update build.yaml 2022-09-10 21:44:48 +02:00
jomjol
6759164c82 Rolling 20220910 2022-09-10 21:38:32 +02:00
jomjol
2445377d0a Rolling 20220910 2022-09-10 21:38:02 +02:00
CaCO3
99b6393fbc Update build.yaml 2022-09-10 21:35:17 +02:00
CaCO3
7e049f3270 Update build.yaml 2022-09-10 21:25:55 +02:00
jomjol
79d1e6b932 Rollling 20220910 2022-09-10 20:19:31 +02:00
jomjol
3403e3ea5c Merge pull request #995 from haverland/rolling
testcase for #921 (2022-09-10)
2022-09-10 20:01:26 +02:00
jomjol
c3a3aa9c0d Merge pull request #996 from caco3/auto-build_and_pack
Auto build and pack
2022-09-10 20:00:56 +02:00
jomjol
89c9ff144c Merge pull request #989 from caco3/update-webUI
Update web UI
2022-09-10 19:57:03 +02:00
CaCO3
82e6334832 Update build.yaml 2022-09-10 17:28:23 +02:00
Frank Haverland
dd70aa8969 testcase for #921 (2022-09-10) 2022-09-10 17:06:23 +02:00
CaCO3
4cf190239e rename firmware file 2022-09-10 15:59:57 +02:00
CaCO3
cb4ba51eee initial action 2022-09-10 15:38:46 +02:00
CaCO3
4d03867550 updated html version 2022-09-07 22:29:07 +02:00
CaCO3
02294e3afc added link to releases page 2022-09-07 22:18:32 +02:00
CaCO3
f68aa1d931 moved jomjols name from the pages title to the info page, added links there to the Github page 2022-09-07 22:18:21 +02:00
CaCO3
fb390359e1 modified ota page to make separation between firmware/html and CNN better. renamed html/ to Web Interface, as the suer does not care how it is build 2022-09-07 21:59:59 +02:00
jomjol
764c64b615 Merge pull request #986 from haverland/rolling
Testcase für #950
2022-09-07 18:55:11 +02:00
Frank Haverland
a167770848 Merge branch 'jomjol:rolling' into rolling 2022-09-06 21:07:18 +02:00
Frank Haverland
551743abec add Testcase to #950 2022-09-06 21:02:58 +02:00
jomjol
68a596707b Rolling 20220409 2022-09-04 18:16:37 +02:00
jomjol
47da2d657e Merge pull request #980 from caco3/re-init-mqtt-on-publish-error
Try to publish. If it fails, run MQTT init and publish again.
2022-09-04 18:01:04 +02:00
jomjol
43372c94a8 Merge pull request #979 from nliaudat/master
Update FeatureRequest.md
2022-09-04 17:59:29 +02:00
jomjol
0643274c68 Merge pull request #976 from caco3/re-integrate-css
Older Firmware versions (11.2.0 and older) do not support css files, therefore we need to keep it integrated for now!
2022-09-04 17:59:04 +02:00
CaCO3
395b471700 added return codes. Try to publish. If it fails, run MQTT init and publish again. Increased Keep Alive timeout to make sure it is greater than the flow interval 2022-09-04 17:55:22 +02:00
Nicolas Liaudat
a040d56fdc Update FeatureRequest.md
deep sleep for a time period only
2022-09-04 16:56:24 +02:00
jomjol
3c8a65c540 Merge pull request #978 from caco3/show-backup-also-on-index_configure.html
Show the backup menu entry also on the index_configure.html page
2022-09-04 15:34:25 +02:00
jomjol
2f38643473 Merge pull request #977 from caco3/reload-index-page-after-reboot
reload index page instead only iframe. Also added a duration estimate.
2022-09-04 15:34:02 +02:00
George Ruinelli
906a2e05eb Show the backup menu entry also on the index_configure.html page 2022-09-03 21:55:01 +02:00
CaCO3
3390e24534 reload parent page instead only iframe. Also added a duration estimate. 2022-09-03 21:41:46 +02:00
George Ruinelli
dc28b892bc Older Firmware versions (11.2.0 and older) do not support css files, therefore we need to keep the css integrated for now! 2022-09-03 21:16:24 +02:00
jomjol
3023cd139d Merge pull request #972 from haverland/rolling
add test case for #921
2022-09-03 18:04:36 +02:00
Frank Haverland
127135e1cb new versions of dig-class100 and ana-class100
- new images and retraining
2022-09-03 16:54:08 +02:00
Frank Haverland
aa4e115f80 add test case for #921
https://github.com/jomjol/AI-on-the-edge-device/issues/921#issuecomment-1236119370
2022-09-03 16:23:01 +02:00
jomjol
90f204b833 Rolling 20220903 2022-09-03 10:52:48 +02:00
jomjol
77427a8ec4 Merge pull request #971 from caco3/add-mqtt-log-messages
Improve MQTT handling
2022-09-03 08:12:53 +02:00
CaCO3
eafcdb01c2 moved log entry 2022-09-03 00:26:16 +02:00
CaCO3
3f58086aa1 Re-init MQTT if it failed last time, added log messages 2022-09-03 00:09:50 +02:00
jomjol
d36a4dbdbe Merge pull request #967 from haverland/921-keinNulldurchgang
No transition if previous values calculate the eval_vorgaenger is 9 (no transition)
2022-09-02 20:26:21 +02:00
jomjol
6879aa96e2 Merge pull request #966 from haverland/rolling
Fix for html upload #959 and new test case for #921
2022-09-02 20:25:28 +02:00
jomjol
fc219cc7f3 Merge pull request #965 from f-fritz/configpage-information-text
Update config-page info-text about MQTT topic structure (explain VALUE_NAME key)
2022-09-02 20:24:34 +02:00
Frank Haverland
787f9362eb #921 No zero trasition 2022-09-01 21:42:12 +02:00
Frank Haverland
231ac5305f fix html-upload #959 2022-09-01 21:11:45 +02:00
f-fritz
4bd79e370f Update config-page info-text about MQTT topic structure (explain VALUE_NAME) 2022-09-01 11:22:19 +02:00
jomjol
5c0cd63da0 Rolling 20220831 2022-08-31 22:04:02 +02:00
jomjol
5a31543538 Merge branch 'pr/952' into rolling 2022-08-31 19:58:04 +02:00
jomjol
1b88bf8690 Rolling 20220831 2022-08-31 19:53:53 +02:00
jomjol
6db01e67c9 Merge branch 'rolling' of https://github.com/jomjol/AI-on-the-edge-device into rolling 2022-08-31 19:37:24 +02:00
jomjol
f7cb6576b3 Merge pull request #960 from caco3/improve-iframe-sizing
Various WebUI Improvements
2022-08-31 19:32:52 +02:00
Frank Haverland
4a71749d1c add test case for #921 (79.99996) 2022-08-31 18:23:11 +02:00
CaCO3
d13f457344 Merge branch 'rolling' into improve-iframe-sizing 2022-08-30 22:50:24 +02:00
George Ruinelli
ab41401643 add min iframe height (to fix view on android chrome browser) 2022-08-30 22:41:33 +02:00
George Ruinelli
592b93ce2b added reboot buttons to config pages 2022-08-30 22:07:25 +02:00
George Ruinelli
5dc7b90a1c reformating 2022-08-30 22:06:51 +02:00
George Ruinelli
b1e0203527 added system menu to configure_index.html, reformating 2022-08-30 22:06:37 +02:00
George Ruinelli
774dde5767 changed color of selected submenu entries to red 8like selected main menu entries) 2022-08-30 22:06:05 +02:00
George Ruinelli
c322cd8b48 changed iframe style to make sure it uses whole height (minus header) 2022-08-30 21:23:36 +02:00
George Ruinelli
3dcdd52eb7 added separate stylesheet and moved common js to a file. 2022-08-30 21:23:05 +02:00
George Ruinelli
fe17d97a05 added example log entry 2022-08-30 21:09:07 +02:00
George Ruinelli
b71a34b476 add support for css files 2022-08-30 21:08:30 +02:00
George Ruinelli
ea71c86f69 removed unused styles 2022-08-30 20:46:27 +02:00
jomjol
550fe605bd Rolling 20220830 2022-08-30 20:27:45 +02:00
jomjol
109dd97016 Merge pull request #957 from caco3/add-config-backup-functionality
Add config backup functionality
2022-08-30 20:14:04 +02:00
jomjol
715f0a10f3 Merge pull request #958 from caco3/improve-ota-page
Improve ota page
2022-08-30 20:11:12 +02:00
George Ruinelli
d01caea53e Reduce height of the OTA page to make sure point 4 is not hidden by default. 2022-08-30 18:07:48 +02:00
George Ruinelli
a9cab31a1f . 2022-08-30 17:57:39 +02:00
George Ruinelli
d0968e87d7 . 2022-08-30 17:54:21 +02:00
George Ruinelli
6417f780e6 added backup page 2022-08-30 00:29:50 +02:00
jomjol
fd4d76de93 Merge pull request #955 from haverland/rolling
fix https://github.com/jomjol/AI-on-the-edge-device/issues/921#issuec…
2022-08-29 18:15:11 +02:00
Frank Haverland
cdf8663bd3 fix https://github.com/jomjol/AI-on-the-edge-device/issues/921#issuecomment-1229552041 2022-08-29 16:11:05 +02:00
George Ruinelli
bf9050757b reduce height of the OTA page to make sure point 4 is not hidden by default. 2022-08-28 23:59:52 +02:00
jomjol
bce99da6e5 v11.2.0 2022-08-28 20:04:36 +02:00
jomjol
234925c850 Merge branch 'rolling' 2022-08-28 19:54:15 +02:00
jomjol
c9b7a5f84c v11.2.0 2022-08-28 19:52:51 +02:00
Anthony
cfaeaa370a cleanup. 2022-08-28 02:11:08 -04:00
Anthony
8cd5a89d6a added CPU Temp and RSSI to staus box of webgui. 2022-08-28 01:17:57 -04:00
Anthony
090f8d5cc9 Added CPU temperature to MQTT. 2022-08-27 23:59:06 -04:00
Anthony
29ae8cfa7f updated config for CCW param, and roi to top aligm image when multiple number readings. 2022-08-27 18:50:11 -04:00
jomjol
338184712d Delete dig-cont_0570_s3_q.tflite 2022-08-27 20:41:14 +02:00
jomjol
b2c510d73e Rolling 20220827 2022-08-27 20:21:53 +02:00
Anthony
e8b649d315 latest update for CCW 2022-08-27 13:19:31 -04:00
jomjol
6a5d5511f1 Merge pull request #949 from caco3/beautify-restart
Beautify restart
2022-08-27 18:03:03 +02:00
jomjol
ec99ac3a3b Merge pull request #951 from haverland/rolling
Testcases for #942
2022-08-27 18:01:43 +02:00
Anthony
58cfd8bc42 fix for CCW parsing of config file into OTA zip. 2022-08-27 10:50:17 -04:00
Frank Haverland
f676f12e02 Testcases for https://github.com/jomjol/AI-on-the-edge-device/issues/942 2022-08-27 16:48:42 +02:00
Anthony
393fe43090 fixed parsing roi for CCW flag. 2022-08-26 23:12:23 -04:00
George Ruinelli
296a50a6d2 . 2022-08-26 23:51:24 +02:00
George Ruinelli
b1ee3d8793 Show progress on reboot and reload page automatically 2022-08-26 23:45:25 +02:00
Anthony
d930fdae78 fixes for CCW analog. 2022-08-26 17:05:00 -04:00
jomjol
993fbfe5a8 Rolling 2022-08-26 2022-08-26 21:20:26 +02:00
Anthony
af546ef0f1 Added Counter Clockwise option for analog dial. 2022-08-26 10:43:22 -04:00
jomjol
2b60e81a52 Rolling 2022-08-24 2022-08-24 17:52:21 +02:00
jomjol
11418459b8 Merge pull request #939 from caco3/rolling
Extend Config Page
2022-08-24 17:46:34 +02:00
George Ruinelli
3b8b8e47da Added link to wiki. Added filter for CNNs: on digital selection show all files except those which contain '/ana' in theri name and vice versa for the analog selection. 2022-08-22 23:48:48 +02:00
jomjol
ae302d49ef Rolling 2022-08-22 2022-08-22 22:17:32 +02:00
jomjol
0153229d3c Merge pull request #936 from haverland/rolling
add test case to reproduce
2022-08-22 21:07:02 +02:00
jomjol
eeb74dd6fd Merge pull request #931 from caco3/master
added favicon and adjusted window title to show hostname first
2022-08-22 21:01:46 +02:00
Frank Haverland
ecc62a3ba9 add test case to reproduce 2022-08-22 21:01:44 +02:00
jomjol
aca60465f0 v11.1.1 2022-08-22 18:20:00 +02:00
jomjol
57bdca37fc v11.1.1 2022-08-22 18:12:08 +02:00
jomjol
6409397770 Merge pull request #933 from haverland/rolling
Fix for #921, #919
2022-08-22 17:58:30 +02:00
Frank Haverland
974044adf0 last analog result_float as Readout parameter, testcases for #921, #919 2022-08-22 16:10:07 +02:00
Frank Haverland
59aeeda786 Merge branch 'rolling' into analogtodig_as_float 2022-08-22 10:33:10 +02:00
Frank Haverland
b6bf8d992f Test case for postprocessing 2022-08-22 10:31:25 +02:00
Frank Haverland
82cb966863 Merge branch 'jomjol:master' into master 2022-08-22 10:21:35 +02:00
jomjol
9d31edc67a Rolling 2022-08-21 2022-08-21 21:33:56 +02:00
George Ruinelli
1b4f4bdd6d added favicon and adjusted window title to show hostname first 2022-08-21 21:05:59 +02:00
jomjol
c9a879d329 v11.1.0 2022-08-21 17:56:46 +02:00
jomjol
ea69b1be00 v11.1.0 2022-08-21 17:44:34 +02:00
jomjol
2a8b3a87ea Merge pull request #922 from haverland/rolling
Rolling
2022-08-21 16:47:07 +02:00
jomjol
52783734ce Merge pull request #925 from jochenchrist/master
Remove .DS_Store
2022-08-21 16:44:12 +02:00
jomjol
0e1b390ec6 Merge pull request #918 from ppisljar/patch-1
Update FeatureRequest.md
2022-08-21 16:41:30 +02:00
jochen
ab49bdf82f .DS_Store removed 2022-08-20 19:25:52 +02:00
jochenchrist
25e7051271 Delete .DS_Store 2022-08-20 19:23:08 +02:00
Frank Haverland
85c0a72ae8 Merge branch 'jomjol:master' into master 2022-08-19 21:40:00 +02:00
Frank Haverland
7315f9adfc Merge branch 'jomjol:rolling' into rolling 2022-08-19 21:33:08 +02:00
Frank Haverland
af1aee4ac3 add testcase for #921 2022-08-19 21:30:11 +02:00
Frank Haverland
d6ff7eef88 fix problems with early transition of digits if analog pointers. #921 2022-08-19 21:05:23 +02:00
Frank Haverland
7706b4dbc3 fix for #919 the prev is int, so <9.0 instead of <9.5 2022-08-18 19:28:13 +02:00
Peter Pisljar
3561ecd2b7 Update FeatureRequest.md 2022-08-18 15:16:57 +02:00
Frank Haverland
99afb88957 Merge branch 'jomjol:master' into master 2022-07-24 20:20:28 +02:00
Frank Haverland
70e94205d1 Merge branch 'jomjol:master' into master 2022-07-17 19:58:43 +02:00
Frank Haverland
8e12d71580 Merge branch 'jomjol:master' into master 2022-06-18 13:40:43 +02:00
Frank Haverland
a681a76c4b Kaggle Notebook | pmd-effnet-zca | Version 1 2022-05-24 21:41:45 +02:00
610 changed files with 4841 additions and 113185 deletions

BIN
.DS_Store vendored

Binary file not shown.

31
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,31 @@
---
name: Bug report
about: Create a bug report to help us improve
title: ''
labels: ''
assignees: ''
---
Thank you for taking your time to report a bug.
Before you proceed, please check:
- Do you use the latest version? See https://github.com/jomjol/AI-on-the-edge-device/releases.
- Is there already another similar issue? Check for open and closed Issue in https://github.com/jomjol/AI-on-the-edge-device/issues.
- Are instructions in the readme solving your issue (https://github.com/jomjol/AI-on-the-edge-device/tree/master#readme)?
- Are instructions in the wiki solving your issue? (https://github.com/jomjol/AI-on-the-edge-device/wiki)?
**Describe the bug**
A clear and concise description of what the bug is.
**Version**
Which version are you using? (See menu `System > Info`).
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,10 @@
---
name: General Question
about: General Question => Open a discussion instead
title: ''
labels: ''
assignees: ''
---
If its not a bug, please open a discussion instead, see https://github.com/jomjol/AI-on-the-edge-device/discussions

319
.github/workflows/build.yaml vendored Normal file
View File

@@ -0,0 +1,319 @@
name: Build and Pack
on: [push]
jobs:
#########################################################################################
## Build Firmware
#########################################################################################
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Set Variables
id: vars
run: |
echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Cache PlatformIO
uses: actions/cache@v2
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python
uses: actions/setup-python@v2
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
- name: Build Firmware
# run: echo "Testing... ${{ github.ref_name }}, ${{ steps.vars.outputs.sha_short }}" > ./sd-card/html/version.txt; mkdir -p ./code/.pio/build/esp32cam/; cd ./code/.pio/build/esp32cam/; echo "${{ steps.vars.outputs.sha_short }}" > firmware.bin; cp firmware.bin partitions.bin; cp firmware.bin bootloader.bin # Testing
run: cd code; platformio run --environment esp32cam
- name: Store generated files in cache
uses: actions/cache@v3
with:
path: |
./code/.pio/build/esp32cam/firmware.bin
./code/.pio/build/esp32cam/partitions.bin
./code/.pio/build/esp32cam/bootloader.bin
./sd-card/html/version.txt
key: ${{ github.run_number }}
#########################################################################################
## Pack for old OTA (v1)
#########################################################################################
pack-for-OTA-v1:
# Old OTA concept
# firmware__*.zip needs to be unpacked before attaching to the release!
# The bin filename can contain versioning.
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v2
- name: Get generated files from cache
uses: actions/cache@v3
with:
path: |
./code/.pio/build/esp32cam/firmware.bin
./code/.pio/build/esp32cam/partitions.bin
./code/.pio/build/esp32cam/bootloader.bin
./sd-card/html/version.txt
key: ${{ github.run_number }}
- name: Set Variables
id: vars
run: |
echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Rename firmware file to contain versioning (old ota)
run: |
mkdir -p ./dist_old_ota
cp "./code/.pio/build/esp32cam/firmware.bin" "./dist_old_ota/firmware__${{ github.ref_name }}_(${{ steps.vars.outputs.sha_short }}).bin"
- name: Upload Firmware artifact (old OTA concept)
uses: actions/upload-artifact@v3
with:
# name: "firmware__${{ github.ref_name }}__(${{ steps.vars.outputs.sha_short }})__(extract_before_upload__only_needed_for_migration_from_11.3.1)"
name: "firmware__(extract_before_upload)__${{ github.ref_name }}__(${{ steps.vars.outputs.sha_short }})"
path: ./dist_old_ota/*
- name: Upload Web interface artifact (old OTA concept)
uses: actions/upload-artifact@v3
with:
name: "html__${{ github.ref_name }}__(${{ steps.vars.outputs.sha_short }})"
path: ./sd-card/html/*
#########################################################################################
## Pack for new OTA (v2)
#########################################################################################
pack-for-OTA-v2:
# New OTA concept
# update__version.zip file with following content:
# - /firmware.bin
# - (optional) /html/*
# - (optional) /config/*.tfl
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v2
- name: Get generated files from cache
uses: actions/cache@v3
with:
path: |
./code/.pio/build/esp32cam/firmware.bin
./code/.pio/build/esp32cam/partitions.bin
./code/.pio/build/esp32cam/bootloader.bin
./sd-card/html/version.txt
key: ${{ github.run_number }}
- name: Set Variables
id: vars
run: |
echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Prepare update.zip artifact
run: |
mkdir -p ./dist
cp "./code/.pio/build/esp32cam/firmware.bin" "dist/firmware.bin"
# - name: Upload update.zip Artifact (Firmware only)
# uses: actions/upload-artifact@v3
# with:
# name: "update_firmware_only__${{ github.ref_name }}_(${{ steps.vars.outputs.sha_short }})"
# path: ./dist/*
- name: Add Web UI to dist
run: cp -r ./sd-card/html ./dist/
# - name: Upload update.zip artifact (Firmware + Web UI)
# uses: actions/upload-artifact@v3
# with:
# name: "update_firmware+webinterface__${{ github.ref_name }}_(${{ steps.vars.outputs.sha_short }})"
# path: ./dist/*
- name: Add CNN to dist
run: |
mkdir ./dist/config/
cp ./sd-card/config/*.tfl ./dist/config/ 2>/dev/null || true
cp ./sd-card/config/*.tflite ./dist/config/ 2>/dev/null || true
- name: Upload dist as update.zip artifact (Firmware + Web UI + CNN)
uses: actions/upload-artifact@v3
with:
name: "update__${{ github.ref_name }}_(${{ steps.vars.outputs.sha_short }})"
path: ./dist/*
- name: Store generated files in cache
uses: actions/cache@v3
with:
path: |
./dist
key: ${{ github.run_number }}-pack-for-OTA-v2
#########################################################################################
## Pack for a fresh install (USB flashing) (initial_esp32_setup)
#########################################################################################
pack-for-fresh-install:
# creates old style binaries for fresh installation (backward compatible to wiki)
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v2
- name: Get generated files from cache
uses: actions/cache@v3
with:
path: |
./code/.pio/build/esp32cam/firmware.bin
./code/.pio/build/esp32cam/partitions.bin
./code/.pio/build/esp32cam/bootloader.bin
./sd-card/html/version.txt
key: ${{ github.run_number }}
- name: Prepare artifacts for release
run: |
mkdir -p firmware
rm -rf firmware/*.zip
mkdir -p release
# copy builds to firmware folder
cp -f "./code/.pio/build/esp32cam/firmware.bin" "firmware/firmware.bin"
cp -f "./code/.pio/build/esp32cam/bootloader.bin" "firmware/bootloader.bin"
cp -f "./code/.pio/build/esp32cam/partitions.bin" "firmware/partitions.bin"
zip -r ./firmware/sd-card.zip sd-card
cd ./firmware
- name: Upload initial_esp32_setup.zip artifact (Firmware + Bootloader + Partitions + Web UI)
uses: actions/upload-artifact@v3
with:
name: "initial_esp32_setup__${{ github.ref_name }}_(${{ steps.vars.outputs.sha_short }})"
path: ./firmware
- name: Store generated files in cache
uses: actions/cache@v3
with:
path: |
./firmware
key: ${{ github.run_number }}-pack-for-fresh-install
#########################################################################################
## Prepare and create release
#########################################################################################
release:
runs-on: ubuntu-latest
needs: [pack-for-OTA-v2, pack-for-fresh-install]
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v2
- name: Get generated files from cache
uses: actions/cache@v3
with:
path: |
./code/.pio/build/esp32cam/firmware.bin
./code/.pio/build/esp32cam/partitions.bin
./code/.pio/build/esp32cam/bootloader.bin
./sd-card/html/version.txt
key: ${{ github.run_number }}
# import the changes from
- name: Get generated files from cache
uses: actions/cache@v3
with:
path: |
./dist
key: ${{ github.run_number }}-pack-for-OTA-v2
# import cached artifacts from pack-for-fresh-install
- name: Get generated files from cache
uses: actions/cache@v3
with:
path: |
./firmware
key: ${{ github.run_number }}-pack-for-fresh-install
- name: Set Variables
id: vars
run: |
echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Prepare artifacts for release
run: |
mkdir -p release
# create a update.zip like "update__rolling"
cd ./dist
zip -r ../release/update.zip .
cd ../firmware
zip -r ../release/initial_esp32_setup.zip .
cd ../sd-card/html
zip -r ../../firmware/html-from-11.3.1.zip .
# extract the version used in next step
- id: get_version
if: startsWith(github.ref, 'refs/tags/')
uses: battila7/get-version-action@v2
# the changelog [unreleased] will now be changed to the release version
- name: Update changelog
uses: thomaseizinger/keep-a-changelog-new-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
changelogPath: Changelog.md
version: ${{ steps.get_version.outputs.version-without-v }}
# the release notes will be extracted from changelog
- name: Extract release notes
id: extract-release-notes
if: startsWith(github.ref, 'refs/tags/')
uses: ffurrer2/extract-release-notes@v1
with:
changelog_file: Changelog.md
# Releases should only be created on master by tagging the last commit.
# all artifacts in firmware folder pushed to the release
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
name: ${{ steps.get_version.outputs.version-without-v }}
body: ${{ steps.extract-release-notes.outputs.release_notes }}
files: |
release/*
firmware/firmware.bin
firmware/html-from-11.3.1.zip
# Commit&Push Changelog to master branch. Must be manually merged back to rolling
- name: Commit changes and push changes
if: startsWith(github.ref, 'refs/tags/')
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git add Changelog.md
git commit Changelog.md -m "Update Changelog.md for ${{github.event.inputs.versionIncrement}} release"
git push origin HEAD:master

8
.gitignore vendored
View File

@@ -4,8 +4,13 @@
.code-workspace
/sd-card/htm./.vscode/
/code/build
/code/.helper
/sd-card/html/debug/
/firmware/
version.txt
/dist/
/dist_release/
/dist_old_ota
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
@@ -17,3 +22,4 @@ compile_commands.json
CTestTestfile.cmake
_deps
code/edgeAI.code-workspace
.DS_Store

9
.gitmodules vendored Normal file
View File

@@ -0,0 +1,9 @@
[submodule "code/components/esp32-camera"]
path = code/components/esp32-camera
url = https://github.com/espressif/esp32-camera.git
[submodule "code/components/esp-nn"]
path = code/components/esp-nn
url = https://github.com/espressif/esp-nn.git
[submodule "code/components/tflite-micro-esp-examples"]
path = code/components/tflite-micro-esp-examples
url = https://github.com/espressif/tflite-micro-esp-examples.git

View File

@@ -1,447 +1,669 @@
# Versions
# Changelog
##### 10.6.2 - Stability Increase (2022-07-24)
## [Unreleased]
- **NEW 10.6.2**: ignore hidden files in model selection (configuration page)
Improve **u**ser e**x**perience
- **NEW 10.6.1**: Revoke esp32cam & tflite update
:bangbang: The release breaks a few things in ota update :bangbang:
- **NEW 10.6.1**: Bug Fix: tflite-filename with ".", HTML spelling error
**Make sure to read the instructions below carfully!**.
- IndluxDB: direct injection into InfluxDB - thanks to **[wetneb](https://github.com/wetneb)**
1. Backup your configuration (use the `System > Backup/Restore` page)!
2. You should update to `11.3.1` before you update to this release. All other migrations are not tested.
Rolling newer than `11.3.1` can also be used, but no guaranty.
3. Upload and update the `firmware.bin` file from this release. **but do not reboot**
4. Upload the `html-from-11.3.1.zip` in html upload and update the web interface.
5. Now you can reboot.
- MQTT: implemented "Retain Flag" and extend with absolute Change (in addition to rate)
If anything bricks you can try to
1. Call `http://<IP>/ota?task=update&file=firmware.bin` resp. `http://<IP>/ota?task=update&file=html.zip` if the upload successed but the extraction failed.
1. Use the initial_esp32_setup.zip ( <https://github.com/jomjol/AI-on-the-edge-device/wiki/Installation> ) as alternative.
- `config.ini`: removal of modelsize (readout from tflite)
### Added
- Updated analog neural network file (`ana1000s2.tflite`) & digital neural network file (`dig1400s2q.tflite`)
- Automatic release creation
- Newest firmware of rolling branch now automatically build and provided in [Github Actions Output](https://github.com/jomjol/AI-on-the-edge-device/actions) (developers only)
- [\#1068](https://github.com/jomjol/AI-on-the-edge-device/issues/1068) New update mechanism:
- Handling of all files (`zip`, `tfl`, `tflite`, `bin`) within in one common update interface
- Using the `update.zip` from the [Release page](https://github.com/jomjol/AI-on-the-edge-device/releases)
- Status (`upload`, `processing`, ...) displayed on Web Interface
- Automatical detection and suggestion for reboot where needed (Web Interface uupdates only need a page refresh)
- :bangbang: Best for OTA use Firefox. Chrome works with warnings. Safari stuck in upload.
- TFMicro/Lite: Update (espressif Version 20220716)
### Changed
- Integrated version info better shown on the Info page and in the log
- Updated menu
- Update used libraries (`tflite`, `esp32-cam`, `esp-nn`, as of 20220924)
### Fixed
- Updated esp32cam (v20220716)
- [\#1092](https://github.com/jomjol/AI-on-the-edge-device/issues/1092) censor passwords in log outputs
- [\#1029](https://github.com/jomjol/AI-on-the-edge-device/issues/1029) wrong change of `checkDigitConsistency` now working like releases before `11.3.1`
- Spelling corrections (**[cristianmitran](https://github.com/cristianmitran)**)
- ESP-IDF: Update to 4.4
- Internal update (CNN algorithm optimizations, reparation for new neural network type)
### Removed
- Remove the folder `/firmware` from GitHub repository.
If you want to get the latest `firmware.bin` and `html.zip` files, please download from the automated [build action](https://github.com/jomjol/AI-on-the-edge-device/actions) or [release page](https://github.com/jomjol/AI-on-the-edge-device/releases)
- Bug Fix: no time with fixed IP, Postprocessing, MQTT
## [11.3.1] - (2022-09-17)
Intermediate Digits
- **ATTENTION**:
- first update the `firmware.bin` and ensure that the new version is running
- Only afterwards update the `html.zip`
- Otherwise the downwards compatibility of the new counter clockwise feature is not given and you end in a reboot loop, that needs manual flashing!
##### 10.5.2 - Stability Increase (2022-02-22)
- NEW 10.5.2: Bug Fix: wrong `firmware.bin` (no rate update)
- NEW 10.5.1: Bug Fix: wrong return value, rate value & PreValue status, HTML: SSID & IP were not displayed
- MQTT: changed wifi naming to "wifiRSSI"
- HTML: check selectable values for consistency
- Refactoring of check postprocessing consistency (e.g. max rate, negative rate, ...)
- Bug Fix: corrected error in "Check Consistency Increase"
- **NEW v11.3.1**: corrected corrupted asset `firmware.bin`
- Increased precision (more than 6-7 digits)
- Implements Counter Clockwise Analog Pointers
- Improved post processing algorithm
- Debugging: intensive use of testcases
- MQTT: improved handling, extended logging, automated reconnect
- HTML: Backup Option for Configuration
- HTML: Improved Reboot
- HTML: Update WebUI (Reboot, Infos, CPU Temp, RSSI)
- This version is largely also based on the work of **[caco3](https://github.com/caco3)**, **[adellafave](https://github.com/adellafave)**, **[haverland](https://github.com/haverland)**, **[stefanbode](https://github.com/stefanbode)**, **[PLCHome](https://github.com/PLCHome)**
## [11.2.0] - (2022-08-28)
Intermediate Digits
- Updated Tensorflow / TFlite to newest tflite (version as of 2022-07-27)
- Updated analog neural network file (`ana-cont_11.3.0_s2.tflite` - default, `ana-class100_0120_s1_q.tflite`)
- Updated digital neural network file (`dig-cont_0570_s3.tflite` - default, `dig-class100_0120_s2_q.tflite`)
- Added automated filtering of tflite-file in the graphical configuration (thanks to @**[caco3](https://github.com/caco3)**)
- Updated consistency algorithm & test cases
- HTML: added favicon and system name, Improved reboot dialog (thanks to @**[caco3](https://github.com/caco3)**)
## [11.1.1] - (2022-08-22)
Intermediate Digits
- New and improved consistency check (especially with analog and digital counters mixed)
- Bug Fix: digital counter algorithm
## [11.0.1] - (2022-08-18)
Intermediate Digits
- **NEW v11.0.1**: Bug Fix InfluxDB configuration (only update of html.zip necessary)
- Implementation of new CNN types to detect intermediate values of digits with rolling numbers
- By default the old algo (0, 1, ..., 9, "N") is active (due to the limited types of digits trained so far)
- Activation can be done by selection a tflite file with the new trained model in the 'config.ini'
- **Details can be found in the [wiki](https://github.com/jomjol/AI-on-the-edge-device/wiki/Neural-Network-Types)** (different types, trained image types, naming convention)
- Updated neural network files (and adaption to new naming convention)
- Published a tool to download and combine log files - **Thanks to **
- Files see ['/tools/logfile-tool'](tbd), How-to see [wiki](https://github.com/jomjol/AI-on-the-edge-device/wiki/Gasmeter-Log-Downloader)
- Bug Fix: InfluxDB enabling in grahic configuration
## [10.6.2] - (2022-07-24)
##### 10.4.0 - Stability Increase (2022-02-12)
Stability Increase
- Graphical configuration: select available neural network files (*.tfl, *.tflite) from drop down menu
- OTA-update: add option to upload tfl / tflite files to the correct location (`/config/`)
- In the future the new files will also be copied to the `firmware` directory of the repository
- Added Wifi RSSI to MQTT information
- Updated analog neural network file (`ana-s3-q-20220105.tflite`)
- Updated digital neural network file (`dig-s1-q-20220102.tflite`)
- Updated build environment to `Espressif 3.5.0`
### Added
- **NEW 10.6.2**: ignore hidden files in model selection (configuration page)
- **NEW 10.6.1**: Revoke esp32cam & tflite update
##### 10.3.0 - Stability Increase (2022-01-29)
- **NEW 10.6.1**: Bug Fix: tflite-filename with ".", HTML spelling error
- Implemented LED flash dimming (`LEDIntensity`).
Remark: as auto illumination in the camera is used, this is rather for energy saving. It will not help reducing reflections
- Additional camera parameters: saturation, contrast (although not too much impact yet)
- Some readings will have removable "N"s that can not be removed automatically and are handled with an "error" --> no return value in the field "value" anymore (still reported back via field "raw value")
- Updated esp32 camera hardware driver
- Bug fix: MQTT, HTML improvements
- IndluxDB: direct injection into InfluxDB - thanks to **[wetneb](https://github.com/wetneb)**
- MQTT: implemented "Retain Flag" and extend with absolute Change (in addition to rate)
- `config.ini`: removal of modelsize (readout from tflite)
- Updated analog neural network file (`ana1000s2.tflite`) & digital neural network file (`dig1400s2q.tflite`)
- TFMicro/Lite: Update (espressif Version 20220716)
- Updated esp32cam (v20220716)
- ESP-IDF: Update to 4.4
- Internal update (CNN algorithm optimizations, reparation for new neural network type)
- Bug Fix: no time with fixed IP, Postprocessing, MQTT
## [10.5.2] - (2022-02-22)
Stability Increase
### Changed
- NEW 10.5.2: Bug Fix: wrong `firmware.bin` (no rate update)
- NEW 10.5.1: Bug Fix: wrong return value, rate value & PreValue status, HTML: SSID & IP were not displayed
- MQTT: changed wifi naming to "wifiRSSI"
- HTML: check selectable values for consistency
- Refactoring of check postprocessing consistency (e.g. max rate, negative rate, ...)
- Bug Fix: corrected error in "Check Consistency Increase"
## [10.4.0] - (2022-02-12)
Stability Increase
### Changed
- Graphical configuration: select available neural network files (_.tfl,_.tflite) from drop down menu
- OTA-update: add option to upload tfl / tflite files to the correct location (`/config/`)
- In the future the new files will also be copied to the `firmware` directory of the repository
- Added Wifi RSSI to MQTT information
- Updated analog neural network file (`ana-s3-q-20220105.tflite`)
- Updated digital neural network file (`dig-s1-q-20220102.tflite`)
- Updated build environment to `Espressif 3.5.0`
## [10.3.0] - (2022-01-29)
Stability Increase
### Changed
- Implemented LED flash dimming (`LEDIntensity`).
Remark: as auto illumination in the camera is used, this is rather for energy saving. It will not help reducing reflections
- Additional camera parameters: saturation, contrast (although not too much impact yet)
- Some readings will have removable "N"s that can not be removed automatically and are handled with an "error" --> no return value in the field "value" anymore (still reported back via field "raw value")
- Updated esp32 camera hardware driver
- Bug fix: MQTT, HTML improvements
**ATTENTION: The new ESP32 camera hardware driver is much more stable on newer OV2640 versions (no or much less reboots) but seems to be not fully compatible with older versions.**
* If you have problem with stalled systems you can try the following
- Update the parameter `ImageQuality` to `12` instead of current value `5` (manually in the `config.ini`)
If you have problem with stalled systems you can try the following
- If this is not helping, you might need to update your hardware or stay with version 9.2
- Update the parameter `ImageQuality` to `12` instead of current value `5` (manually in the `config.ini`)
##### 10.2.0 - Stability Increase (2022-01-14)
- If this is not helping, you might need to update your hardware or stay with version 9.2
- Due to the updated camera driver, the image looks different and a new setup might be needed
## [10.2.0] - (2022-01-14)
- Update reference image
- Update Alignment marks
Stability Increase
- Reduce reboot due to camera problems
### Changed
- Update esp32-camera to new version (master as of 2022-01-09)
- Due to the updated camera driver, the image looks different and a new setup might be needed
- Update reference image
- Update Alignment marks
##### 10.1.1 - Stability Increase (2022-01-12)
- Reduce reboot due to camera problems
- Bug Fix MQTT problem
- Issue:
- Changing from v9.x to 10.x the MQTT-parameter "Topic" was renamed into "MainTopic" to address multiple number meters. This renaming should have been done automatically in the background within the graphical configuration, but was not working. Instead the parameter "Topic" was deleted and "MainTopic" was set to disabled and "undefined".
- ToDo
- Update the `html.zip`
- If old `config.ini` available: copy it to `/config`, open the graphical configuration and save it again.
- If old `config.ini` not available: reset the parameter "MainTopic" within the `config.ini` manually
- Reboot
- Update esp32-camera to new version (master as of 2022-01-09)
##### 10.1.0 - Stability Increase (2022-01-09)
## [10.1.1] - (2022-01-12)
- Reduce ESP32 frequency to 160MHz
Stability Increase
- Update tflite (new source: https://github.com/espressif/tflite-micro-esp-examples)
### Changed
- Update analog neural network (ana-s3-q-20220105.tflite)
- Bug Fix MQTT problem
- Issue:
- Changing from v9.x to 10.x the MQTT-parameter "Topic" was renamed into "MainTopic" to address multiple number meters. This renaming should have been done automatically in the background within the graphical configuration, but was not working. Instead the parameter "Topic" was deleted and "MainTopic" was set to disabled and "undefined".
- ToDo
- Update the `html.zip`
- If old `config.ini` available: copy it to `/config`, open the graphical configuration and save it again.
- If old `config.ini` not available: reset the parameter "MainTopic" within the `config.ini` manually
- Reboot
- Update digital neural network (dig-s1-q-20220102.tflite)
## [10.1.0] - (2022-01-09)
- Increased web-server buffers
- bug fix: compiler compatibility
Stability Increase
##### 10.0.2 - Stability Increase (2022-01-01)
### Changed
- NEW v10.0.2: Corrected JSON error
- Reduce ESP32 frequency to 160MHz
- Updated compiler toolchain to ESP-IDF 4.3
- Update tflite (new source: <https://github.com/espressif/tflite-micro-esp-examples>)
- Removal of memory leak
- Update analog neural network (ana-s3-q-20220105.tflite)
- Improved error handling during startup (check PSRAM and camera with remark in logfile)
- Update digital neural network (dig-s1-q-20220102.tflite)
- MQTT: implemented raw value additionally, removal of regex contrain
- Increased web-server buffers
- Normalized Parameter ``MaxRateValue`` to "change per minute"
- bug fix: compiler compatibility
- HTML: improved input handling
## [10.0.2] - (2022-01-01)
- Corrected error handling: in case of error the old value, rate, timestamp are not transmitted any more
Stability Increase
### Changed
##### 9.2.0 - External Illumination (2021-12-02)
- NEW v10.0.2: Corrected JSON error
- Direct JSON access: ``http://IP-ADRESS/json``
- Error message in log file in case camera error during startup
- Upgrade analog CNN to v9.1.0
- Upgrade digital CNN to v13.3.0 (added new images)
- html: support of different ports
- Updated compiler toolchain to ESP-IDF 4.3
##### 9.1.1 - External Illumination (2021-11-16)
- Removal of memory leak
- NEW 9.1.1 bug fix: LED implemenetation
- External LEDs: change control mode (resolve bug with more than 2 LEDs)
- Additional info into log file
- Bug fix: decimal shift, html, log file
- Improved error handling during startup (check PSRAM and camera with remark in logfile)
##### 9.0.0 - External Illumination (2021-10-23)
- MQTT: implemented raw value additionally, removal of regex contrain
* Implementation of external illumination to adjust positioning, brightness and color of the illumination now set individually
* Technical details can be found in the wiki: https://github.com/jomjol/AI-on-the-edge-device/wiki/External-LED
<img src="https://raw.githubusercontent.com/jomjol/ai-on-the-edge-device/master/images/intern_vs_external.jpg" width="500">
* New housing published for external LEDs and small clearing: https://www.thingiverse.com/thing:5028229
- Normalized Parameter `MaxRateValue` to "change per minute"
- HTML: improved input handling
- Corrected error handling: in case of error the old value, rate, timestamp are not transmitted any more
##### 8.5.0 - Multi Meter Support (2021-10-07)
## [9.2.0] - (2021-12-02)
* Upgrade digital CNN to v13.1.0 (added new images)
* bug fix: wlan password with space, double digit output
External Illumination
##### 8.4.0 - Multi Meter Support (2021-09-25)
### Changed
* License change (remove MIT license, remark see below)
- Direct JSON access: `http://IP-ADRESS/json`
- Error message in log file in case camera error during startup
- Upgrade analog CNN to v9.1.0
- Upgrade digital CNN to v13.3.0 (added new images)
- html: support of different ports
* html: show hostname in title and main page
## [9.1.1] - External Illumination (2021-11-16)
* configuration:
### Changed
* moved setting `ExtendedResolution` to individual number settings
* New parameter `IgnoreLeadingNaN` (delete leading NaN's specifically)
* **ATTENTION**: update of the `config.ini` needed (open, adjust `ExtendedResolution`, save)
- NEW 9.1.1 bug fix: LED implemenetation
- External LEDs: change control mode (resolve bug with more than 2 LEDs)
- Additional info into log file
- Bug fix: decimal shift, html, log file
* Bug fixing (html, images of recognized numbers)
## [9.0.0] - External Illumination (2021-10-23)
### Changed
### **ATTENTION: LICENSE CHANGE - removal of MIT License.**
- Implementation of external illumination to adjust positioning, brightness and color of the illumination now set individually
- Technical details can be found in the wiki: <https://github.com/jomjol/AI-on-the-edge-device/wiki/External-LED>
<img src="https://raw.githubusercontent.com/jomjol/ai-on-the-edge-device/master/images/intern_vs_external.jpg" width="500">
- New housing published for external LEDs and small clearing: <https://www.thingiverse.com/thing:5028229>
- Currently no licence published - copyright belongs to author
- If you are interested in a commercial usage or dedicated versions please contact the developer
- no limits to private usage
## [8.5.0] - Multi Meter Support (2021-10-07)
### Changed
- Upgrade digital CNN to v13.1.0 (added new images)
- bug fix: wlan password with space, double digit output
##### 8.3.0 - Multi Meter Support (2021-09-12)
## [8.4.0] - Multi Meter Support (2021-09-25)
* Upgrade digital CNN to v12.1.0 (added new images)
* Dedicated NaN handling, internal refactoring (CNN-Handling)
* HTML: confirmation after config.ini update
* Bug fixing
### Changed
##### 8.2.0 - Multi Meter Support (2021-08-24)
- License change (remove MIT license, remark see below)
* Improve server responsiveness
* Flow status and prevalue status in overview
* Improved prevalue handling
- html: show hostname in title and main page
##### 8.1.0 - Multi Meter Support (2021-08-12)
- configuration:
* GPIO: using the general mqtt main topic for GPIO
- moved setting `ExtendedResolution` to individual number settings
- New parameter `IgnoreLeadingNaN` (delete leading NaN's specifically)
- **ATTENTION**: update of the `config.ini` needed (open, adjust `ExtendedResolution`, save)
* Upgrade digital CNN to v12.0.0 (added new images)
* Update tfmicro to new master (2021-08-07)
* Bug fix: remove text in mqtt value, remove connect limit in wlan reconnet
- Bug fixing (html, images of recognized numbers)
##### 8.0.5 - Multi Meter Support (2021-08-01)
**ATTENTION: LICENSE CHANGE - removal of MIT License.**
* NEW 8.0.5: bug fix: saving prevalue
* NEW 8.0.4: bug fix: load config.ini after upgrade
* NEW 8.0.3: bug fix: reboot during `config.ini` handling, html error
* NEW 8.0.2: saving roundes prevalue, bug fix html server
* NEW 8.0.1: bug fix: html handling of parameter `FixedExposure` and `ImageSize`
* Dual / multi meter support (more than 1 number to be recognized)
This is implemented with the feature "number" on the ROI definition as well as selected options
* MQTT: standardization of the naming - including new topics (`json`, `freeMem `, `uptime`)c
* Preparation for extended GPIO support (thanks to Zwerk2k) - not tested and fully functional yet
* Bug fixing: html server, memory leak, MQTT connect, hostname, turn of flash LED
- Currently no licence published - copyright belongs to author
<span style="color: red;">**ATTENTION: the configuration and prevalue files are modified automatically and will not be backward compatible!**</span>
- If you are interested in a commercial usage or dedicated versions please contact the developer
- no limits to private usage
##### 7.1.2 MQTT-Update - (2021-06-17)
## [8.3.0] - Multi Meter Support (2021-09-12)
* NEW: 7.1.2: bug fix setting hostname, Flash-LED not off during reboot
### Changed
* NEW: 7.1.1: bug fix wlan password with "=" (again)
- Upgrade digital CNN to v12.1.0 (added new images)
- Dedicated NaN handling, internal refactoring (CNN-Handling)
- HTML: confirmation after config.ini update
- Bug fixing
* MQTT error message: changes "no error", send retain flag
## [8.2.0] - Multi Meter Support (2021-08-24)
* Update wlan handling to esp-idf 4.1
### Changed
* Upgrade digital CNN to v8.7.0 (added new images)
- Improve server responsiveness
* Bug fix: MQTT, WLAN, LED-Controll, GPIO usage, fixed IP, calculation flow rate
- Flow status and prevalue status in overview
- Improved prevalue handling
##### 7.0.1 MQTT-Update - (2021-05-13)
## [8.1.0] - Multi Meter Support (2021-08-12)
* NEW: 7.0.1: bug fix wlan password with "="
### Changed
* Upgrade digital CNN to v8.5.0 (added new images)
- GPIO: using the general mqtt main topic for GPIO
* New MQTT topics: flow rate (units/minute), time stamp (last correct read readout)
* Update MQTT/Error topic to " " in case no error (instead of empty string)
- Upgrade digital CNN to v12.0.0 (added new images)
- Update tfmicro to new master (2021-08-07)
- Bug fix: remove text in mqtt value, remove connect limit in wlan reconnet
* Portrait or landscape image orientation in rotated image (avoid cropping)
## [8.0.5] - Multi Meter Support (2021-08-01)
##### 6.7.2 Image Processing in Memory - (2021-05-01)
### Changed
* NEW 6.7.2: Updated html for setup modus - remove reboot on edit configuration)
- NEW 8.0.5: bug fix: saving prevalue
* NEW 6.7.1: Improved stability of camera (back to v6.6.1) - remove black strips and areas
* Upgrade digital CNN to v8.3.0 (added new type of digits)
- NEW 8.0.4: bug fix: load config.ini after upgrade
- NEW 8.0.3: bug fix: reboot during `config.ini` handling, html error
- NEW 8.0.2: saving roundes prevalue, bug fix html server
- NEW 8.0.1: bug fix: html handling of parameter `FixedExposure` and `ImageSize`
- Dual / multi meter support (more than 1 number to be recognized)
This is implemented with the feature "number" on the ROI definition as well as selected options
- MQTT: standardization of the naming - including new topics (`json`, `freeMem`, `uptime`)c
- Preparation for extended GPIO support (thanks to Zwerk2k) - not tested and fully functional yet
- Bug fixing: html server, memory leak, MQTT connect, hostname, turn of flash LED
* Internal update: TFlite (v2.5), esp32cam, startup sequence
<span style="color: red;">**ATTENTION: the configuration and prevalue files are modified automatically and will not be backward compatible!**</span>
* Rollback to espressif v2.1.0, as v3.2.0 shows unstable reboot
## [7.1.2] MQTT-Update - (2021-06-17)
* Bugfix: WLan-passwords, reset of hostname
### Changed
- NEW: 7.1.2: bug fix setting hostname, Flash-LED not off during reboot
##### 6.6.1 Image Processing in Memory - (2021-04-05)
* NEW 6.6.1: failed SD card initialization indicated by fast blinking LED at startup
* Improved SD-card handling (increase compatibility with more type of cards)
- NEW: 7.1.1: bug fix wlan password with "=" (again)
##### 6.5.0 Image Processing in Memory - (2021-03-25)
- MQTT error message: changes "no error", send retain flag
* Upgrade digital CNN to v8.2.0 (added new type of digits)
* Supporting alignment structures in ROI definition
* Bug fixing: definition of hostname in `config.ini`
- Update wlan handling to esp-idf 4.1
##### 6.4.0 Image Processing in Memory - (2021-03-20)
- Upgrade digital CNN to v8.7.0 (added new images)
* Additional alignment marks for settings the ROIs (analog and digit)
* Upgrade analog CNN to v7.0.0 (added new type of pointer)
- Bug fix: MQTT, WLAN, LED-Controll, GPIO usage, fixed IP, calculation flow rate
##### 6.3.1 Image Processing in Memory - (2021-03-16)
## [7.0.1] MQTT-Update - (2021-05-13)
* NEW: 6.3.1: bug fixing in initial edit reference image and `config.ini` (Spelling error in `InitialRotate`)
* Initial setup mode: bug fixing, error correction
* Bug-fixing
### Changed
##### 6.2.2 Image Processing in Memory - (2021-03-10)
- NEW: 7.0.1: bug fix wlan password with "="
* NEW 6.2.2: bug fixing
* NEW 6.2.1: Changed brightness and contrast to default if not enabled (resolves to bright images)
* Determination of fixed illumination settings during startup - speed up of 5s in each run
* Update digital CNN to v8.1.1 (additional digital images trained)
* Extended error message in MQTT error message
- Upgrade digital CNN to v8.5.0 (added new images)
* Image brightness is now adjustable
- New MQTT topics: flow rate (units/minute), time stamp (last correct read readout)
- Update MQTT/Error topic to " " in case no error (instead of empty string)
* Bug fixing: minor topics
- Portrait or landscape image orientation in rotated image (avoid cropping)
## [6.7.2] Image Processing in Memory - (2021-05-01)
##### 6.1.0 Image Processing in Memory - (2021-01-20)
### Changed
* Disabling of analog / digital counters in configuration
* Improved Alignment Algorithm (`AlignmentAlgo` = `Default`, `Accurate` , `Fast`)
* Analog counters: `ExtendedResolution` (last digit is extended by sub comma value of CNN)
* `config.ini`: additional parameter `hostname` (additional to wlan.ini)
* Switching of GPIO12/13 via http-interface: `/GPIO?GPIO=12&Status=high/low`
* Bug fixing: html configuration page, wlan password ("=" now possible)
- NEW 6.7.2: Updated html for setup modus - remove reboot on edit configuration)
##### 6.0.0 Image Processing in Memory - (2021-01-02)
* **Major change**: image processing fully in memory - no need of SD card buffer anymore
- NEW 6.7.1: Improved stability of camera (back to v6.6.1) - remove black strips and areas
* Need to limit camera resolution to VGA (due to memory limits)
* MQTT: Last Will Testament (LWT) implemented: "connection lost" in case of connection lost to `TopicError`
* Disabled `CheckDigitIncreaseConsistency` in default configuration - must now be explicit enabled if needed
* Update digital CNN to v7.2.1 (additional digital images trained)
* Setting of arbitrary time server in `config.ini`
* Option for fixed IP-, DNS-Settings in `wlan.ini`
* Increased stability (internal image and camera handling)
* Bug fixing: edit digits, handling PreValue, html-bugs
- Upgrade digital CNN to v8.3.0 (added new type of digits)
- Internal update: TFlite (v2.5), esp32cam, startup sequence
- Rollback to espressif v2.1.0, as v3.2.0 shows unstable reboot
##### 5.0.0 Setup Modus - (2020-12-06)
- Bugfix: WLan-passwords, reset of hostname
* Implementation of initial setup modus for fresh installation
## [6.6.1] Image Processing in Memory - (2021-04-05)
* Code restructuring (full compatibility between pure ESP-IDF and Platformio w/ espressif)
### Changed
- NEW 6.6.1: failed SD card initialization indicated by fast blinking LED at startup
##### 4.1.1 Configuration editor - (2020-12-02)
* Bug fixing: internal improvement of file handling (reduce not responding)
- Improved SD-card handling (increase compatibility with more type of cards)
## [6.5.0] Image Processing in Memory - (2021-03-25)
##### 4.1.0 Configuration editor - (2020-11-30)
### Changed
* Implementation of configuration editor (including basic and expert mode)
- Upgrade digital CNN to v8.2.0 (added new type of digits)
* Adjustable time zone to adjust to local time setting (incl. daylight saving time)
* MQTT: additional topic for error reporting
- Supporting alignment structures in ROI definition
- Bug fixing: definition of hostname in `config.ini`
* standardized access to current logfile via `http://IP-ADRESS/logfileact`
## [6.4.0] Image Processing in Memory - (2021-03-20)
* Update digital CNN to v7.2.0, analog CNN to 6.3.0
### Changed
* Bug fixing: truncation error, CheckDigitConsistency & PreValue implementation
- Additional alignment marks for settings the ROIs (analog and digit)
- Upgrade analog CNN to v7.0.0 (added new type of pointer)
##### 4.0.0 Tflite Core - (2020-11-15)
## [6.3.1] Image Processing in Memory - (2021-03-16)
* Implementation of rolling log-files
### Changed
* Update Tflite-Core to master@20201108 (v2.4)
- NEW: 6.3.1: bug fixing in initial edit reference image and `config.ini` (Spelling error in `InitialRotate`)
* Bug-fixing for reducing reboots
- Initial setup mode: bug fixing, error correction
- Bug-fixing
##### 3.1.0 MQTT-Client - (2020-10-26)
## [6.2.2] Image Processing in Memory - (2021-03-10)
* Update digital CNN to v6.5.0 and HTML (Info to hostname, IP, ssid)
### Changed
* New implementation of "checkDigitConsistency" also for digits
* MQTT-Adapter: user and password for sign in MQTT-Broker
- NEW 6.2.2: bug fixing
##### 3.0.0 MQTT-Client (2020-10-14)
* Implementation of MQTT Client
* Improved Version Control
* bug-fixing
- NEW 6.2.1: Changed brightness and contrast to default if not enabled (resolves to bright images)
- Determination of fixed illumination settings during startup - speed up of 5s in each run
- Update digital CNN to v8.1.1 (additional digital images trained)
##### 2.2.1 Version Control (2020-09-27)
- Extended error message in MQTT error message
* Bug-Fixing (hostname in wlan.ini and error handling inside flow)
- Image brightness is now adjustable
- Bug fixing: minor topics
##### 2.2.0 Version Control (2020-09-27)
## [6.1.0] Image Processing in Memory - (2021-01-20)
* Integrated automated versioning system (menu: SYSTEM --> INFO)
* Update Build-System to PlatformIO - Espressif 32 v2.0.0 (ESP-IDF 4.1)
### Changed
- Disabling of analog / digital counters in configuration
##### 2.1.0 Decimal Shift, Chrome & Edge (2020-09-25)
* Implementation of Decimal Shift
- Improved Alignment Algorithm (`AlignmentAlgo` = `Default`, `Accurate` , `Fast`)
- Analog counters: `ExtendedResolution` (last digit is extended by sub comma value of CNN)
- `config.ini`: additional parameter `hostname` (additional to wlan.ini)
- Switching of GPIO12/13 via http-interface: `/GPIO?GPIO=12&Status=high/low`
- Bug fixing: html configuration page, wlan password ("=" now possible)
* Update default CNN for digits to v6.4.0
## [6.0.0] Image Processing in Memory - (2021-01-02)
* Improvement HTML
### Changed
* Support for Chrome and Edge
- **Major change**: image processing fully in memory - no need of SD card buffer anymore
* Reduce logging to minimum - extended logging on demand
- Need to limit camera resolution to VGA (due to memory limits)
* Implementation of hostname in wlan.ini (`hostname = "HOSTNAME")`
* Bug fixing, code corrections
- MQTT: Last Will Testament (LWT) implemented: "connection lost" in case of connection lost to `TopicError`
- Disabled `CheckDigitIncreaseConsistency` in default configuration - must now be explicit enabled if needed
- Update digital CNN to v7.2.1 (additional digital images trained)
- Setting of arbitrary time server in `config.ini`
- Option for fixed IP-, DNS-Settings in `wlan.ini`
- Increased stability (internal image and camera handling)
- Bug fixing: edit digits, handling PreValue, html-bugs
## [5.0.0] Setup Modus - (2020-12-06)
##### 2.0.0 Layout update (2020-09-12)
### Changed
* Update to **new and modern layout**
* Support for Chrome improved
* Improved robustness: improved error handling in auto flow reduces spontaneous reboots
* File server: Option for "DELETE ALL"
* WLan: support of spaces in SSID and password
* Reference Image: Option for mirror image, option for image update on the fly
* additional parameter in `wasserzaehler.html?noerror=true` to suppress an potential error message
* bug fixing
- Implementation of initial setup modus for fresh installation
- Code restructuring (full compatibility between pure ESP-IDF and Platformio w/ espressif)
##### 1.1.3 (2020-09-09)
## [4.1.1] Configuration editor - (2020-12-02)
* **Bug in configuration of analog ROIs corrected** - correction in v.1.0.2 did not work properly
* Improved update page for the web server (`/html` can be updated via a zip-file, which is provided in `/firmware/html.zip`)
* Improved Chrome support
### Changed
##### 1.1.0 (2020-09-06)
- Bug fixing: internal improvement of file handling (reduce not responding)
* Implementation of "delete complete directory"
**Attention: beside the `firmware.bin`, also the content of `/html` needs to be updated!**
## [4.1.0] Configuration editor - (2020-11-30)
### Changed
- Implementation of configuration editor (including basic and expert mode)
##### 1.0.2 (2020-09-06)
* Bug in configuration of analog ROIs corrected
* minor bug correction
- Adjustable time zone to adjust to local time setting (incl. daylight saving time)
##### 1.0.1 (2020-09-05)
- MQTT: additional topic for error reporting
* preValue.ini Bug corrected
* minor bug correction
- standardized access to current logfile via `http://IP-ADRESS/logfileact`
##### 1.0.0 (2020-09-04)
- Update digital CNN to v7.2.0, analog CNN to 6.3.0
* **First usable version** - compatible to previous project (https://github.com/jomjol/water-meter-system-complete)
* NEW:
* no docker container for CNN calculation necessary
* web based configuration editor on board
- Bug fixing: truncation error, CheckDigitConsistency & PreValue implementation
##### 0.1.0 (2020-08-07)
## [4.0.0] Tflite Core - (2020-11-15)
* Initial Version
### Changed
- Implementation of rolling log-files
- Update Tflite-Core to master@20201108 (v2.4)
- Bug-fixing for reducing reboots
## [3.1.0] MQTT-Client - (2020-10-26)
### Changed
- Update digital CNN to v6.5.0 and HTML (Info to hostname, IP, ssid)
- New implementation of "checkDigitConsistency" also for digits
- MQTT-Adapter: user and password for sign in MQTT-Broker
## [3.0.0] MQTT-Client (2020-10-14)
### Changed
- Implementation of MQTT Client
- Improved Version Control
- bug-fixing
## [2.2.1] Version Control (2020-09-27)
### Changed
- Bug-Fixing (hostname in wlan.ini and error handling inside flow)
## \[2.2.0| Version Control (2020-09-27)
### Changed
- Integrated automated versioning system (menu: SYSTEM --> INFO)
- Update Build-System to PlatformIO - Espressif 32 v2.0.0 (ESP-IDF 4.1)
## [2.1.0] Decimal Shift, Chrome & Edge (2020-09-25)
### Changed
- Implementation of Decimal Shift
- Update default CNN for digits to v6.4.0
- Improvement HTML
- Support for Chrome and Edge
- Reduce logging to minimum - extended logging on demand
- Implementation of hostname in wlan.ini (`hostname = "HOSTNAME")`
- Bug fixing, code corrections
## [2.0.0] Layout update (2020-09-12)
### Changed
- Update to **new and modern layout**
- Support for Chrome improved
- Improved robustness: improved error handling in auto flow reduces spontaneous reboots
- File server: Option for "DELETE ALL"
- WLan: support of spaces in SSID and password
- Reference Image: Option for mirror image, option for image update on the fly
- additional parameter in `wasserzaehler.html?noerror=true` to suppress an potential error message
- bug fixing
## [1.1.3](2020-09-09)
### Changed
- **Bug in configuration of analog ROIs corrected** - correction in v.1.0.2 did not work properly
- Improved update page for the web server (`/html` can be updated via a zip-file, which is provided in `/firmware/html.zip`)
- Improved Chrome support
## [1.1.0](2020-09-06)
### Changed
- Implementation of "delete complete directory"
**Attention: beside the `firmware.bin`, also the content of `/html` needs to be updated!**
## [1.0.2](2020-09-06)
### Changed
- Bug in configuration of analog ROIs corrected
- minor bug correction
## [1.0.1](2020-09-05)
### Changed
- preValue.ini Bug corrected
- minor bug correction
## [1.0.0](2020-09-04)
### Changed
- **First usable version** - compatible to previous project (<https://github.com/jomjol/water-meter-system-complete>)
- NEW:
- no docker container for CNN calculation necessary
- web based configuration editor on board
## [0.1.0](2020-08-07)
### Changed
- Initial Version
[Unreleased]: https://github.com/haverland/AI-on-the-edge-device/compare/11.4.3...HEAD
[11.4.3]: https://github.com/haverland/AI-on-the-edge-device/compare/10.6.2...11.4.3
[11.4.2]: https://github.com/haverland/AI-on-the-edge-device/compare/10.6.2...11.4.2
[11.3.9]: https://github.com/haverland/AI-on-the-edge-device/compare/10.6.2...11.3.9

View File

@@ -11,6 +11,15 @@
____
#### ~~#29 Add favicon and use the hostname for the website~~- implemented v11.3.1
~~* https://github.com/jomjol/AI-on-the-edge-device/issues/927~~
#### #28 Improved error handling for ROIs
* In case a ROI is out of the image, there is no error message, but a non sense image is used
* Implement a error message for wrong configuratioin of ROI
#### #27 Use Homie Spec for Mqtt binding
* Use the standardized Home Protocol for the Mqtt binding
@@ -40,9 +49,9 @@ ____
#### #22 Direct hint to the different neural network files in the other repositories
#### ~~#22 Direct hint to the different neural network files in the other repositories~~- implemented >v11.3.1
* https://github.com/jomjol/AI-on-the-edge-device/issues/644
~~* https://github.com/jomjol/AI-on-the-edge-device/issues/644~~
@@ -55,7 +64,10 @@ ____
#### #20 Deep sleep and push mode
* Let the device be normally in deep sleep state, and wake it up periodically to collect data and push it via MQTT or HTTP post.
* Support ESP-NOW to reduce the overhead of connecting to wifi and mqtt
* the above should enable battery powered applications
* An other way to set deep sleep would be to enable it in a specific period (at night).
#### #19 Extended log informations
@@ -96,13 +108,13 @@ ____
#### #14 Backup and restore option for configuration
#### ~~#14 Backup and restore option for configuration~~- implemented v11.3.1
* https://github.com/jomjol/AI-on-the-edge-device/issues/459
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/459~~
* Implement a zip file compression for store and restore
* ~~Implement a zip file compression for store and restore~~
* Update the html to handle it
* ~~Update the html to handle it~~

149
README.md
View File

@@ -1,27 +1,66 @@
# AI-on-the-edge-device
# Welcome to the AI-on-the-edge-device
Artificial intelligence based systems have been established in our every days live. Just think of speech or image recognition. Most of the systems relay on either powerful processors or a direct connection to the cloud for doing the calculations up there. With the increasing power of modern processors the AI systems are coming closer to the end user - which is usually called **edge computing**.
Here this edge computing is brought into a practical oriented example, where a AI network is implemented on a ESP32 device so: **AI on the edge**.
This is an example of Artificial Intelligence (AI) calculations on a very cheap hardware.
This projects allows you to digitalize your **analoge** water, gas, power and other meters using cheap and easily available hardware.
### Details on **function**, **installation** and **configuration** can be found on the **[Wiki Page](https://github.com/jomjol/AI-on-the-edge-device/wiki)**
All you need is an [ESP32 board with a supported camera](https://github.com/jomjol/AI-on-the-edge-device/wiki/Hardware-Compatibility) and a bit of a practical hand.
<img src="images/esp32-cam.png" width="200">
## Key features
- **Small** and **cheap** device (3x4.5x2 cm³, < 10 EUR)
- camera and illumination integrated
- Web surface for administration and control
- OTA-Interface to update directly through the web interface
- API for easy integration
- Inline Image processing (feature detection, alignment, ROI extraction)
- Tensorflow Lite (TFlite) integration - including easy to use wrapper
## Workflow
The device takes a photo of your meter at a defined interval. It then extracts the Regions of Interest (ROI's) out of it and runs them through an artificial inteligence. As a result, you get the digitalized value of your meter.
There are several options what to do with that value. Either send it to a MQTT broker, write it to an InfluxDb or simply provide it throug a REST API.
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/idea.jpg" width="600">
## Impressions
### AI-on-the-edge-device on a Water Meter
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter_all.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/main.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/size.png" width="200">
### Web Interface (Water Meter)
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter.jpg" width="600">
### AI-on-the-edge-device on a Electrical Power Meter
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/powermeter.jpg" width="600">
## Setup
There is a growing [wiki](https://github.com/jomjol/AI-on-the-edge-device/wiki) which provides you with a lot of information.
Head there to get a start, set it up and configure it.
There are also a articles in the German Heise magazine "make:" about the setup and the technical background (!not for free!)
* [DIY - Setup](https://www.heise.de/select/make/2021/2/2103513300897420296)
* Background [Neural Networks](https://www.heise.de/select/make/2021/6/2126410443385102621), [Training Neural Networks](https://www.heise.de/select/make/2022/1/2134114065999161585) and [Programming on the ESP32](https://www.heise.de/select/make/2022/2/2204010051597422030)
## Download
The latest available version is available on the [Releases page](https://github.com/jomjol/AI-on-the-edge-device/releases).
Initially you will have to flash it through an USB connection. Later an update is possible directly over the Air (OTA).
### Casing
A 3d-printable housing can be found here:
- https://www.thingiverse.com/thing:4573481 (Water Meter)
- https://www.thingiverse.com/thing:5028229 (Power Meter)
- https://www.thingiverse.com/thing:4571627 (ESP32-Cam housing only)
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter_all.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/main.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/size.png" width="200">
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter.jpg" width="600">
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/powermeter.jpg" width="600">
## Build it yourself
See [Build Instructions](code/README.md).
## Donate
------
If you would like to support the developer with a cup of coffee you can do that via [Paypal](https://www.paypal.com/donate?hosted_button_id=8TRSVYNYKDSWL).
<form action="https://www.paypal.com/donate" method="post" target="_top">
@@ -33,14 +72,81 @@ If you have any technical topics, you can file a issue in this repository.
In other cases you can contact the developer via email: <img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/mail.jpg" height="25">
------
## Change log
## Changelog
**General remark:** Besides the file `firmware.bin`, typically the content of `/html` will need to be updated!
------
##### 11.0.1 - Intermediate Digits
##### Rolling (2022-09-27)
- Internal improvements
- Bug fix: html update page
##### Rolling (2022-09-24)
- Updated menue
- Update tflite, esp32-cam-master, esp-nn (as of today 20220924)
##### Rolling (2022-09-21)
- Spelling corrections (**[cristianmitran](https://github.com/cristianmitran)**)
##### Rolling (2022-09-21)
- New update mechanism:
- handling of all files (zip, tfl, tflite, bin) within in one updload
- status (upload, processing, ...) displayed
- auto suggestion for reboot (or not in case of web ui update only)
##### Rolling (2022-09-19)
- Integrated version info better into the html (main page, logfile)
- Updated autobuild
- Remove `/firmware` from GitHub.
- If you want to get the latest firmware and html files, please download from the automated build action.
##### Rolling (2022-09-18)
- Updated PostProcessing with respect to `CheckDigitIncreaseConsistency`
##### 11.3.1 - Intermediate Digits (2022-09-17)
- **ATTENTION**:
- first update the 'firmware.bin' and ensure that the new version is running
- Only afterwards update the 'html.zip'
- Otherwise the downwards compatibility of the new counter clockwise feature is not given and you end in a reboot loop, that needs manual flashing!
- **NEW v11.3.1**: corrected corrupted asset `firmware.bin`
- Increased precision (more than 6-7 digits)
- Implements Counter Clockwise Analog Pointers
- Improved post processing algorithm
- Debugging: intensive use of testcases
- MQTT: improved handling, extended logging, automated reconnect
- HTML: Backup Option for Configuration
- HTML: Improved Reboot
- HTML: Update WebUI (Reboot, Infos, CPU Temp, RSSI)
- This version is largely also based on the work of **[caco3](https://github.com/caco3)**, **[adellafave](https://github.com/adellafave)**, **[haverland](https://github.com/haverland)**, **[stefanbode](https://github.com/stefanbode)**, **[PLCHome](https://github.com/PLCHome)**
##### 11.2.0 - Intermediate Digits (2022-08-28)
- Updated Tensorflow / TFlite to newest tflite (version as of 2022-07-27)
- Updated analog neural network file (`ana-cont_11.3.0_s2.tflite` - default, `ana-class100_0120_s1_q.tflite`)
- Updated digital neural network file (`dig-cont_0570_s3.tflite` - default, `dig-class100_0120_s2_q.tflite`)
- Added automated filtering of tflite-file in the graphical configuration (thanks to @**[caco3](https://github.com/caco3)**)
- Updated consistency algorithm & test cases
- HTML: added favicon and system name, Improved reboot dialog (thanks to @**[caco3](https://github.com/caco3)**)
##### 11.1.1 - Intermediate Digits (2022-08-22)
- New and improved consistency check (especially with analog and digital counters mixed)
- Bug Fix: digital counter algorithm
##### 11.0.1 - Intermediate Digits (2022-08-18)
- **NEW v11.0.1**: Bug Fix InfluxDB configuration (only update of html.zip necessary)
@@ -58,21 +164,14 @@ In other cases you can contact the developer via email: <img src="https://raw.gi
- Bug Fix: InfluxDB enabling in grahic configuration
## Tools
* Logfile downloader and combiner (Thx to [reserve85](https://github.com/reserve85))
* Files see ['/tools/logfile-tool'](tbd), How-to see [wiki](https://github.com/jomjol/AI-on-the-edge-device/wiki/Gasmeter-Log-Downloader)
## Additional Ideas
There are some ideas and feature requests which are not followed currently - mainly due to capacity reasons on side of the developer. They are collected here: [FeatureRequest.md](FeatureRequest.md)
------
## History

2
code/.gitignore vendored
View File

@@ -3,3 +3,5 @@
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
version.cpp
sdkconfig.esp32cam

View File

@@ -1,3 +0,0 @@
copy "..\..\code\build\esp32cam-server-only.bin" "..\..\firmware\firmware.bin"
copy "..\..\code\build\bootloader\bootloader.bin" "..\..\firmware\bootloader.bin"
copy "..\..\code\build\partition_table\partition-table.bin" "..\..\firmware\partitions.bin"

View File

@@ -1 +1 @@
powershell Compress-Archive "..\..\sd-card\html\*.*" "..\..\firmware\html.zip"
powershell Compress-Archive -Path "..\..\sd-card\html\*.*" -DestinationPath "..\..\firmware\html.zip"

View File

@@ -1,8 +1,6 @@
cmake_minimum_required(VERSION 3.13.4)
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
set(PROJECT_VER "0.0.9.3")
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/tflite-micro-esp-examples/components/tflite-lib)
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp

64
code/README.md Normal file
View File

@@ -0,0 +1,64 @@
# Build
## Preparations
```
git clone https://github.com/jomjol/AI-on-the-edge-device.git
cd AI-on-the-edge-device
git checkout rolling
git submodule update --init
```
## Build and Flash within terminal
See further down to build it within an IDE.
### Compile
```
cd code
platformio run --environment esp32cam
```
### Upload
```
pio run --target upload
```
If it doesnt find the device:
1. make sure it is in bootloader mode
1. set the UART device correctly: In `platformio.ini`, set `upload_port` correctly, eg. `upload_port = /dev/ttyUSB0`
### Monitor UART Log
```
pio device monitor
```
## Build and Flash with Visual Code IDE
- Download and install VS Code
- https://code.visualstudio.com/Download
- Install the VS Code platform io plugin
- <img src="https://raw.githubusercontent.com/jomjol/ai-on-the-edge-device/master/images/platformio_plugin.jpg" width="200" align="middle">
- Check for error messages, maybe you need to manually add some python libraries
- e.g. in my Ubuntu a python3-env was missing: `sudo apt-get install python3-venv`
- git clone this project
- in Linux:
```
git clone https://github.com/jomjol/AI-on-the-edge-device.git
cd AI-on-the-edge-device
git checkout rolling
git submodule update --init
```
- in VS code, open the `AI-on-the-edge-device/code`
- from terminal: `cd AI-on-the-edge-device/code && code .`
- open a pio terminal (click on the terminal sign in the bottom menu bar)
- make sure you are in the `code` directory
- To build, type `platformio run --environment esp32cam`
- or use the graphical interface:
<img src="https://raw.githubusercontent.com/jomjol/ai-on-the-edge-device/master/images/platformio_build.jpg" width="200" align="middle">
- the build artifacts are stored in `code/.pio/build/esp32cam/`
- Connect the device and type `pio device monitor`. There you will see your device and can copy the name to the next instruction
- Add `upload_port = you_device_port` to the `platformio.ini` file
- make sure an sd card with the contents of the `sd_card` folder is inserted and you have changed the wifi details
- `pio run --target erase` to erase the flash
- `pio run --target upload` this will upload the `bootloader.bin, partitions.bin,firmware.bin` from the `code/.pio/build/esp32cam/` folder.
- `pio device monitor` to observe the logs via uart

View File

@@ -1,57 +0,0 @@
.config
*.o
*.i
*.s
*.orig
*.pyc
# gtags
GTAGS
GRTAGS
GPATH
# emacs
.dir-locals.el
# emacs temp file suffixes
*~
.#*
\#*#
# eclipse setting
.settings
# MacOS directory files
.DS_Store
# Example project files
examples/**/sdkconfig
examples/**/sdkconfig.old
examples/**/build
# Test app files
test_app/build
test_app/sdkconfig
test_app/sdkconfig.old
# Doc build artifacts
docs/_build/
docs/doxygen-warning-log.txt
docs/sphinx-warning-log.txt
docs/sphinx-warning-log-sanitized.txt
docs/xml/
docs/xml_in/
docs/man/
docs/doxygen_sqlite3.db
TEST_LOGS
# gcov coverage reports
*.gcda
*.gcno
coverage.info
coverage_report/
# VS Code Settings
.vscode/

View File

@@ -1,55 +0,0 @@
stages:
- build
variables:
BATCH_BUILD: "1"
V: "0"
MAKEFLAGS: "-j8 --no-keep-going"
IDF_PATH: "$CI_PROJECT_DIR/esp-idf"
LOG_PATH: "$CI_PROJECT_DIR"
.set_git_config: &set_git_config
# Set git config
- git config user.email "test@espressif.com"
- git config user.name "Espressif"
.add_ssh_key: &add_ssh_key
# Add gitlab ssh key
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo -n $GITLAB_KEY > ~/.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 -

View File

@@ -1,48 +0,0 @@
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_depthwise_conv_ansi.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_s16_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()

View File

@@ -1,29 +0,0 @@
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 ESP32, just the ANSI C versions are selected for now.
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 ESP32, just the ANSI C versions are selected for now.
endchoice
config NN_OPTIMIZATIONS
int
default 0 if NN_ANSI_C
default 1 if NN_OPTIMIZED
endmenu

View File

@@ -1,202 +0,0 @@
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.

View File

@@ -1,54 +0,0 @@
# 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 (ANSI C versions)
## 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 ESP32, ANSI-C versions are selected by default.
* For debugging purposes, you may want to select `ANSI C`
## 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.

View File

@@ -1,46 +0,0 @@
// 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)
#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)
#ifdef ARCH_ESP32_S3
#include "esp_nn_esp32s3.h"
#endif
#ifdef ARCH_ESP32
#include "esp_nn_esp32.h"
#endif
#else
#include "esp_nn_ansi_c.h"
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -1,46 +0,0 @@
// 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_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

View File

@@ -1,283 +0,0 @@
// 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 <stdint.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 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);
/**
* @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 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 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 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);
int esp_nn_get_conv_scratch_size_ansi(const uint16_t input_wd,
const uint16_t input_ht,
const uint16_t in_ch,
const uint16_t out_ch,
const uint16_t filter_wd,
const uint16_t filter_ht);
void esp_nn_set_conv_scratch_buf_ansi(const void *buf);
int esp_nn_get_depthwise_conv_scratch_size_ansi(const uint16_t input_wd,
const uint16_t input_ht,
const uint16_t channels,
const uint16_t ch_mult,
const uint16_t filter_wd,
const uint16_t filter_ht);
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);
/* ANSI C function to be hooked up when optimised version needed */
void esp_nn_set_softmax_scratch_buf_opt(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);
/**
* @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);

View File

@@ -1,48 +0,0 @@
// 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 platform.
* We are hooking up just the C versions for now.
* The file hence is exactly same as `esp_nn_ansi_c.h`
*/
#pragma once
#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_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

View File

@@ -1,261 +0,0 @@
// 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 <stdint.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 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);
/**
* @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 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 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 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);
int esp_nn_get_conv_scratch_size_esp32s3(const uint16_t input_wd,
const uint16_t input_ht,
const uint16_t in_ch,
const uint16_t out_ch,
const uint16_t filter_wd,
const uint16_t filter_ht);
void esp_nn_set_conv_scratch_buf_esp32s3(const void *buf);
int esp_nn_get_depthwise_conv_scratch_size_esp32s3(const uint16_t input_wd,
const uint16_t input_ht,
const uint16_t channels,
const uint16_t ch_mult,
const uint16_t filter_wd,
const uint16_t filter_ht);
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

View File

@@ -1,30 +0,0 @@
// 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 <stdint.h>
#include <stdlib.h>
#include <common_functions.h>
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);
}
}

View File

@@ -1,97 +0,0 @@
// 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 <stdint.h>
#include <common_functions.h>
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;
}
}

View File

@@ -1,42 +0,0 @@
// 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 <stdint.h>
#include <common_functions.h>
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;
}
}

View File

@@ -1,218 +0,0 @@
// 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 <stdint.h>
#include <stdbool.h>
#include <string.h>
/**
* 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)
{
__asm__ volatile("nsau %0, %0" : "+r" (in));
return in;
}
__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);
}
/**
* 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)
{
__asm__ volatile("clamps %0, %0, 7" : "+a"(in));
return in;
}
__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 * 2);
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;
}
}
#if 0
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++;
}
memset(dst, pad_val, pad_wd * channels);
dst += pad_wd * channels;
}
/* pad end `pad_ht` lines at end */
memset(dst, pad_val, (input_wd + pad_wd) * pad_ht * channels);
}
#endif
/**
* @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];
}
}

View File

@@ -1,175 +0,0 @@
// 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 <stdint.h>
#include <common_functions.h>
int esp_nn_get_conv_scratch_size_ansi(const uint16_t input_wd,
const uint16_t input_ht,
const uint16_t in_ch,
const uint16_t out_ch,
const uint16_t filter_wd,
const uint16_t filter_ht)
{
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 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 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 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 - 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;
}
}
}
}

View File

@@ -1,436 +0,0 @@
// 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 <stdint.h>
#include <stdio.h>
#include <common_functions.h>
static int16_t *scratch_buffer = NULL;
extern void esp_nn_conv_s16_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 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_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 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 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 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 - 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_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 uint16_t input_wd,
const uint16_t input_ht,
const uint16_t in_ch,
const uint16_t out_ch,
const uint16_t filter_wd,
const uint16_t filter_ht)
{
int filter_size = filter_wd * filter_ht * in_ch * out_ch;
int input_size = input_wd * input_ht * in_ch;
int transpose_buf_size = 8 * in_ch; /* to store intermediate data */
int align_buf_size = 32; /* extra buffer for alignment */
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 int8_t *input,
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 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)
{
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) {
int scratch_offset = (int) (filter_data16 + filter_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_conv_s16_mult8_1x1_esp32s3(
input, input_wd, input_ht, channels, input_offset, 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 % 4 == 0 && filter_wd == 1 && filter_ht == 1 &&
(input_wd * input_ht) % 16 == 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, 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_channels, out_offset, out_shift,
out_mult, activation_min, activation_max);
}
}

View File

@@ -1,97 +0,0 @@
// 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 <stdint.h>
#include <common_functions.h>
int esp_nn_get_depthwise_conv_scratch_size_ansi(const uint16_t input_wd,
const uint16_t input_ht,
const uint16_t channels,
const uint16_t ch_mult,
const uint16_t filter_wd,
const uint16_t filter_ht)
{
return 0;
}
void esp_nn_set_depthwise_conv_scratch_buf_ansi(const void *buf)
{
}
void esp_nn_depthwise_conv_s8_ansi(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
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;
}
}
}
}
}

View File

@@ -1,483 +0,0 @@
// 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 <stdint.h>
#include <stdio.h>
#include <common_functions.h>
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 uint16_t input_wd,
const uint16_t input_ht,
const uint16_t channels,
const uint16_t ch_mult,
const uint16_t filter_wd,
const uint16_t filter_ht)
{
int filter_size = filter_wd * filter_ht * channels * ch_mult;
int padding_used = ((filter_wd == 3) && (filter_ht == 3)) * 2;
int input_size = (input_wd + padding_used) * (input_ht + padding_used) * channels;
return 2 * (filter_size + input_size) + 16; //16 for alignment
}
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 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 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 ((pad_wd == 0) && (pad_ht == 0) &&
// because this does not handle padding offset cases yet, run just for stride (1, 1).
// end padding of input with `-input_offset` should solve this
(stride_wd == 1) && (stride_ht == 1)) {
/* process in 8 bits */
int8_t *filter_aligned = (int8_t *) scratch_buffer;
memcpy(filter_aligned, filter_data, filter_size);
esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3(input_data, input_wd, input_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 { /* (channels % 8) == 0 && pad_wd == 1 && pad_ht == 1 */
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);
}
}

View File

@@ -1,50 +0,0 @@
// 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 <stdint.h>
#include <common_functions.h>
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;
}
}

View File

@@ -1,72 +0,0 @@
// 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 <stdint.h>
#include <common_functions.h>
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;
}
}
}
}

View File

@@ -1,66 +0,0 @@
// 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 <stdint.h>
#include <common_functions.h>
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;
}
}
}
}

View File

@@ -1,88 +0,0 @@
// 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;
}
}

View File

@@ -1,108 +0,0 @@
// 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 <stdio.h>
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;
}
}

View File

@@ -1,104 +0,0 @@
// 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 <stdint.h>
#include <common_functions.h>
#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);
}

View File

@@ -1,9 +0,0 @@
# 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)

View File

@@ -1,7 +0,0 @@
set(COMPONENT_SRCS "main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
set(COMPONENT_PRIV_REQUIRES tests)
register_component()

View File

@@ -1,8 +0,0 @@
#
# 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.
#

View File

@@ -1,87 +0,0 @@
// 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 <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <test_functions.h>
#include <esp_timer.h>
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");
}

View File

@@ -1,5 +0,0 @@
#
# esp-nn
#
CONFIG_NN_ESP32=y

View File

@@ -1,15 +0,0 @@
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)

View File

@@ -1,4 +0,0 @@
# Tests for esp_nn library
- Include these in your test framework and run the framework.
- For IDF test please refer `test_app`

View File

@@ -1,5 +0,0 @@
#FIXME
COMPONENT_ADD_INCLUDEDIRS := include/
COMPONENT_SRCDIRS := src/

View File

@@ -1,48 +0,0 @@
// 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();

View File

@@ -1,87 +0,0 @@
// 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 <stdint.h>
#include <stdbool.h>
#include <common_functions.h>
#include <stdio.h>
/* 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"); \
})

View File

@@ -1,343 +0,0 @@
// 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 <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <common_functions.h>
#include <esp_nn.h>
#include "test_utils.h"
#if CONFIG_IDF_CMAKE
#define IDF_HEAP_CAPS 1
#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
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_data_c) {
free(out_c_orig);
}
if (out_data_opt) {
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
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_data_c) {
free(out_c_orig);
}
if (out_data_opt) {
free(out_opt_orig);
}
}
}

View File

@@ -1,571 +0,0 @@
// 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 <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <esp_nn.h>
#include "test_utils.h"
#if CONFIG_IDF_CMAKE
#define IDF_HEAP_CAPS 1
#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 10 iterations
for (int itr = 0; itr < 10; 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;
default:
input_wd = 4;
input_ht = 4;
filter_ht = 3;
filter_wd = 3;
ch_mult = 4;
channels = 4;
pad_wd = 1;
pad_ht = 1;
stride_wd = 1;
stride_ht = 1;
break;
}
uint16_t out_wd = (input_wd - filter_wd + 1) / stride_wd;
uint16_t out_ht = (input_ht - filter_ht + 1) / stride_ht;
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;
}
int scratch_buf_size = esp_nn_get_depthwise_conv_scratch_size(input_wd, input_ht,
channels, ch_mult,
filter_wd, filter_ht);
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, input_wd, input_ht, channels, input_offset,
pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
filter_data + 4, filter_wd, filter_ht,
bias + 1, out_data_c, out_wd, out_ht, out_offset, out_shift,
out_mult, activation_min, activation_max);
if (itr == 0) {
profile_c_end();
profile_opt_start();
}
/* Optimized function */
esp_nn_depthwise_conv_s8(input, input_wd, input_ht, channels, input_offset,
pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
filter_data + 4, filter_wd, filter_ht,
bias + 1, out_data_opt, out_wd, out_ht, out_offset, out_shift,
out_mult, activation_min, activation_max);
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;
}
int scratch_buf_size = esp_nn_get_conv_scratch_size(in_wd, in_ht, in_channels,
out_channels, filter_wd, filter_ht);
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, in_wd, in_ht, in_channels, input_offset,
pad_wd, pad_ht, stride_wd, stride_ht,
filter_data + 2, filter_wd, filter_ht, bias,
out_data_c, out_wd, out_ht, 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_conv_s8(input, in_wd, in_ht, in_channels, input_offset,
pad_wd, pad_ht, stride_wd, stride_ht,
filter_data + 2, filter_wd, filter_ht, bias,
out_data_opt, out_wd, out_ht, out_channels, out_offset, out_shift,
out_mult, activation_min, activation_max);
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);
}
}
}

View File

@@ -1,111 +0,0 @@
// 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 <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <esp_nn.h>
#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);
}
}

View File

@@ -1,184 +0,0 @@
// 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 <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <esp_nn.h>
#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);
}
}

View File

@@ -1,83 +0,0 @@
// 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 <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <esp_nn.h>
#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);
}
}

View File

@@ -1,101 +0,0 @@
// 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 <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <esp_nn.h>
#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);
}
}

Binary file not shown.

Binary file not shown.

View File

@@ -1,79 +0,0 @@
name: Build examples
on:
push:
branches:
- master
pull_request:
jobs:
build-master:
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@latest
with:
path: 'examples'
build-release-v4_0:
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-v4.0
with:
path: 'examples'
build-release-v4_1:
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-v4.1
with:
path: 'examples'
build-release-v4_2:
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-v4.2
with:
path: 'examples'
build-release-v4_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-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'

View File

@@ -1,27 +0,0 @@
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
#
# You can adjust the behavior by modifying this file.
# For more information, see:
# https://github.com/actions/stale
name: Mark stale issues and pull requests
on:
schedule:
- cron: '20 9 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue appears to be stale. Please close it if its no longer valid.'
stale-pr-message: 'This pull request appears to be stale. Please close it if its no longer valid.'
stale-issue-label: 'no-issue-activity'
stale-pr-label: 'no-pr-activity'

View File

@@ -1,21 +0,0 @@
name: Push component to https://components.espressif.com
on:
push:
tags:
- v*
jobs:
upload_components:
runs-on: ubuntu-latest
steps:
- 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 }}
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}

View File

@@ -1,5 +0,0 @@
*.DS_Store
.vscode
**/build
**/sdkconfig
**/sdkconfig.old

View File

@@ -1,65 +0,0 @@
if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET STREQUAL "esp32s3")
set(COMPONENT_SRCS
driver/esp_camera.c
driver/cam_hal.c
driver/sccb.c
driver/sensor.c
sensors/ov2640.c
sensors/ov3660.c
sensors/ov5640.c
sensors/ov7725.c
sensors/ov7670.c
sensors/nt99141.c
sensors/gc0308.c
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
)
set(COMPONENT_ADD_INCLUDEDIRS
driver/include
conversions/include
)
set(COMPONENT_PRIV_INCLUDEDIRS
driver/private_include
sensors/private_include
conversions/private_include
target/private_include
)
if(IDF_TARGET STREQUAL "esp32")
list(APPEND COMPONENT_SRCS
target/xclk.c
target/esp32/ll_cam.c
)
endif()
if(IDF_TARGET STREQUAL "esp32s2")
list(APPEND COMPONENT_SRCS
target/xclk.c
target/esp32s2/ll_cam.c
target/esp32s2/tjpgd.c
)
list(APPEND COMPONENT_PRIV_INCLUDEDIRS
target/esp32s2/private_include
)
endif()
if(IDF_TARGET STREQUAL "esp32s3")
list(APPEND COMPONENT_SRCS
target/esp32s3/ll_cam.c
)
endif()
set(COMPONENT_REQUIRES driver)
set(COMPONENT_PRIV_REQUIRES freertos nvs_flash)
register_component()
endif()

View File

@@ -1,129 +0,0 @@
menu "Camera configuration"
config OV7670_SUPPORT
bool "Support OV7670 VGA"
default y
help
Enable this option if you want to use the OV7670.
Disable this option to save memory.
config OV7725_SUPPORT
bool "Support OV7725 VGA"
default y
help
Enable this option if you want to use the OV7725.
Disable this option to save memory.
config NT99141_SUPPORT
bool "Support NT99141 HD"
default y
help
Enable this option if you want to use the NT99141.
Disable this option to save memory.
config OV2640_SUPPORT
bool "Support OV2640 2MP"
default y
help
Enable this option if you want to use the OV2640.
Disable this option to save memory.
config OV3660_SUPPORT
bool "Support OV3660 3MP"
default y
help
Enable this option if you want to use the OV3360.
Disable this option to save memory.
config OV5640_SUPPORT
bool "Support OV5640 5MP"
default y
help
Enable this option if you want to use the OV5640.
Disable this option to save memory.
config GC2145_SUPPORT
bool "Support GC2145 2MP"
default y
help
Enable this option if you want to use the GC2145.
Disable this option to save memory.
config GC032A_SUPPORT
bool "Support GC032A VGA"
default y
help
Enable this option if you want to use the GC032A.
Disable this option to save memory.
config GC0308_SUPPORT
bool "Support GC0308 VGA"
default y
help
Enable this option if you want to use the GC0308.
Disable this option to save memory.
config BF3005_SUPPORT
bool "Support BF3005(BYD3005) VGA"
default y
help
Enable this option if you want to use the BF3005.
Disable this option to save memory.
choice SCCB_HARDWARE_I2C_PORT
bool "I2C peripheral to use for SCCB"
default SCCB_HARDWARE_I2C_PORT1
config SCCB_HARDWARE_I2C_PORT0
bool "I2C0"
config SCCB_HARDWARE_I2C_PORT1
bool "I2C1"
endchoice
config SCCB_CLK_FREQ
int "SCCB clk frequency"
default 100000
range 100000 400000
help
Increasing this value can reduce the initialization time of the sensor.
Please refer to the relevant instructions of the sensor to adjust the value.
choice GC_SENSOR_WINDOW_MODE
bool "GalaxyCore Sensor Window Mode"
depends on (GC2145_SUPPORT || GC032A_SUPPORT || GC0308_SUPPORT)
default GC_SENSOR_SUBSAMPLE_MODE
help
This option determines how to reduce the output size when the resolution you set is less than the maximum resolution.
SUBSAMPLE_MODE has a bigger perspective and WINDOWING_MODE has a higher frame rate.
config GC_SENSOR_WINDOWING_MODE
bool "Windowing Mode"
config GC_SENSOR_SUBSAMPLE_MODE
bool "Subsample Mode"
endchoice
choice CAMERA_TASK_PINNED_TO_CORE
bool "Camera task pinned to core"
default CAMERA_CORE0
help
Pin the camera handle task to a certain core(0/1). It can also be done automatically choosing NO_AFFINITY.
config CAMERA_CORE0
bool "CORE0"
config CAMERA_CORE1
bool "CORE1"
config CAMERA_NO_AFFINITY
bool "NO_AFFINITY"
endchoice
config CAMERA_DMA_BUFFER_SIZE_MAX
int "DMA buffer size"
range 8192 32768
default 32768
help
Maximum value of DMA buffer
Larger values may fail to allocate due to insufficient contiguous memory blocks, and smaller value may cause DMA interrupt to be too frequent
endmenu

View File

@@ -1,202 +0,0 @@
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.

View File

@@ -1,369 +0,0 @@
# ESP32 Camera Driver
[![Build examples](https://github.com/espressif/esp32-camera/actions/workflows/build.yml/badge.svg)](https://github.com/espressif/esp32-camera/actions/workflows/build.yml)
## General Information
This repository hosts ESP32 series Soc compatible driver for image sensors. Additionally it provides a few tools, which allow converting the captured frame data to the more common BMP and JPEG formats.
### Supported Soc
- ESP32
- ESP32-S2
- ESP32-S3
### Supported Sensor
| model | max resolution | color type | output format | Len Size |
| ------- | -------------- | ---------- | ------------------------------------------------------------ | -------- |
| OV2640 | 1600 x 1200 | color | YUV(422/420)/YCbCr422<br>RGB565/555<br>8-bit compressed data<br>8/10-bit Raw RGB data | 1/4" |
| OV3660 | 2048 x 1536 | color | raw RGB data<br/>RGB565/555/444<br/>CCIR656<br/>YCbCr422<br/>compression | 1/5" |
| OV5640 | 2592 x 1944 | color | RAW RGB<br/>RGB565/555/444<br/>CCIR656<br/>YUV422/420<br/>YCbCr422<br/>compression | 1/4" |
| OV7670 | 640 x 480 | color | Raw Bayer RGB<br/>Processed Bayer RGB<br>YUV/YCbCr422<br>GRB422<br>RGB565/555 | 1/6" |
| OV7725 | 640 x 480 | color | Raw RGB<br/>GRB 422<br/>RGB565/555/444<br/>YCbCr 422 | 1/4" |
| NT99141 | 1280 x 720 | color | YCbCr 422<br/>RGB565/555/444<br/>Raw<br/>CCIR656<br/>JPEG compression | 1/4" |
| GC032A | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/10" |
| GC0308 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/6.5" |
| GC2145 | 1600 x 1200 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/5" |
| BF3005 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/4" |
## Important to Remember
- Except when using CIF or lower resolution with JPEG, the driver requires PSRAM to be installed and activated.
- Using YUV or RGB puts a lot of strain on the chip because writing to PSRAM is not particularly fast. The result is that image data might be missing. This is particularly true if WiFi is enabled. If you need RGB data, it is recommended that JPEG is captured and then turned into RGB using `fmt2rgb888` or `fmt2bmp`/`frame2bmp`.
- When 1 frame buffer is used, the driver will wait for the current frame to finish (VSYNC) and start I2S DMA. After the frame is acquired, I2S will be stopped and the frame buffer returned to the application. This approach gives more control over the system, but results in longer time to get the frame.
- When 2 or more frame bufers are used, I2S is running in continuous mode and each frame is pushed to a queue that the application can access. This approach puts more strain on the CPU/Memory, but allows for double the frame rate. Please use only with JPEG.
## Installation Instructions
### Using esp-idf
- Clone or download and extract the repository to the components folder of your ESP-IDF project
- Enable PSRAM in `menuconfig` (also set Flash and PSRAM frequiencies to 80MHz)
- Include `esp_camera.h` in your code
### Using PlatformIO
The easy way -- on the `env` section of `platformio.ini`, add the following:
```ini
[env]
lib_deps =
esp32-camera
```
Now the `esp_camera.h` is available to be included:
```c
#include "esp_camera.h"
```
Enable PSRAM on `menuconfig` or type it direclty on `sdkconfig`. Check the [official doc](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html#config-esp32-spiram-support) for more info.
```
CONFIG_ESP32_SPIRAM_SUPPORT=y
```
***Arduino*** The easy-way (content above) only seems to work if you're using `framework=arduino` which seems to take a bunch of the guesswork out (thanks Arduino!) but also suck up a lot more memory and flash, almost crippling the performance. If you plan to use the `framework=espidf` then read the sections below carefully!!
## Platform.io lib/submodule (for framework=espidf)
It's probably easier to just skip the platform.io library registry version and link the git repo as a submodule. (i.e. using code outside the platform.io library management). In this example we will install this as a submodule inside the platform.io $project/lib folder:
```
cd $project\lib
git submodule add -b master https://github.com/espressif/esp32-camera.git
```
Then in `platformio.ini` file
```
build_flags =
-I../lib/esp32-camera
```
After that `#include "esp_camera.h"` statement will be available. Now the module is included, and you're hopefully back to the same place as the easy-Arduino way.
**Warning about platform.io/espidf and fresh (not initialized) git repos**
There is a sharp-edge on you'll discover in the platform.io build process (in espidf v3.3 & 4.0.1) where a project which has only had `git init` but nothing committed will crash platform.io build process with highly non-useful output. The cause is due to lack of a version (making you think you did something wrong, when you didn't at all) - the output is horribly non-descript. Solution: the devs want you to create a file called version.txt with a number in it, or simply commit any file to the projects git repo and use git. This happens because platform.io build process tries to be too clever and determine the build version number from the git repo - it's a sharp edge you'll only encounter if you're experimenting on a new project with no commits .. like wtf is my camera not working let's try a 'clean project'?! </rant>
## Platform.io Kconfig
Kconfig is used by the platform.io menuconfig (accessed by running: `pio run -t menuconfig`) to interactively manage the various #ifdef statements throughout the espidf and supporting libraries (i.e. this repo: esp32-camera and arduino-esp32.git). The menuconfig process generates the `sdkconfig` file which is ultimately used behind the scenes by espidf compile+build process.
**Make sure to append or symlink** [this `Kconfig`](./Kconfig) content into the `Kconfig` of your project.
You symlink (or copy) the included Kconfig into your platform.io projects src directory. The file should be named `Kconfig.projbuild` in your projects src\ directory or you could also add the library path to a CMakefile.txt and hope the `Kconfig` (or `Kconfig.projbuild`) gets discovered by the menuconfig process, though this unpredictable for me.
The unpredictable wonky behavior in platform.io build process around Kconfig naming (Kconfig vs. Kconfig.projbuild) occurs between espidf versions 3.3 and 4.0 - but if you don't see "Camera configuration" in your `pio run -t menuconfig` then there is no point trying to test camera code (it may compile, but it probably won't work!) and it seems the platform.io devs (when they built their wrapper around the espidf menuconfig) didn't implement it properly. You've probably already figured out you can't use the espidf build tools since the files are in totally different locations and also different versions with sometimes different syntax. This is one of those times you might consider changing the `platformio.ini` from `platform=espressif32` to `platform=https://github.com/platformio/platform-espressif32.git#develop` to get a more recent version of the espidf 4.0 tools.
However with a bit of patience and experimenting you'll figure the Kconfig out. Once Kconfig (or Kconfig.projbuild) is working then you will be able to choose the configurations according to your setup or the camera libraries will be compiled. Although you might also need to delete your .pio/build directory before the options appear .. again, the `pio run -t menuconfig` doens't always notice the new Kconfig files!
If you miss-skip-ignore this critical step the camera module will compile but camera logic inside the library will be 'empty' because the Kconfig sets the proper #ifdef statements during the build process to initialize the selected cameras. It's very not optional!
## Examples
### Initialization
```c
#include "esp_camera.h"
//WROVER-KIT PIN Map
#define CAM_PIN_PWDN -1 //power down is not used
#define CAM_PIN_RESET -1 //software reset will be performed
#define CAM_PIN_XCLK 21
#define CAM_PIN_SIOD 26
#define CAM_PIN_SIOC 27
#define CAM_PIN_D7 35
#define CAM_PIN_D6 34
#define CAM_PIN_D5 39
#define CAM_PIN_D4 36
#define CAM_PIN_D3 19
#define CAM_PIN_D2 18
#define CAM_PIN_D1 5
#define CAM_PIN_D0 4
#define CAM_PIN_VSYNC 25
#define CAM_PIN_HREF 23
#define CAM_PIN_PCLK 22
static camera_config_t camera_config = {
.pin_pwdn = CAM_PIN_PWDN,
.pin_reset = CAM_PIN_RESET,
.pin_xclk = CAM_PIN_XCLK,
.pin_sscb_sda = CAM_PIN_SIOD,
.pin_sscb_scl = CAM_PIN_SIOC,
.pin_d7 = CAM_PIN_D7,
.pin_d6 = CAM_PIN_D6,
.pin_d5 = CAM_PIN_D5,
.pin_d4 = CAM_PIN_D4,
.pin_d3 = CAM_PIN_D3,
.pin_d2 = CAM_PIN_D2,
.pin_d1 = CAM_PIN_D1,
.pin_d0 = CAM_PIN_D0,
.pin_vsync = CAM_PIN_VSYNC,
.pin_href = CAM_PIN_HREF,
.pin_pclk = CAM_PIN_PCLK,
.xclk_freq_hz = 20000000,//EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_JPEG,//YUV422,GRAYSCALE,RGB565,JPEG
.frame_size = FRAMESIZE_UXGA,//QQVGA-QXGA Do not use sizes above QVGA when not JPEG
.jpeg_quality = 12, //0-63 lower number means higher quality
.fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
.grab_mode = CAMERA_GRAB_WHEN_EMPTY//CAMERA_GRAB_LATEST. Sets when buffers should be filled
};
esp_err_t camera_init(){
//power up the camera if PWDN pin is defined
if(CAM_PIN_PWDN != -1){
pinMode(CAM_PIN_PWDN, OUTPUT);
digitalWrite(CAM_PIN_PWDN, LOW);
}
//initialize the camera
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Camera Init Failed");
return err;
}
return ESP_OK;
}
esp_err_t camera_capture(){
//acquire a frame
camera_fb_t * fb = esp_camera_fb_get();
if (!fb) {
ESP_LOGE(TAG, "Camera Capture Failed");
return ESP_FAIL;
}
//replace this with your own function
process_image(fb->width, fb->height, fb->format, fb->buf, fb->len);
//return the frame buffer back to the driver for reuse
esp_camera_fb_return(fb);
return ESP_OK;
}
```
### JPEG HTTP Capture
```c
#include "esp_camera.h"
#include "esp_http_server.h"
#include "esp_timer.h"
typedef struct {
httpd_req_t *req;
size_t len;
} jpg_chunking_t;
static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){
jpg_chunking_t *j = (jpg_chunking_t *)arg;
if(!index){
j->len = 0;
}
if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){
return 0;
}
j->len += len;
return len;
}
esp_err_t jpg_httpd_handler(httpd_req_t *req){
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
size_t fb_len = 0;
int64_t fr_start = esp_timer_get_time();
fb = esp_camera_fb_get();
if (!fb) {
ESP_LOGE(TAG, "Camera capture failed");
httpd_resp_send_500(req);
return ESP_FAIL;
}
res = httpd_resp_set_type(req, "image/jpeg");
if(res == ESP_OK){
res = httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");
}
if(res == ESP_OK){
if(fb->format == PIXFORMAT_JPEG){
fb_len = fb->len;
res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
} else {
jpg_chunking_t jchunk = {req, 0};
res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
httpd_resp_send_chunk(req, NULL, 0);
fb_len = jchunk.len;
}
}
esp_camera_fb_return(fb);
int64_t fr_end = esp_timer_get_time();
ESP_LOGI(TAG, "JPG: %uKB %ums", (uint32_t)(fb_len/1024), (uint32_t)((fr_end - fr_start)/1000));
return res;
}
```
### JPEG HTTP Stream
```c
#include "esp_camera.h"
#include "esp_http_server.h"
#include "esp_timer.h"
#define PART_BOUNDARY "123456789000000000000987654321"
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
esp_err_t jpg_stream_httpd_handler(httpd_req_t *req){
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
size_t _jpg_buf_len;
uint8_t * _jpg_buf;
char * part_buf[64];
static int64_t last_frame = 0;
if(!last_frame) {
last_frame = esp_timer_get_time();
}
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
if(res != ESP_OK){
return res;
}
while(true){
fb = esp_camera_fb_get();
if (!fb) {
ESP_LOGE(TAG, "Camera capture failed");
res = ESP_FAIL;
break;
}
if(fb->format != PIXFORMAT_JPEG){
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
if(!jpeg_converted){
ESP_LOGE(TAG, "JPEG compression failed");
esp_camera_fb_return(fb);
res = ESP_FAIL;
}
} else {
_jpg_buf_len = fb->len;
_jpg_buf = fb->buf;
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
if(res == ESP_OK){
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
}
if(fb->format != PIXFORMAT_JPEG){
free(_jpg_buf);
}
esp_camera_fb_return(fb);
if(res != ESP_OK){
break;
}
int64_t fr_end = esp_timer_get_time();
int64_t frame_time = fr_end - last_frame;
last_frame = fr_end;
frame_time /= 1000;
ESP_LOGI(TAG, "MJPG: %uKB %ums (%.1ffps)",
(uint32_t)(_jpg_buf_len/1024),
(uint32_t)frame_time, 1000.0 / (uint32_t)frame_time);
}
last_frame = 0;
return res;
}
```
### BMP HTTP Capture
```c
#include "esp_camera.h"
#include "esp_http_server.h"
#include "esp_timer.h"
esp_err_t bmp_httpd_handler(httpd_req_t *req){
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
int64_t fr_start = esp_timer_get_time();
fb = esp_camera_fb_get();
if (!fb) {
ESP_LOGE(TAG, "Camera capture failed");
httpd_resp_send_500(req);
return ESP_FAIL;
}
uint8_t * buf = NULL;
size_t buf_len = 0;
bool converted = frame2bmp(fb, &buf, &buf_len);
esp_camera_fb_return(fb);
if(!converted){
ESP_LOGE(TAG, "BMP conversion failed");
httpd_resp_send_500(req);
return ESP_FAIL;
}
res = httpd_resp_set_type(req, "image/x-windows-bmp")
|| httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.bmp")
|| httpd_resp_send(req, (const char *)buf, buf_len);
free(buf);
int64_t fr_end = esp_timer_get_time();
ESP_LOGI(TAG, "BMP: %uKB %ums", (uint32_t)(buf_len/1024), (uint32_t)((fr_end - fr_start)/1000));
return res;
}
```

View File

@@ -1,4 +0,0 @@
COMPONENT_ADD_INCLUDEDIRS := driver/include conversions/include
COMPONENT_PRIV_INCLUDEDIRS := driver/private_include conversions/private_include sensors/private_include target/private_include
COMPONENT_SRCDIRS := driver conversions sensors target target/esp32
CXXFLAGS += -fno-rtti

View File

@@ -1,132 +0,0 @@
// Copyright 2015-2016 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 "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/rom/tjpgd.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "tjpgd.h"
#elif CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/rom/tjpgd.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
#include "rom/tjpgd.h"
#endif
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
#include "esp32-hal-log.h"
#define TAG ""
#else
#include "esp_log.h"
static const char* TAG = "esp_jpg_decode";
#endif
typedef struct {
jpg_scale_t scale;
jpg_reader_cb reader;
jpg_writer_cb writer;
void * arg;
size_t len;
size_t index;
} esp_jpg_decoder_t;
static const char * jd_errors[] = {
"Succeeded",
"Interrupted by output function",
"Device error or wrong termination of input stream",
"Insufficient memory pool for the image",
"Insufficient stream input buffer",
"Parameter error",
"Data format error",
"Right format but not supported",
"Not supported JPEG standard"
};
static uint32_t _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
{
uint16_t x = rect->left;
uint16_t y = rect->top;
uint16_t w = rect->right + 1 - x;
uint16_t h = rect->bottom + 1 - y;
uint8_t *data = (uint8_t *)bitmap;
esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device;
if (jpeg->writer) {
return jpeg->writer(jpeg->arg, x, y, w, h, data);
}
return 0;
}
static uint32_t _jpg_read(JDEC *decoder, uint8_t *buf, uint32_t len)
{
esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device;
if (jpeg->len && len > (jpeg->len - jpeg->index)) {
len = jpeg->len - jpeg->index;
}
if (len) {
len = jpeg->reader(jpeg->arg, jpeg->index, buf, len);
if (!len) {
ESP_LOGE(TAG, "Read Fail at %u/%u", jpeg->index, jpeg->len);
}
jpeg->index += len;
}
return len;
}
esp_err_t esp_jpg_decode(size_t len, jpg_scale_t scale, jpg_reader_cb reader, jpg_writer_cb writer, void * arg)
{
static uint8_t work[3100];
JDEC decoder;
esp_jpg_decoder_t jpeg;
jpeg.len = len;
jpeg.reader = reader;
jpeg.writer = writer;
jpeg.arg = arg;
jpeg.scale = scale;
jpeg.index = 0;
JRESULT jres = jd_prepare(&decoder, _jpg_read, work, 3100, &jpeg);
if(jres != JDR_OK){
ESP_LOGE(TAG, "JPG Header Parse Failed! %s", jd_errors[jres]);
return ESP_FAIL;
}
uint16_t output_width = decoder.width / (1 << (uint8_t)(jpeg.scale));
uint16_t output_height = decoder.height / (1 << (uint8_t)(jpeg.scale));
//output start
writer(arg, 0, 0, output_width, output_height, NULL);
//output write
jres = jd_decomp(&decoder, _jpg_write, (uint8_t)jpeg.scale);
//output end
writer(arg, output_width, output_height, output_width, output_height, NULL);
if (jres != JDR_OK) {
ESP_LOGE(TAG, "JPG Decompression Failed! %s", jd_errors[jres]);
return ESP_FAIL;
}
//check if all data has been consumed.
if (len && jpeg.index < len) {
_jpg_read(&decoder, NULL, len - jpeg.index);
}
return ESP_OK;
}

View File

@@ -1,43 +0,0 @@
// Copyright 2015-2016 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.
#ifndef _ESP_JPG_DECODE_H_
#define _ESP_JPG_DECODE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
typedef enum {
JPG_SCALE_NONE,
JPG_SCALE_2X,
JPG_SCALE_4X,
JPG_SCALE_8X,
JPG_SCALE_MAX = JPG_SCALE_8X
} jpg_scale_t;
typedef size_t (* jpg_reader_cb)(void * arg, size_t index, uint8_t *buf, size_t len);
typedef bool (* jpg_writer_cb)(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data);
esp_err_t esp_jpg_decode(size_t len, jpg_scale_t scale, jpg_reader_cb reader, jpg_writer_cb writer, void * arg);
#ifdef __cplusplus
}
#endif
#endif /* _ESP_JPG_DECODE_H_ */

View File

@@ -1,130 +0,0 @@
// Copyright 2015-2016 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.
#ifndef _IMG_CONVERTERS_H_
#define _IMG_CONVERTERS_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "esp_camera.h"
#include "esp_jpg_decode.h"
typedef size_t (* jpg_out_cb)(void * arg, size_t index, const void* data, size_t len);
/**
* @brief Convert image buffer to JPEG
*
* @param src Source buffer in RGB565, RGB888, YUYV or GRAYSCALE format
* @param src_len Length in bytes of the source buffer
* @param width Width in pixels of the source image
* @param height Height in pixels of the source image
* @param format Format of the source image
* @param quality JPEG quality of the resulting image
* @param cp Callback to be called to write the bytes of the output JPEG
* @param arg Pointer to be passed to the callback
*
* @return true on success
*/
bool fmt2jpg_cb(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, jpg_out_cb cb, void * arg);
/**
* @brief Convert camera frame buffer to JPEG
*
* @param fb Source camera frame buffer
* @param quality JPEG quality of the resulting image
* @param cp Callback to be called to write the bytes of the output JPEG
* @param arg Pointer to be passed to the callback
*
* @return true on success
*/
bool frame2jpg_cb(camera_fb_t * fb, uint8_t quality, jpg_out_cb cb, void * arg);
/**
* @brief Convert image buffer to JPEG buffer
*
* @param src Source buffer in RGB565, RGB888, YUYV or GRAYSCALE format
* @param src_len Length in bytes of the source buffer
* @param width Width in pixels of the source image
* @param height Height in pixels of the source image
* @param format Format of the source image
* @param quality JPEG quality of the resulting image
* @param out Pointer to be populated with the address of the resulting buffer.
* You MUST free the pointer once you are done with it.
* @param out_len Pointer to be populated with the length of the output buffer
*
* @return true on success
*/
bool fmt2jpg(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, uint8_t ** out, size_t * out_len);
/**
* @brief Convert camera frame buffer to JPEG buffer
*
* @param fb Source camera frame buffer
* @param quality JPEG quality of the resulting image
* @param out Pointer to be populated with the address of the resulting buffer
* @param out_len Pointer to be populated with the length of the output buffer
*
* @return true on success
*/
bool frame2jpg(camera_fb_t * fb, uint8_t quality, uint8_t ** out, size_t * out_len);
/**
* @brief Convert image buffer to BMP buffer
*
* @param src Source buffer in JPEG, RGB565, RGB888, YUYV or GRAYSCALE format
* @param src_len Length in bytes of the source buffer
* @param width Width in pixels of the source image
* @param height Height in pixels of the source image
* @param format Format of the source image
* @param out Pointer to be populated with the address of the resulting buffer
* @param out_len Pointer to be populated with the length of the output buffer
*
* @return true on success
*/
bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t ** out, size_t * out_len);
/**
* @brief Convert camera frame buffer to BMP buffer
*
* @param fb Source camera frame buffer
* @param out Pointer to be populated with the address of the resulting buffer
* @param out_len Pointer to be populated with the length of the output buffer
*
* @return true on success
*/
bool frame2bmp(camera_fb_t * fb, uint8_t ** out, size_t * out_len);
/**
* @brief Convert image buffer to RGB888 buffer (used for face detection)
*
* @param src Source buffer in JPEG, RGB565, RGB888, YUYV or GRAYSCALE format
* @param src_len Length in bytes of the source buffer
* @param format Format of the source image
* @param rgb_buf Pointer to the output buffer (width * height * 3)
*
* @return true on success
*/
bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint8_t * rgb_buf);
bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale);
#ifdef __cplusplus
}
#endif
#endif /* _IMG_CONVERTERS_H_ */

View File

@@ -1,723 +0,0 @@
// jpge.cpp - C++ class for JPEG compression.
// Public domain, Rich Geldreich <richgel99@gmail.com>
// v1.01, Dec. 18, 2010 - Initial release
// v1.02, Apr. 6, 2011 - Removed 2x2 ordered dither in H2V1 chroma subsampling method load_block_16_8_8(). (The rounding factor was 2, when it should have been 1. Either way, it wasn't helping.)
// v1.03, Apr. 16, 2011 - Added support for optimized Huffman code tables, optimized dynamic memory allocation down to only 1 alloc.
// Also from Alex Evans: Added RGBA support, linear memory allocator (no longer needed in v1.03).
// v1.04, May. 19, 2012: Forgot to set m_pFile ptr to NULL in cfile_stream::close(). Thanks to Owen Kaluza for reporting this bug.
// Code tweaks to fix VS2008 static code analysis warnings (all looked harmless).
// Code review revealed method load_block_16_8_8() (used for the non-default H2V1 sampling mode to downsample chroma) somehow didn't get the rounding factor fix from v1.02.
#include "jpge.h"
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include "esp_heap_caps.h"
#define JPGE_MAX(a,b) (((a)>(b))?(a):(b))
#define JPGE_MIN(a,b) (((a)<(b))?(a):(b))
namespace jpge {
static inline void *jpge_malloc(size_t nSize) {
void * b = malloc(nSize);
if(b){
return b;
}
return heap_caps_malloc(nSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}
static inline void jpge_free(void *p) { free(p); }
// Various JPEG enums and tables.
enum { M_SOF0 = 0xC0, M_DHT = 0xC4, M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_APP0 = 0xE0 };
enum { DC_LUM_CODES = 12, AC_LUM_CODES = 256, DC_CHROMA_CODES = 12, AC_CHROMA_CODES = 256, MAX_HUFF_SYMBOLS = 257, MAX_HUFF_CODESIZE = 32 };
static const uint8 s_zag[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 };
static const int16 s_std_lum_quant[64] = { 16,11,12,14,12,10,16,14,13,14,18,17,16,19,24,40,26,24,22,22,24,49,35,37,29,40,58,51,61,60,57,51,56,55,64,72,92,78,64,68,87,69,55,56,80,109,81,87,95,98,103,104,103,62,77,113,121,112,100,120,92,101,103,99 };
static const int16 s_std_croma_quant[64] = { 17,18,18,24,21,24,47,26,26,47,99,66,56,66,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 };
static const uint8 s_dc_lum_bits[17] = { 0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0 };
static const uint8 s_dc_lum_val[DC_LUM_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
static const uint8 s_ac_lum_bits[17] = { 0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d };
static const uint8 s_ac_lum_val[AC_LUM_CODES] = {
0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
0xf9,0xfa
};
static const uint8 s_dc_chroma_bits[17] = { 0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0 };
static const uint8 s_dc_chroma_val[DC_CHROMA_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
static const uint8 s_ac_chroma_bits[17] = { 0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77 };
static const uint8 s_ac_chroma_val[AC_CHROMA_CODES] = {
0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
0xf9,0xfa
};
const int YR = 19595, YG = 38470, YB = 7471, CB_R = -11059, CB_G = -21709, CB_B = 32768, CR_R = 32768, CR_G = -27439, CR_B = -5329;
static int32 m_last_quality = 0;
static int32 m_quantization_tables[2][64];
static bool m_huff_initialized = false;
static uint m_huff_codes[4][256];
static uint8 m_huff_code_sizes[4][256];
static uint8 m_huff_bits[4][17];
static uint8 m_huff_val[4][256];
static inline uint8 clamp(int i) {
if (i < 0) {
i = 0;
} else if (i > 255){
i = 255;
}
return static_cast<uint8>(i);
}
static void RGB_to_YCC(uint8* pDst, const uint8 *pSrc, int num_pixels) {
for ( ; num_pixels; pDst += 3, pSrc += 3, num_pixels--) {
const int r = pSrc[0], g = pSrc[1], b = pSrc[2];
pDst[0] = static_cast<uint8>((r * YR + g * YG + b * YB + 32768) >> 16);
pDst[1] = clamp(128 + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16));
pDst[2] = clamp(128 + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16));
}
}
static void RGB_to_Y(uint8* pDst, const uint8 *pSrc, int num_pixels) {
for ( ; num_pixels; pDst++, pSrc += 3, num_pixels--) {
pDst[0] = static_cast<uint8>((pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) >> 16);
}
}
static void Y_to_YCC(uint8* pDst, const uint8* pSrc, int num_pixels) {
for( ; num_pixels; pDst += 3, pSrc++, num_pixels--) {
pDst[0] = pSrc[0];
pDst[1] = 128;
pDst[2] = 128;
}
}
// Forward DCT - DCT derived from jfdctint.
enum { CONST_BITS = 13, ROW_BITS = 2 };
#define DCT_DESCALE(x, n) (((x) + (((int32)1) << ((n) - 1))) >> (n))
#define DCT_MUL(var, c) (static_cast<int16>(var) * static_cast<int32>(c))
#define DCT1D(s0, s1, s2, s3, s4, s5, s6, s7) \
int32 t0 = s0 + s7, t7 = s0 - s7, t1 = s1 + s6, t6 = s1 - s6, t2 = s2 + s5, t5 = s2 - s5, t3 = s3 + s4, t4 = s3 - s4; \
int32 t10 = t0 + t3, t13 = t0 - t3, t11 = t1 + t2, t12 = t1 - t2; \
int32 u1 = DCT_MUL(t12 + t13, 4433); \
s2 = u1 + DCT_MUL(t13, 6270); \
s6 = u1 + DCT_MUL(t12, -15137); \
u1 = t4 + t7; \
int32 u2 = t5 + t6, u3 = t4 + t6, u4 = t5 + t7; \
int32 z5 = DCT_MUL(u3 + u4, 9633); \
t4 = DCT_MUL(t4, 2446); t5 = DCT_MUL(t5, 16819); \
t6 = DCT_MUL(t6, 25172); t7 = DCT_MUL(t7, 12299); \
u1 = DCT_MUL(u1, -7373); u2 = DCT_MUL(u2, -20995); \
u3 = DCT_MUL(u3, -16069); u4 = DCT_MUL(u4, -3196); \
u3 += z5; u4 += z5; \
s0 = t10 + t11; s1 = t7 + u1 + u4; s3 = t6 + u2 + u3; s4 = t10 - t11; s5 = t5 + u2 + u4; s7 = t4 + u1 + u3;
static void DCT2D(int32 *p) {
int32 c, *q = p;
for (c = 7; c >= 0; c--, q += 8) {
int32 s0 = q[0], s1 = q[1], s2 = q[2], s3 = q[3], s4 = q[4], s5 = q[5], s6 = q[6], s7 = q[7];
DCT1D(s0, s1, s2, s3, s4, s5, s6, s7);
q[0] = s0 << ROW_BITS; q[1] = DCT_DESCALE(s1, CONST_BITS-ROW_BITS); q[2] = DCT_DESCALE(s2, CONST_BITS-ROW_BITS); q[3] = DCT_DESCALE(s3, CONST_BITS-ROW_BITS);
q[4] = s4 << ROW_BITS; q[5] = DCT_DESCALE(s5, CONST_BITS-ROW_BITS); q[6] = DCT_DESCALE(s6, CONST_BITS-ROW_BITS); q[7] = DCT_DESCALE(s7, CONST_BITS-ROW_BITS);
}
for (q = p, c = 7; c >= 0; c--, q++) {
int32 s0 = q[0*8], s1 = q[1*8], s2 = q[2*8], s3 = q[3*8], s4 = q[4*8], s5 = q[5*8], s6 = q[6*8], s7 = q[7*8];
DCT1D(s0, s1, s2, s3, s4, s5, s6, s7);
q[0*8] = DCT_DESCALE(s0, ROW_BITS+3); q[1*8] = DCT_DESCALE(s1, CONST_BITS+ROW_BITS+3); q[2*8] = DCT_DESCALE(s2, CONST_BITS+ROW_BITS+3); q[3*8] = DCT_DESCALE(s3, CONST_BITS+ROW_BITS+3);
q[4*8] = DCT_DESCALE(s4, ROW_BITS+3); q[5*8] = DCT_DESCALE(s5, CONST_BITS+ROW_BITS+3); q[6*8] = DCT_DESCALE(s6, CONST_BITS+ROW_BITS+3); q[7*8] = DCT_DESCALE(s7, CONST_BITS+ROW_BITS+3);
}
}
// Compute the actual canonical Huffman codes/code sizes given the JPEG huff bits and val arrays.
static void compute_huffman_table(uint *codes, uint8 *code_sizes, uint8 *bits, uint8 *val)
{
int i, l, last_p, si;
static uint8 huff_size[257];
static uint huff_code[257];
uint code;
int p = 0;
for (l = 1; l <= 16; l++) {
for (i = 1; i <= bits[l]; i++) {
huff_size[p++] = (char)l;
}
}
huff_size[p] = 0;
last_p = p; // write sentinel
code = 0; si = huff_size[0]; p = 0;
while (huff_size[p]) {
while (huff_size[p] == si) {
huff_code[p++] = code++;
}
code <<= 1;
si++;
}
memset(codes, 0, sizeof(codes[0])*256);
memset(code_sizes, 0, sizeof(code_sizes[0])*256);
for (p = 0; p < last_p; p++) {
codes[val[p]] = huff_code[p];
code_sizes[val[p]] = huff_size[p];
}
}
void jpeg_encoder::flush_output_buffer()
{
if (m_out_buf_left != JPGE_OUT_BUF_SIZE) {
m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_buf(m_out_buf, JPGE_OUT_BUF_SIZE - m_out_buf_left);
}
m_pOut_buf = m_out_buf;
m_out_buf_left = JPGE_OUT_BUF_SIZE;
}
void jpeg_encoder::emit_byte(uint8 i)
{
*m_pOut_buf++ = i;
if (--m_out_buf_left == 0) {
flush_output_buffer();
}
}
void jpeg_encoder::put_bits(uint bits, uint len)
{
uint8 c = 0;
m_bit_buffer |= ((uint32)bits << (24 - (m_bits_in += len)));
while (m_bits_in >= 8) {
c = (uint8)((m_bit_buffer >> 16) & 0xFF);
emit_byte(c);
if (c == 0xFF) {
emit_byte(0);
}
m_bit_buffer <<= 8;
m_bits_in -= 8;
}
}
void jpeg_encoder::emit_word(uint i)
{
emit_byte(uint8(i >> 8)); emit_byte(uint8(i & 0xFF));
}
// JPEG marker generation.
void jpeg_encoder::emit_marker(int marker)
{
emit_byte(uint8(0xFF)); emit_byte(uint8(marker));
}
// Emit JFIF marker
void jpeg_encoder::emit_jfif_app0()
{
emit_marker(M_APP0);
emit_word(2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1);
emit_byte(0x4A); emit_byte(0x46); emit_byte(0x49); emit_byte(0x46); /* Identifier: ASCII "JFIF" */
emit_byte(0);
emit_byte(1); /* Major version */
emit_byte(1); /* Minor version */
emit_byte(0); /* Density unit */
emit_word(1);
emit_word(1);
emit_byte(0); /* No thumbnail image */
emit_byte(0);
}
// Emit quantization tables
void jpeg_encoder::emit_dqt()
{
for (int i = 0; i < ((m_num_components == 3) ? 2 : 1); i++)
{
emit_marker(M_DQT);
emit_word(64 + 1 + 2);
emit_byte(static_cast<uint8>(i));
for (int j = 0; j < 64; j++)
emit_byte(static_cast<uint8>(m_quantization_tables[i][j]));
}
}
// Emit start of frame marker
void jpeg_encoder::emit_sof()
{
emit_marker(M_SOF0); /* baseline */
emit_word(3 * m_num_components + 2 + 5 + 1);
emit_byte(8); /* precision */
emit_word(m_image_y);
emit_word(m_image_x);
emit_byte(m_num_components);
for (int i = 0; i < m_num_components; i++)
{
emit_byte(static_cast<uint8>(i + 1)); /* component ID */
emit_byte((m_comp_h_samp[i] << 4) + m_comp_v_samp[i]); /* h and v sampling */
emit_byte(i > 0); /* quant. table num */
}
}
// Emit Huffman table.
void jpeg_encoder::emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag)
{
emit_marker(M_DHT);
int length = 0;
for (int i = 1; i <= 16; i++)
length += bits[i];
emit_word(length + 2 + 1 + 16);
emit_byte(static_cast<uint8>(index + (ac_flag << 4)));
for (int i = 1; i <= 16; i++)
emit_byte(bits[i]);
for (int i = 0; i < length; i++)
emit_byte(val[i]);
}
// Emit all Huffman tables.
void jpeg_encoder::emit_dhts()
{
emit_dht(m_huff_bits[0+0], m_huff_val[0+0], 0, false);
emit_dht(m_huff_bits[2+0], m_huff_val[2+0], 0, true);
if (m_num_components == 3) {
emit_dht(m_huff_bits[0+1], m_huff_val[0+1], 1, false);
emit_dht(m_huff_bits[2+1], m_huff_val[2+1], 1, true);
}
}
// emit start of scan
void jpeg_encoder::emit_sos()
{
emit_marker(M_SOS);
emit_word(2 * m_num_components + 2 + 1 + 3);
emit_byte(m_num_components);
for (int i = 0; i < m_num_components; i++)
{
emit_byte(static_cast<uint8>(i + 1));
if (i == 0)
emit_byte((0 << 4) + 0);
else
emit_byte((1 << 4) + 1);
}
emit_byte(0); /* spectral selection */
emit_byte(63);
emit_byte(0);
}
void jpeg_encoder::load_block_8_8_grey(int x)
{
uint8 *pSrc;
sample_array_t *pDst = m_sample_array;
x <<= 3;
for (int i = 0; i < 8; i++, pDst += 8)
{
pSrc = m_mcu_lines[i] + x;
pDst[0] = pSrc[0] - 128; pDst[1] = pSrc[1] - 128; pDst[2] = pSrc[2] - 128; pDst[3] = pSrc[3] - 128;
pDst[4] = pSrc[4] - 128; pDst[5] = pSrc[5] - 128; pDst[6] = pSrc[6] - 128; pDst[7] = pSrc[7] - 128;
}
}
void jpeg_encoder::load_block_8_8(int x, int y, int c)
{
uint8 *pSrc;
sample_array_t *pDst = m_sample_array;
x = (x * (8 * 3)) + c;
y <<= 3;
for (int i = 0; i < 8; i++, pDst += 8)
{
pSrc = m_mcu_lines[y + i] + x;
pDst[0] = pSrc[0 * 3] - 128; pDst[1] = pSrc[1 * 3] - 128; pDst[2] = pSrc[2 * 3] - 128; pDst[3] = pSrc[3 * 3] - 128;
pDst[4] = pSrc[4 * 3] - 128; pDst[5] = pSrc[5 * 3] - 128; pDst[6] = pSrc[6 * 3] - 128; pDst[7] = pSrc[7 * 3] - 128;
}
}
void jpeg_encoder::load_block_16_8(int x, int c)
{
uint8 *pSrc1, *pSrc2;
sample_array_t *pDst = m_sample_array;
x = (x * (16 * 3)) + c;
int a = 0, b = 2;
for (int i = 0; i < 16; i += 2, pDst += 8)
{
pSrc1 = m_mcu_lines[i + 0] + x;
pSrc2 = m_mcu_lines[i + 1] + x;
pDst[0] = ((pSrc1[ 0 * 3] + pSrc1[ 1 * 3] + pSrc2[ 0 * 3] + pSrc2[ 1 * 3] + a) >> 2) - 128; pDst[1] = ((pSrc1[ 2 * 3] + pSrc1[ 3 * 3] + pSrc2[ 2 * 3] + pSrc2[ 3 * 3] + b) >> 2) - 128;
pDst[2] = ((pSrc1[ 4 * 3] + pSrc1[ 5 * 3] + pSrc2[ 4 * 3] + pSrc2[ 5 * 3] + a) >> 2) - 128; pDst[3] = ((pSrc1[ 6 * 3] + pSrc1[ 7 * 3] + pSrc2[ 6 * 3] + pSrc2[ 7 * 3] + b) >> 2) - 128;
pDst[4] = ((pSrc1[ 8 * 3] + pSrc1[ 9 * 3] + pSrc2[ 8 * 3] + pSrc2[ 9 * 3] + a) >> 2) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3] + pSrc2[10 * 3] + pSrc2[11 * 3] + b) >> 2) - 128;
pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3] + pSrc2[12 * 3] + pSrc2[13 * 3] + a) >> 2) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3] + pSrc2[14 * 3] + pSrc2[15 * 3] + b) >> 2) - 128;
int temp = a; a = b; b = temp;
}
}
void jpeg_encoder::load_block_16_8_8(int x, int c)
{
uint8 *pSrc1;
sample_array_t *pDst = m_sample_array;
x = (x * (16 * 3)) + c;
for (int i = 0; i < 8; i++, pDst += 8)
{
pSrc1 = m_mcu_lines[i + 0] + x;
pDst[0] = ((pSrc1[ 0 * 3] + pSrc1[ 1 * 3]) >> 1) - 128; pDst[1] = ((pSrc1[ 2 * 3] + pSrc1[ 3 * 3]) >> 1) - 128;
pDst[2] = ((pSrc1[ 4 * 3] + pSrc1[ 5 * 3]) >> 1) - 128; pDst[3] = ((pSrc1[ 6 * 3] + pSrc1[ 7 * 3]) >> 1) - 128;
pDst[4] = ((pSrc1[ 8 * 3] + pSrc1[ 9 * 3]) >> 1) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3]) >> 1) - 128;
pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3]) >> 1) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3]) >> 1) - 128;
}
}
void jpeg_encoder::load_quantized_coefficients(int component_num)
{
int32 *q = m_quantization_tables[component_num > 0];
int16 *pDst = m_coefficient_array;
for (int i = 0; i < 64; i++)
{
sample_array_t j = m_sample_array[s_zag[i]];
if (j < 0)
{
if ((j = -j + (*q >> 1)) < *q)
*pDst++ = 0;
else
*pDst++ = static_cast<int16>(-(j / *q));
}
else
{
if ((j = j + (*q >> 1)) < *q)
*pDst++ = 0;
else
*pDst++ = static_cast<int16>((j / *q));
}
q++;
}
}
void jpeg_encoder::code_coefficients_pass_two(int component_num)
{
int i, j, run_len, nbits, temp1, temp2;
int16 *pSrc = m_coefficient_array;
uint *codes[2];
uint8 *code_sizes[2];
if (component_num == 0)
{
codes[0] = m_huff_codes[0 + 0]; codes[1] = m_huff_codes[2 + 0];
code_sizes[0] = m_huff_code_sizes[0 + 0]; code_sizes[1] = m_huff_code_sizes[2 + 0];
}
else
{
codes[0] = m_huff_codes[0 + 1]; codes[1] = m_huff_codes[2 + 1];
code_sizes[0] = m_huff_code_sizes[0 + 1]; code_sizes[1] = m_huff_code_sizes[2 + 1];
}
temp1 = temp2 = pSrc[0] - m_last_dc_val[component_num];
m_last_dc_val[component_num] = pSrc[0];
if (temp1 < 0)
{
temp1 = -temp1; temp2--;
}
nbits = 0;
while (temp1)
{
nbits++; temp1 >>= 1;
}
put_bits(codes[0][nbits], code_sizes[0][nbits]);
if (nbits) put_bits(temp2 & ((1 << nbits) - 1), nbits);
for (run_len = 0, i = 1; i < 64; i++)
{
if ((temp1 = m_coefficient_array[i]) == 0)
run_len++;
else
{
while (run_len >= 16)
{
put_bits(codes[1][0xF0], code_sizes[1][0xF0]);
run_len -= 16;
}
if ((temp2 = temp1) < 0)
{
temp1 = -temp1;
temp2--;
}
nbits = 1;
while (temp1 >>= 1)
nbits++;
j = (run_len << 4) + nbits;
put_bits(codes[1][j], code_sizes[1][j]);
put_bits(temp2 & ((1 << nbits) - 1), nbits);
run_len = 0;
}
}
if (run_len)
put_bits(codes[1][0], code_sizes[1][0]);
}
void jpeg_encoder::code_block(int component_num)
{
DCT2D(m_sample_array);
load_quantized_coefficients(component_num);
code_coefficients_pass_two(component_num);
}
void jpeg_encoder::process_mcu_row()
{
if (m_num_components == 1)
{
for (int i = 0; i < m_mcus_per_row; i++)
{
load_block_8_8_grey(i); code_block(0);
}
}
else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1))
{
for (int i = 0; i < m_mcus_per_row; i++)
{
load_block_8_8(i, 0, 0); code_block(0); load_block_8_8(i, 0, 1); code_block(1); load_block_8_8(i, 0, 2); code_block(2);
}
}
else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1))
{
for (int i = 0; i < m_mcus_per_row; i++)
{
load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0);
load_block_16_8_8(i, 1); code_block(1); load_block_16_8_8(i, 2); code_block(2);
}
}
else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2))
{
for (int i = 0; i < m_mcus_per_row; i++)
{
load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0);
load_block_8_8(i * 2 + 0, 1, 0); code_block(0); load_block_8_8(i * 2 + 1, 1, 0); code_block(0);
load_block_16_8(i, 1); code_block(1); load_block_16_8(i, 2); code_block(2);
}
}
}
void jpeg_encoder::load_mcu(const void *pSrc)
{
const uint8* Psrc = reinterpret_cast<const uint8*>(pSrc);
uint8* pDst = m_mcu_lines[m_mcu_y_ofs]; // OK to write up to m_image_bpl_xlt bytes to pDst
if (m_num_components == 1) {
if (m_image_bpp == 3)
RGB_to_Y(pDst, Psrc, m_image_x);
else
memcpy(pDst, Psrc, m_image_x);
} else {
if (m_image_bpp == 3)
RGB_to_YCC(pDst, Psrc, m_image_x);
else
Y_to_YCC(pDst, Psrc, m_image_x);
}
// Possibly duplicate pixels at end of scanline if not a multiple of 8 or 16
if (m_num_components == 1)
memset(m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt, pDst[m_image_bpl_xlt - 1], m_image_x_mcu - m_image_x);
else
{
const uint8 y = pDst[m_image_bpl_xlt - 3 + 0], cb = pDst[m_image_bpl_xlt - 3 + 1], cr = pDst[m_image_bpl_xlt - 3 + 2];
uint8 *q = m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt;
for (int i = m_image_x; i < m_image_x_mcu; i++)
{
*q++ = y; *q++ = cb; *q++ = cr;
}
}
if (++m_mcu_y_ofs == m_mcu_y)
{
process_mcu_row();
m_mcu_y_ofs = 0;
}
}
// Quantization table generation.
void jpeg_encoder::compute_quant_table(int32 *pDst, const int16 *pSrc)
{
int32 q;
if (m_params.m_quality < 50)
q = 5000 / m_params.m_quality;
else
q = 200 - m_params.m_quality * 2;
for (int i = 0; i < 64; i++)
{
int32 j = *pSrc++; j = (j * q + 50L) / 100L;
*pDst++ = JPGE_MIN(JPGE_MAX(j, 1), 255);
}
}
// Higher-level methods.
bool jpeg_encoder::jpg_open(int p_x_res, int p_y_res, int src_channels)
{
m_num_components = 3;
switch (m_params.m_subsampling)
{
case Y_ONLY:
{
m_num_components = 1;
m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1;
m_mcu_x = 8; m_mcu_y = 8;
break;
}
case H1V1:
{
m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1;
m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
m_mcu_x = 8; m_mcu_y = 8;
break;
}
case H2V1:
{
m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 1;
m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
m_mcu_x = 16; m_mcu_y = 8;
break;
}
case H2V2:
{
m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 2;
m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
m_mcu_x = 16; m_mcu_y = 16;
}
}
m_image_x = p_x_res; m_image_y = p_y_res;
m_image_bpp = src_channels;
m_image_bpl = m_image_x * src_channels;
m_image_x_mcu = (m_image_x + m_mcu_x - 1) & (~(m_mcu_x - 1));
m_image_y_mcu = (m_image_y + m_mcu_y - 1) & (~(m_mcu_y - 1));
m_image_bpl_xlt = m_image_x * m_num_components;
m_image_bpl_mcu = m_image_x_mcu * m_num_components;
m_mcus_per_row = m_image_x_mcu / m_mcu_x;
if ((m_mcu_lines[0] = static_cast<uint8*>(jpge_malloc(m_image_bpl_mcu * m_mcu_y))) == NULL) {
return false;
}
for (int i = 1; i < m_mcu_y; i++)
m_mcu_lines[i] = m_mcu_lines[i-1] + m_image_bpl_mcu;
if(m_last_quality != m_params.m_quality){
m_last_quality = m_params.m_quality;
compute_quant_table(m_quantization_tables[0], s_std_lum_quant);
compute_quant_table(m_quantization_tables[1], s_std_croma_quant);
}
if(!m_huff_initialized){
m_huff_initialized = true;
memcpy(m_huff_bits[0+0], s_dc_lum_bits, 17); memcpy(m_huff_val[0+0], s_dc_lum_val, DC_LUM_CODES);
memcpy(m_huff_bits[2+0], s_ac_lum_bits, 17); memcpy(m_huff_val[2+0], s_ac_lum_val, AC_LUM_CODES);
memcpy(m_huff_bits[0+1], s_dc_chroma_bits, 17); memcpy(m_huff_val[0+1], s_dc_chroma_val, DC_CHROMA_CODES);
memcpy(m_huff_bits[2+1], s_ac_chroma_bits, 17); memcpy(m_huff_val[2+1], s_ac_chroma_val, AC_CHROMA_CODES);
compute_huffman_table(&m_huff_codes[0+0][0], &m_huff_code_sizes[0+0][0], m_huff_bits[0+0], m_huff_val[0+0]);
compute_huffman_table(&m_huff_codes[2+0][0], &m_huff_code_sizes[2+0][0], m_huff_bits[2+0], m_huff_val[2+0]);
compute_huffman_table(&m_huff_codes[0+1][0], &m_huff_code_sizes[0+1][0], m_huff_bits[0+1], m_huff_val[0+1]);
compute_huffman_table(&m_huff_codes[2+1][0], &m_huff_code_sizes[2+1][0], m_huff_bits[2+1], m_huff_val[2+1]);
}
m_out_buf_left = JPGE_OUT_BUF_SIZE;
m_pOut_buf = m_out_buf;
m_bit_buffer = 0;
m_bits_in = 0;
m_mcu_y_ofs = 0;
m_pass_num = 2;
memset(m_last_dc_val, 0, 3 * sizeof(m_last_dc_val[0]));
// Emit all markers at beginning of image file.
emit_marker(M_SOI);
emit_jfif_app0();
emit_dqt();
emit_sof();
emit_dhts();
emit_sos();
return m_all_stream_writes_succeeded;
}
bool jpeg_encoder::process_end_of_image()
{
if (m_mcu_y_ofs) {
if (m_mcu_y_ofs < 16) { // check here just to shut up static analysis
for (int i = m_mcu_y_ofs; i < m_mcu_y; i++) {
memcpy(m_mcu_lines[i], m_mcu_lines[m_mcu_y_ofs - 1], m_image_bpl_mcu);
}
}
process_mcu_row();
}
put_bits(0x7F, 7);
emit_marker(M_EOI);
flush_output_buffer();
m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_buf(NULL, 0);
m_pass_num++; // purposely bump up m_pass_num, for debugging
return true;
}
void jpeg_encoder::clear()
{
m_mcu_lines[0] = NULL;
m_pass_num = 0;
m_all_stream_writes_succeeded = true;
}
jpeg_encoder::jpeg_encoder()
{
clear();
}
jpeg_encoder::~jpeg_encoder()
{
deinit();
}
bool jpeg_encoder::init(output_stream *pStream, int width, int height, int src_channels, const params &comp_params)
{
deinit();
if (((!pStream) || (width < 1) || (height < 1)) || ((src_channels != 1) && (src_channels != 3) && (src_channels != 4)) || (!comp_params.check())) return false;
m_pStream = pStream;
m_params = comp_params;
return jpg_open(width, height, src_channels);
}
void jpeg_encoder::deinit()
{
jpge_free(m_mcu_lines[0]);
clear();
}
bool jpeg_encoder::process_scanline(const void* pScanline)
{
if ((m_pass_num < 1) || (m_pass_num > 2)) {
return false;
}
if (m_all_stream_writes_succeeded) {
if (!pScanline) {
if (!process_end_of_image()) {
return false;
}
} else {
load_mcu(pScanline);
}
}
return m_all_stream_writes_succeeded;
}
} // namespace jpge

View File

@@ -1,142 +0,0 @@
// jpge.h - C++ class for JPEG compression.
// Public domain, Rich Geldreich <richgel99@gmail.com>
// Alex Evans: Added RGBA support, linear memory allocator.
#ifndef JPEG_ENCODER_H
#define JPEG_ENCODER_H
namespace jpge
{
typedef unsigned char uint8;
typedef signed short int16;
typedef signed int int32;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef unsigned int uint;
// JPEG chroma subsampling factors. Y_ONLY (grayscale images) and H2V2 (color images) are the most common.
enum subsampling_t { Y_ONLY = 0, H1V1 = 1, H2V1 = 2, H2V2 = 3 };
// JPEG compression parameters structure.
struct params {
inline params() : m_quality(85), m_subsampling(H2V2) { }
inline bool check() const {
if ((m_quality < 1) || (m_quality > 100)) {
return false;
}
if ((uint)m_subsampling > (uint)H2V2) {
return false;
}
return true;
}
// Quality: 1-100, higher is better. Typical values are around 50-95.
int m_quality;
// m_subsampling:
// 0 = Y (grayscale) only
// 1 = H1V1 subsampling (YCbCr 1x1x1, 3 blocks per MCU)
// 2 = H2V1 subsampling (YCbCr 2x1x1, 4 blocks per MCU)
// 3 = H2V2 subsampling (YCbCr 4x1x1, 6 blocks per MCU-- very common)
subsampling_t m_subsampling;
};
// Output stream abstract class - used by the jpeg_encoder class to write to the output stream.
// put_buf() is generally called with len==JPGE_OUT_BUF_SIZE bytes, but for headers it'll be called with smaller amounts.
class output_stream {
public:
virtual ~output_stream() { };
virtual bool put_buf(const void* Pbuf, int len) = 0;
virtual uint get_size() const = 0;
};
// Lower level jpeg_encoder class - useful if more control is needed than the above helper functions.
class jpeg_encoder {
public:
jpeg_encoder();
~jpeg_encoder();
// Initializes the compressor.
// pStream: The stream object to use for writing compressed data.
// params - Compression parameters structure, defined above.
// width, height - Image dimensions.
// channels - May be 1, or 3. 1 indicates grayscale, 3 indicates RGB source data.
// Returns false on out of memory or if a stream write fails.
bool init(output_stream *pStream, int width, int height, int src_channels, const params &comp_params = params());
// Call this method with each source scanline.
// width * src_channels bytes per scanline is expected (RGB or Y format).
// You must call with NULL after all scanlines are processed to finish compression.
// Returns false on out of memory or if a stream write fails.
bool process_scanline(const void* pScanline);
// Deinitializes the compressor, freeing any allocated memory. May be called at any time.
void deinit();
private:
jpeg_encoder(const jpeg_encoder &);
jpeg_encoder &operator =(const jpeg_encoder &);
typedef int32 sample_array_t;
enum { JPGE_OUT_BUF_SIZE = 512 };
output_stream *m_pStream;
params m_params;
uint8 m_num_components;
uint8 m_comp_h_samp[3], m_comp_v_samp[3];
int m_image_x, m_image_y, m_image_bpp, m_image_bpl;
int m_image_x_mcu, m_image_y_mcu;
int m_image_bpl_xlt, m_image_bpl_mcu;
int m_mcus_per_row;
int m_mcu_x, m_mcu_y;
uint8 *m_mcu_lines[16];
uint8 m_mcu_y_ofs;
sample_array_t m_sample_array[64];
int16 m_coefficient_array[64];
int m_last_dc_val[3];
uint8 m_out_buf[JPGE_OUT_BUF_SIZE];
uint8 *m_pOut_buf;
uint m_out_buf_left;
uint32 m_bit_buffer;
uint m_bits_in;
uint8 m_pass_num;
bool m_all_stream_writes_succeeded;
bool jpg_open(int p_x_res, int p_y_res, int src_channels);
void flush_output_buffer();
void put_bits(uint bits, uint len);
void emit_byte(uint8 i);
void emit_word(uint i);
void emit_marker(int marker);
void emit_jfif_app0();
void emit_dqt();
void emit_sof();
void emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag);
void emit_dhts();
void emit_sos();
void compute_quant_table(int32 *dst, const int16 *src);
void load_quantized_coefficients(int component_num);
void load_block_8_8_grey(int x);
void load_block_8_8(int x, int y, int c);
void load_block_16_8(int x, int c);
void load_block_16_8_8(int x, int c);
void code_coefficients_pass_two(int component_num);
void code_block(int component_num);
void process_mcu_row();
bool process_end_of_image();
void load_mcu(const void* src);
void clear();
void init();
};
} // namespace jpge
#endif // JPEG_ENCODER

View File

@@ -1,29 +0,0 @@
// Copyright 2015-2016 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.
#ifndef _CONVERSIONS_YUV_H_
#define _CONVERSIONS_YUV_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b);
#ifdef __cplusplus
}
#endif
#endif /* _CONVERSIONS_YUV_H_ */

View File

@@ -1,393 +0,0 @@
// Copyright 2015-2016 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 <stddef.h>
#include <string.h>
#include "img_converters.h"
#include "soc/efuse_reg.h"
#include "esp_heap_caps.h"
#include "yuv.h"
#include "sdkconfig.h"
#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"
#define TAG ""
#else
#include "esp_log.h"
static const char* TAG = "to_bmp";
#endif
static const int BMP_HEADER_LEN = 54;
typedef struct {
uint32_t filesize;
uint32_t reserved;
uint32_t fileoffset_to_pixelarray;
uint32_t dibheadersize;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bitsperpixel;
uint32_t compression;
uint32_t imagesize;
uint32_t ypixelpermeter;
uint32_t xpixelpermeter;
uint32_t numcolorspallette;
uint32_t mostimpcolor;
} bmp_header_t;
typedef struct {
uint16_t width;
uint16_t height;
uint16_t data_offset;
const uint8_t *input;
uint8_t *output;
} rgb_jpg_decoder;
static void *_malloc(size_t size)
{
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}
//output buffer and image width
static bool _rgb_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data)
{
rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
if(!data){
if(x == 0 && y == 0){
//write start
jpeg->width = w;
jpeg->height = h;
//if output is null, this is BMP
if(!jpeg->output){
jpeg->output = (uint8_t *)_malloc((w*h*3)+jpeg->data_offset);
if(!jpeg->output){
return false;
}
}
} else {
//write end
}
return true;
}
size_t jw = jpeg->width*3;
size_t t = y * jw;
size_t b = t + (h * jw);
size_t l = x * 3;
uint8_t *out = jpeg->output+jpeg->data_offset;
uint8_t *o = out;
size_t iy, ix;
w = w * 3;
for(iy=t; iy<b; iy+=jw) {
o = out+iy+l;
for(ix=0; ix<w; ix+= 3) {
o[ix] = data[ix+2];
o[ix+1] = data[ix+1];
o[ix+2] = data[ix];
}
data+=w;
}
return true;
}
static bool _rgb565_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data)
{
rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
if(!data){
if(x == 0 && y == 0){
//write start
jpeg->width = w;
jpeg->height = h;
//if output is null, this is BMP
if(!jpeg->output){
jpeg->output = (uint8_t *)_malloc((w*h*3)+jpeg->data_offset);
if(!jpeg->output){
return false;
}
}
} else {
//write end
}
return true;
}
size_t jw = jpeg->width*3;
size_t jw2 = jpeg->width*2;
size_t t = y * jw;
size_t t2 = y * jw2;
size_t b = t + (h * jw);
size_t l = x * 2;
uint8_t *out = jpeg->output+jpeg->data_offset;
uint8_t *o = out;
size_t iy, iy2, ix, ix2;
w = w * 3;
for(iy=t, iy2=t2; iy<b; iy+=jw, iy2+=jw2) {
o = out+iy2+l;
for(ix2=ix=0; ix<w; ix+= 3, ix2 +=2) {
uint16_t r = data[ix];
uint16_t g = data[ix+1];
uint16_t b = data[ix+2];
uint16_t c = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
o[ix2+1] = c>>8;
o[ix2] = c&0xff;
}
data+=w;
}
return true;
}
//input buffer
static uint32_t _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
{
rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
if(buf) {
memcpy(buf, jpeg->input + index, len);
}
return len;
}
static bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale)
{
rgb_jpg_decoder jpeg;
jpeg.width = 0;
jpeg.height = 0;
jpeg.input = src;
jpeg.output = out;
jpeg.data_offset = 0;
if(esp_jpg_decode(src_len, scale, _jpg_read, _rgb_write, (void*)&jpeg) != ESP_OK){
return false;
}
return true;
}
bool jpg2rgb565(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale)
{
rgb_jpg_decoder jpeg;
jpeg.width = 0;
jpeg.height = 0;
jpeg.input = src;
jpeg.output = out;
jpeg.data_offset = 0;
if(esp_jpg_decode(src_len, scale, _jpg_read, _rgb565_write, (void*)&jpeg) != ESP_OK){
return false;
}
return true;
}
bool jpg2bmp(const uint8_t *src, size_t src_len, uint8_t ** out, size_t * out_len)
{
rgb_jpg_decoder jpeg;
jpeg.width = 0;
jpeg.height = 0;
jpeg.input = src;
jpeg.output = NULL;
jpeg.data_offset = BMP_HEADER_LEN;
if(esp_jpg_decode(src_len, JPG_SCALE_NONE, _jpg_read, _rgb_write, (void*)&jpeg) != ESP_OK){
return false;
}
size_t output_size = jpeg.width*jpeg.height*3;
jpeg.output[0] = 'B';
jpeg.output[1] = 'M';
bmp_header_t * bitmap = (bmp_header_t*)&jpeg.output[2];
bitmap->reserved = 0;
bitmap->filesize = output_size+BMP_HEADER_LEN;
bitmap->fileoffset_to_pixelarray = BMP_HEADER_LEN;
bitmap->dibheadersize = 40;
bitmap->width = jpeg.width;
bitmap->height = -jpeg.height;//set negative for top to bottom
bitmap->planes = 1;
bitmap->bitsperpixel = 24;
bitmap->compression = 0;
bitmap->imagesize = output_size;
bitmap->ypixelpermeter = 0x0B13 ; //2835 , 72 DPI
bitmap->xpixelpermeter = 0x0B13 ; //2835 , 72 DPI
bitmap->numcolorspallette = 0;
bitmap->mostimpcolor = 0;
*out = jpeg.output;
*out_len = output_size+BMP_HEADER_LEN;
return true;
}
bool fmt2rgb888(const uint8_t *src_buf, size_t src_len, pixformat_t format, uint8_t * rgb_buf)
{
int pix_count = 0;
if(format == PIXFORMAT_JPEG) {
return jpg2rgb888(src_buf, src_len, rgb_buf, JPG_SCALE_NONE);
} else if(format == PIXFORMAT_RGB888) {
memcpy(rgb_buf, src_buf, src_len);
} else if(format == PIXFORMAT_RGB565) {
int i;
uint8_t hb, lb;
pix_count = src_len / 2;
for(i=0; i<pix_count; i++) {
hb = *src_buf++;
lb = *src_buf++;
*rgb_buf++ = (lb & 0x1F) << 3;
*rgb_buf++ = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
*rgb_buf++ = hb & 0xF8;
}
} else if(format == PIXFORMAT_GRAYSCALE) {
int i;
uint8_t b;
pix_count = src_len;
for(i=0; i<pix_count; i++) {
b = *src_buf++;
*rgb_buf++ = b;
*rgb_buf++ = b;
*rgb_buf++ = b;
}
} else if(format == PIXFORMAT_YUV422) {
pix_count = src_len / 2;
int i, maxi = pix_count / 2;
uint8_t y0, y1, u, v;
uint8_t r, g, b;
for(i=0; i<maxi; i++) {
y0 = *src_buf++;
u = *src_buf++;
y1 = *src_buf++;
v = *src_buf++;
yuv2rgb(y0, u, v, &r, &g, &b);
*rgb_buf++ = b;
*rgb_buf++ = g;
*rgb_buf++ = r;
yuv2rgb(y1, u, v, &r, &g, &b);
*rgb_buf++ = b;
*rgb_buf++ = g;
*rgb_buf++ = r;
}
}
return true;
}
bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t ** out, size_t * out_len)
{
if(format == PIXFORMAT_JPEG) {
return jpg2bmp(src, src_len, out, out_len);
}
*out = NULL;
*out_len = 0;
int pix_count = width*height;
size_t out_size = (pix_count * 3) + BMP_HEADER_LEN;
uint8_t * out_buf = (uint8_t *)_malloc(out_size);
if(!out_buf) {
ESP_LOGE(TAG, "_malloc failed! %u", out_size);
return false;
}
out_buf[0] = 'B';
out_buf[1] = 'M';
bmp_header_t * bitmap = (bmp_header_t*)&out_buf[2];
bitmap->reserved = 0;
bitmap->filesize = out_size;
bitmap->fileoffset_to_pixelarray = BMP_HEADER_LEN;
bitmap->dibheadersize = 40;
bitmap->width = width;
bitmap->height = -height;//set negative for top to bottom
bitmap->planes = 1;
bitmap->bitsperpixel = 24;
bitmap->compression = 0;
bitmap->imagesize = pix_count * 3;
bitmap->ypixelpermeter = 0x0B13 ; //2835 , 72 DPI
bitmap->xpixelpermeter = 0x0B13 ; //2835 , 72 DPI
bitmap->numcolorspallette = 0;
bitmap->mostimpcolor = 0;
uint8_t * rgb_buf = out_buf + BMP_HEADER_LEN;
uint8_t * src_buf = src;
//convert data to RGB888
if(format == PIXFORMAT_RGB888) {
memcpy(rgb_buf, src_buf, pix_count*3);
} else if(format == PIXFORMAT_RGB565) {
int i;
uint8_t hb, lb;
for(i=0; i<pix_count; i++) {
hb = *src_buf++;
lb = *src_buf++;
*rgb_buf++ = (lb & 0x1F) << 3;
*rgb_buf++ = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
*rgb_buf++ = hb & 0xF8;
}
} else if(format == PIXFORMAT_GRAYSCALE) {
int i;
uint8_t b;
for(i=0; i<pix_count; i++) {
b = *src_buf++;
*rgb_buf++ = b;
*rgb_buf++ = b;
*rgb_buf++ = b;
}
} else if(format == PIXFORMAT_YUV422) {
int i, maxi = pix_count / 2;
uint8_t y0, y1, u, v;
uint8_t r, g, b;
for(i=0; i<maxi; i++) {
y0 = *src_buf++;
u = *src_buf++;
y1 = *src_buf++;
v = *src_buf++;
yuv2rgb(y0, u, v, &r, &g, &b);
*rgb_buf++ = b;
*rgb_buf++ = g;
*rgb_buf++ = r;
yuv2rgb(y1, u, v, &r, &g, &b);
*rgb_buf++ = b;
*rgb_buf++ = g;
*rgb_buf++ = r;
}
}
*out = out_buf;
*out_len = out_size;
return true;
}
bool frame2bmp(camera_fb_t * fb, uint8_t ** out, size_t * out_len)
{
return fmt2bmp(fb->buf, fb->len, fb->width, fb->height, fb->format, out, out_len);
}

View File

@@ -1,245 +0,0 @@
// Copyright 2015-2016 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 <stddef.h>
#include <string.h>
#include "esp_attr.h"
#include "soc/efuse_reg.h"
#include "esp_heap_caps.h"
#include "esp_camera.h"
#include "img_converters.h"
#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 ""
#else
#include "esp_log.h"
static const char* TAG = "to_jpg";
#endif
static void *_malloc(size_t size)
{
void * res = malloc(size);
if(res) {
return res;
}
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}
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)
{
int i=0, o=0, l=0;
if(format == PIXFORMAT_GRAYSCALE) {
memcpy(dst, src + line * width, width);
} else if(format == PIXFORMAT_RGB888) {
l = width * 3;
src += l * line;
for(i=0; i<l; i+=3) {
dst[o++] = src[i+2];
dst[o++] = src[i+1];
dst[o++] = src[i];
}
} else if(format == PIXFORMAT_RGB565) {
l = width * 2;
src += l * line;
for(i=0; i<l; i+=2) {
dst[o++] = src[i] & 0xF8;
dst[o++] = (src[i] & 0x07) << 5 | (src[i+1] & 0xE0) >> 3;
dst[o++] = (src[i+1] & 0x1F) << 3;
}
} else if(format == PIXFORMAT_YUV422) {
uint8_t y0, y1, u, v;
uint8_t r, g, b;
l = width * 2;
src += l * line;
for(i=0; i<l; i+=4) {
y0 = src[i];
u = src[i+1];
y1 = src[i+2];
v = src[i+3];
yuv2rgb(y0, u, v, &r, &g, &b);
dst[o++] = r;
dst[o++] = g;
dst[o++] = b;
yuv2rgb(y1, u, v, &r, &g, &b);
dst[o++] = r;
dst[o++] = g;
dst[o++] = b;
}
}
}
bool convert_image(uint8_t *src, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, jpge::output_stream *dst_stream)
{
int num_channels = 3;
jpge::subsampling_t subsampling = jpge::H2V2;
if(format == PIXFORMAT_GRAYSCALE) {
num_channels = 1;
subsampling = jpge::Y_ONLY;
}
if(!quality) {
quality = 1;
} else if(quality > 100) {
quality = 100;
}
jpge::params comp_params = jpge::params();
comp_params.m_subsampling = subsampling;
comp_params.m_quality = quality;
jpge::jpeg_encoder dst_image;
if (!dst_image.init(dst_stream, width, height, num_channels, comp_params)) {
ESP_LOGE(TAG, "JPG encoder init failed");
return false;
}
uint8_t* line = (uint8_t*)_malloc(width * num_channels);
if(!line) {
ESP_LOGE(TAG, "Scan line malloc failed");
return false;
}
for (int i = 0; i < height; i++) {
convert_line_format(src, format, line, width, num_channels, i);
if (!dst_image.process_scanline(line)) {
ESP_LOGE(TAG, "JPG process line %u failed", i);
free(line);
return false;
}
}
free(line);
if (!dst_image.process_scanline(NULL)) {
ESP_LOGE(TAG, "JPG image finish failed");
return false;
}
dst_image.deinit();
return true;
}
class callback_stream : public jpge::output_stream {
protected:
jpg_out_cb ocb;
void * oarg;
size_t index;
public:
callback_stream(jpg_out_cb cb, void * arg) : ocb(cb), oarg(arg), index(0) { }
virtual ~callback_stream() { }
virtual bool put_buf(const void* data, int len)
{
index += ocb(oarg, index, data, len);
return true;
}
virtual size_t get_size() const
{
return index;
}
};
bool fmt2jpg_cb(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, jpg_out_cb cb, void * arg)
{
callback_stream dst_stream(cb, arg);
return convert_image(src, width, height, format, quality, &dst_stream);
}
bool frame2jpg_cb(camera_fb_t * fb, uint8_t quality, jpg_out_cb cb, void * arg)
{
return fmt2jpg_cb(fb->buf, fb->len, fb->width, fb->height, fb->format, quality, cb, arg);
}
class memory_stream : public jpge::output_stream {
protected:
uint8_t *out_buf;
size_t max_len, index;
public:
memory_stream(void *pBuf, uint buf_size) : out_buf(static_cast<uint8_t*>(pBuf)), max_len(buf_size), index(0) { }
virtual ~memory_stream() { }
virtual bool put_buf(const void* pBuf, int len)
{
if (!pBuf) {
//end of image
return true;
}
if ((size_t)len > (max_len - index)) {
//ESP_LOGW(TAG, "JPG output overflow: %d bytes (%d,%d,%d)", len - (max_len - index), len, index, max_len);
len = max_len - index;
}
if (len) {
memcpy(out_buf + index, pBuf, len);
index += len;
}
return true;
}
virtual size_t get_size() const
{
return index;
}
};
bool fmt2jpg(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixformat_t format, uint8_t quality, uint8_t ** out, size_t * out_len)
{
//todo: allocate proper buffer for holding JPEG data
//this should be enough for CIF frame size
int jpg_buf_len = 128*1024;
uint8_t * jpg_buf = (uint8_t *)_malloc(jpg_buf_len);
if(jpg_buf == NULL) {
ESP_LOGE(TAG, "JPG buffer malloc failed");
return false;
}
memory_stream dst_stream(jpg_buf, jpg_buf_len);
if(!convert_image(src, width, height, format, quality, &dst_stream)) {
free(jpg_buf);
return false;
}
*out = jpg_buf;
*out_len = dst_stream.get_size();
return true;
}
bool frame2jpg(camera_fb_t * fb, uint8_t quality, uint8_t ** out, size_t * out_len)
{
return fmt2jpg(fb->buf, fb->len, fb->width, fb->height, fb->format, quality, out, out_len);
}

View File

@@ -1,298 +0,0 @@
// Copyright 2015-2016 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 "yuv.h"
#include "esp_attr.h"
typedef struct {
int16_t vY;
int16_t vVr;
int16_t vVg;
int16_t vUg;
int16_t vUb;
} yuv_table_row;
static const yuv_table_row yuv_table[256] = {
// Y Vr Vg Ug Ub // #
{ -18, -204, 50, 104, -258 }, // 0
{ -17, -202, 49, 103, -256 }, // 1
{ -16, -201, 49, 102, -254 }, // 2
{ -15, -199, 48, 101, -252 }, // 3
{ -13, -197, 48, 100, -250 }, // 4
{ -12, -196, 48, 99, -248 }, // 5
{ -11, -194, 47, 99, -246 }, // 6
{ -10, -193, 47, 98, -244 }, // 7
{ -9, -191, 46, 97, -242 }, // 8
{ -8, -189, 46, 96, -240 }, // 9
{ -6, -188, 46, 95, -238 }, // 10
{ -5, -186, 45, 95, -236 }, // 11
{ -4, -185, 45, 94, -234 }, // 12
{ -3, -183, 44, 93, -232 }, // 13
{ -2, -181, 44, 92, -230 }, // 14
{ -1, -180, 44, 91, -228 }, // 15
{ 0, -178, 43, 91, -226 }, // 16
{ 1, -177, 43, 90, -223 }, // 17
{ 2, -175, 43, 89, -221 }, // 18
{ 3, -173, 42, 88, -219 }, // 19
{ 4, -172, 42, 87, -217 }, // 20
{ 5, -170, 41, 86, -215 }, // 21
{ 6, -169, 41, 86, -213 }, // 22
{ 8, -167, 41, 85, -211 }, // 23
{ 9, -165, 40, 84, -209 }, // 24
{ 10, -164, 40, 83, -207 }, // 25
{ 11, -162, 39, 82, -205 }, // 26
{ 12, -161, 39, 82, -203 }, // 27
{ 13, -159, 39, 81, -201 }, // 28
{ 15, -158, 38, 80, -199 }, // 29
{ 16, -156, 38, 79, -197 }, // 30
{ 17, -154, 37, 78, -195 }, // 31
{ 18, -153, 37, 78, -193 }, // 32
{ 19, -151, 37, 77, -191 }, // 33
{ 20, -150, 36, 76, -189 }, // 34
{ 22, -148, 36, 75, -187 }, // 35
{ 23, -146, 35, 74, -185 }, // 36
{ 24, -145, 35, 73, -183 }, // 37
{ 25, -143, 35, 73, -181 }, // 38
{ 26, -142, 34, 72, -179 }, // 39
{ 27, -140, 34, 71, -177 }, // 40
{ 29, -138, 34, 70, -175 }, // 41
{ 30, -137, 33, 69, -173 }, // 42
{ 31, -135, 33, 69, -171 }, // 43
{ 32, -134, 32, 68, -169 }, // 44
{ 33, -132, 32, 67, -167 }, // 45
{ 34, -130, 32, 66, -165 }, // 46
{ 36, -129, 31, 65, -163 }, // 47
{ 37, -127, 31, 65, -161 }, // 48
{ 38, -126, 30, 64, -159 }, // 49
{ 39, -124, 30, 63, -157 }, // 50
{ 40, -122, 30, 62, -155 }, // 51
{ 41, -121, 29, 61, -153 }, // 52
{ 43, -119, 29, 60, -151 }, // 53
{ 44, -118, 28, 60, -149 }, // 54
{ 45, -116, 28, 59, -147 }, // 55
{ 46, -114, 28, 58, -145 }, // 56
{ 47, -113, 27, 57, -143 }, // 57
{ 48, -111, 27, 56, -141 }, // 58
{ 50, -110, 26, 56, -139 }, // 59
{ 51, -108, 26, 55, -137 }, // 60
{ 52, -106, 26, 54, -135 }, // 61
{ 53, -105, 25, 53, -133 }, // 62
{ 54, -103, 25, 52, -131 }, // 63
{ 55, -102, 25, 52, -129 }, // 64
{ 57, -100, 24, 51, -127 }, // 65
{ 58, -98, 24, 50, -125 }, // 66
{ 59, -97, 23, 49, -123 }, // 67
{ 60, -95, 23, 48, -121 }, // 68
{ 61, -94, 23, 47, -119 }, // 69
{ 62, -92, 22, 47, -117 }, // 70
{ 64, -90, 22, 46, -115 }, // 71
{ 65, -89, 21, 45, -113 }, // 72
{ 66, -87, 21, 44, -110 }, // 73
{ 67, -86, 21, 43, -108 }, // 74
{ 68, -84, 20, 43, -106 }, // 75
{ 69, -82, 20, 42, -104 }, // 76
{ 71, -81, 19, 41, -102 }, // 77
{ 72, -79, 19, 40, -100 }, // 78
{ 73, -78, 19, 39, -98 }, // 79
{ 74, -76, 18, 39, -96 }, // 80
{ 75, -75, 18, 38, -94 }, // 81
{ 76, -73, 17, 37, -92 }, // 82
{ 77, -71, 17, 36, -90 }, // 83
{ 79, -70, 17, 35, -88 }, // 84
{ 80, -68, 16, 34, -86 }, // 85
{ 81, -67, 16, 34, -84 }, // 86
{ 82, -65, 16, 33, -82 }, // 87
{ 83, -63, 15, 32, -80 }, // 88
{ 84, -62, 15, 31, -78 }, // 89
{ 86, -60, 14, 30, -76 }, // 90
{ 87, -59, 14, 30, -74 }, // 91
{ 88, -57, 14, 29, -72 }, // 92
{ 89, -55, 13, 28, -70 }, // 93
{ 90, -54, 13, 27, -68 }, // 94
{ 91, -52, 12, 26, -66 }, // 95
{ 93, -51, 12, 26, -64 }, // 96
{ 94, -49, 12, 25, -62 }, // 97
{ 95, -47, 11, 24, -60 }, // 98
{ 96, -46, 11, 23, -58 }, // 99
{ 97, -44, 10, 22, -56 }, // 100
{ 98, -43, 10, 21, -54 }, // 101
{ 100, -41, 10, 21, -52 }, // 102
{ 101, -39, 9, 20, -50 }, // 103
{ 102, -38, 9, 19, -48 }, // 104
{ 103, -36, 8, 18, -46 }, // 105
{ 104, -35, 8, 17, -44 }, // 106
{ 105, -33, 8, 17, -42 }, // 107
{ 107, -31, 7, 16, -40 }, // 108
{ 108, -30, 7, 15, -38 }, // 109
{ 109, -28, 7, 14, -36 }, // 110
{ 110, -27, 6, 13, -34 }, // 111
{ 111, -25, 6, 13, -32 }, // 112
{ 112, -23, 5, 12, -30 }, // 113
{ 114, -22, 5, 11, -28 }, // 114
{ 115, -20, 5, 10, -26 }, // 115
{ 116, -19, 4, 9, -24 }, // 116
{ 117, -17, 4, 8, -22 }, // 117
{ 118, -15, 3, 8, -20 }, // 118
{ 119, -14, 3, 7, -18 }, // 119
{ 121, -12, 3, 6, -16 }, // 120
{ 122, -11, 2, 5, -14 }, // 121
{ 123, -9, 2, 4, -12 }, // 122
{ 124, -7, 1, 4, -10 }, // 123
{ 125, -6, 1, 3, -8 }, // 124
{ 126, -4, 1, 2, -6 }, // 125
{ 128, -3, 0, 1, -4 }, // 126
{ 129, -1, 0, 0, -2 }, // 127
{ 130, 0, 0, 0, 0 }, // 128
{ 131, 1, 0, 0, 2 }, // 129
{ 132, 3, 0, -1, 4 }, // 130
{ 133, 4, -1, -2, 6 }, // 131
{ 135, 6, -1, -3, 8 }, // 132
{ 136, 7, -1, -4, 10 }, // 133
{ 137, 9, -2, -4, 12 }, // 134
{ 138, 11, -2, -5, 14 }, // 135
{ 139, 12, -3, -6, 16 }, // 136
{ 140, 14, -3, -7, 18 }, // 137
{ 142, 15, -3, -8, 20 }, // 138
{ 143, 17, -4, -8, 22 }, // 139
{ 144, 19, -4, -9, 24 }, // 140
{ 145, 20, -5, -10, 26 }, // 141
{ 146, 22, -5, -11, 28 }, // 142
{ 147, 23, -5, -12, 30 }, // 143
{ 148, 25, -6, -13, 32 }, // 144
{ 150, 27, -6, -13, 34 }, // 145
{ 151, 28, -7, -14, 36 }, // 146
{ 152, 30, -7, -15, 38 }, // 147
{ 153, 31, -7, -16, 40 }, // 148
{ 154, 33, -8, -17, 42 }, // 149
{ 155, 35, -8, -17, 44 }, // 150
{ 157, 36, -8, -18, 46 }, // 151
{ 158, 38, -9, -19, 48 }, // 152
{ 159, 39, -9, -20, 50 }, // 153
{ 160, 41, -10, -21, 52 }, // 154
{ 161, 43, -10, -21, 54 }, // 155
{ 162, 44, -10, -22, 56 }, // 156
{ 164, 46, -11, -23, 58 }, // 157
{ 165, 47, -11, -24, 60 }, // 158
{ 166, 49, -12, -25, 62 }, // 159
{ 167, 51, -12, -26, 64 }, // 160
{ 168, 52, -12, -26, 66 }, // 161
{ 169, 54, -13, -27, 68 }, // 162
{ 171, 55, -13, -28, 70 }, // 163
{ 172, 57, -14, -29, 72 }, // 164
{ 173, 59, -14, -30, 74 }, // 165
{ 174, 60, -14, -30, 76 }, // 166
{ 175, 62, -15, -31, 78 }, // 167
{ 176, 63, -15, -32, 80 }, // 168
{ 178, 65, -16, -33, 82 }, // 169
{ 179, 67, -16, -34, 84 }, // 170
{ 180, 68, -16, -34, 86 }, // 171
{ 181, 70, -17, -35, 88 }, // 172
{ 182, 71, -17, -36, 90 }, // 173
{ 183, 73, -17, -37, 92 }, // 174
{ 185, 75, -18, -38, 94 }, // 175
{ 186, 76, -18, -39, 96 }, // 176
{ 187, 78, -19, -39, 98 }, // 177
{ 188, 79, -19, -40, 100 }, // 178
{ 189, 81, -19, -41, 102 }, // 179
{ 190, 82, -20, -42, 104 }, // 180
{ 192, 84, -20, -43, 106 }, // 181
{ 193, 86, -21, -43, 108 }, // 182
{ 194, 87, -21, -44, 110 }, // 183
{ 195, 89, -21, -45, 113 }, // 184
{ 196, 90, -22, -46, 115 }, // 185
{ 197, 92, -22, -47, 117 }, // 186
{ 199, 94, -23, -47, 119 }, // 187
{ 200, 95, -23, -48, 121 }, // 188
{ 201, 97, -23, -49, 123 }, // 189
{ 202, 98, -24, -50, 125 }, // 190
{ 203, 100, -24, -51, 127 }, // 191
{ 204, 102, -25, -52, 129 }, // 192
{ 206, 103, -25, -52, 131 }, // 193
{ 207, 105, -25, -53, 133 }, // 194
{ 208, 106, -26, -54, 135 }, // 195
{ 209, 108, -26, -55, 137 }, // 196
{ 210, 110, -26, -56, 139 }, // 197
{ 211, 111, -27, -56, 141 }, // 198
{ 213, 113, -27, -57, 143 }, // 199
{ 214, 114, -28, -58, 145 }, // 200
{ 215, 116, -28, -59, 147 }, // 201
{ 216, 118, -28, -60, 149 }, // 202
{ 217, 119, -29, -60, 151 }, // 203
{ 218, 121, -29, -61, 153 }, // 204
{ 219, 122, -30, -62, 155 }, // 205
{ 221, 124, -30, -63, 157 }, // 206
{ 222, 126, -30, -64, 159 }, // 207
{ 223, 127, -31, -65, 161 }, // 208
{ 224, 129, -31, -65, 163 }, // 209
{ 225, 130, -32, -66, 165 }, // 210
{ 226, 132, -32, -67, 167 }, // 211
{ 228, 134, -32, -68, 169 }, // 212
{ 229, 135, -33, -69, 171 }, // 213
{ 230, 137, -33, -69, 173 }, // 214
{ 231, 138, -34, -70, 175 }, // 215
{ 232, 140, -34, -71, 177 }, // 216
{ 233, 142, -34, -72, 179 }, // 217
{ 235, 143, -35, -73, 181 }, // 218
{ 236, 145, -35, -73, 183 }, // 219
{ 237, 146, -35, -74, 185 }, // 220
{ 238, 148, -36, -75, 187 }, // 221
{ 239, 150, -36, -76, 189 }, // 222
{ 240, 151, -37, -77, 191 }, // 223
{ 242, 153, -37, -78, 193 }, // 224
{ 243, 154, -37, -78, 195 }, // 225
{ 244, 156, -38, -79, 197 }, // 226
{ 245, 158, -38, -80, 199 }, // 227
{ 246, 159, -39, -81, 201 }, // 228
{ 247, 161, -39, -82, 203 }, // 229
{ 249, 162, -39, -82, 205 }, // 230
{ 250, 164, -40, -83, 207 }, // 231
{ 251, 165, -40, -84, 209 }, // 232
{ 252, 167, -41, -85, 211 }, // 233
{ 253, 169, -41, -86, 213 }, // 234
{ 254, 170, -41, -86, 215 }, // 235
{ 256, 172, -42, -87, 217 }, // 236
{ 257, 173, -42, -88, 219 }, // 237
{ 258, 175, -43, -89, 221 }, // 238
{ 259, 177, -43, -90, 223 }, // 239
{ 260, 178, -43, -91, 226 }, // 240
{ 261, 180, -44, -91, 228 }, // 241
{ 263, 181, -44, -92, 230 }, // 242
{ 264, 183, -44, -93, 232 }, // 243
{ 265, 185, -45, -94, 234 }, // 244
{ 266, 186, -45, -95, 236 }, // 245
{ 267, 188, -46, -95, 238 }, // 246
{ 268, 189, -46, -96, 240 }, // 247
{ 270, 191, -46, -97, 242 }, // 248
{ 271, 193, -47, -98, 244 }, // 249
{ 272, 194, -47, -99, 246 }, // 250
{ 273, 196, -48, -99, 248 }, // 251
{ 274, 197, -48, -100, 250 }, // 252
{ 275, 199, -48, -101, 252 }, // 253
{ 277, 201, -49, -102, 254 }, // 254
{ 278, 202, -49, -103, 256 } // 255
};
#define YUYV_CONSTRAIN(v) ((v)<0)?0:(((v)>255)?255:(v))
void IRAM_ATTR yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b)
{
int16_t ri, gi, bi;
ri = yuv_table[y].vY + yuv_table[v].vVr;
gi = yuv_table[y].vY + yuv_table[u].vUg + yuv_table[v].vVg;
bi = yuv_table[y].vY + yuv_table[u].vUb;
*r = YUYV_CONSTRAIN(ri);
*g = YUYV_CONSTRAIN(gi);
*b = YUYV_CONSTRAIN(bi);
}

View File

@@ -1,483 +0,0 @@
// Copyright 2010-2020 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 <stdio.h>
#include <string.h>
#include "esp_heap_caps.h"
#include "ll_cam.h"
#include "cam_hal.h"
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
static const uint16_t JPEG_EOI_MARKER = 0xD9FF; // written in little-endian for esp32
static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length)
{
uint32_t sig = *((uint32_t *)inbuf) & 0xFFFFFF;
if(sig != JPEG_SOI_MARKER) {
for (uint32_t i = 0; i < length; i++) {
sig = *((uint32_t *)(&inbuf[i])) & 0xFFFFFF;
if (sig == JPEG_SOI_MARKER) {
ESP_LOGW(TAG, "SOI: %d", i);
return i;
}
}
ESP_LOGW(TAG, "NO-SOI");
return -1;
}
return 0;
}
static int cam_verify_jpeg_eoi(const uint8_t *inbuf, uint32_t length)
{
int offset = -1;
uint8_t *dptr = (uint8_t *)inbuf + length - 2;
while (dptr > inbuf) {
uint16_t sig = *((uint16_t *)dptr);
if (JPEG_EOI_MARKER == sig) {
offset = dptr - inbuf;
//ESP_LOGW(TAG, "EOI: %d", length - (offset + 2));
return offset;
}
dptr--;
}
return -1;
}
static bool cam_get_next_frame(int * frame_pos)
{
if(!cam_obj->frames[*frame_pos].en){
for (int x = 0; x < cam_obj->frame_cnt; x++) {
if (cam_obj->frames[x].en) {
*frame_pos = x;
return true;
}
}
} else {
return true;
}
return false;
}
static bool cam_start_frame(int * frame_pos)
{
if (cam_get_next_frame(frame_pos)) {
if(ll_cam_start(cam_obj, *frame_pos)){
// Vsync the frame manually
ll_cam_do_vsync(cam_obj);
uint64_t us = (uint64_t)esp_timer_get_time();
cam_obj->frames[*frame_pos].fb.timestamp.tv_sec = us / 1000000UL;
cam_obj->frames[*frame_pos].fb.timestamp.tv_usec = us % 1000000UL;
return true;
}
}
return false;
}
void IRAM_ATTR ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType_t * HPTaskAwoken)
{
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");
}
}
//Copy fram from DMA dma_buffer to fram dma_buffer
static void cam_task(void *arg)
{
int cnt = 0;
int frame_pos = 0;
cam_obj->state = CAM_STATE_IDLE;
cam_event_t cam_event = 0;
xQueueReset(cam_obj->event_queue);
while (1) {
xQueueReceive(cam_obj->event_queue, (void *)&cam_event, portMAX_DELAY);
DBG_PIN_SET(1);
switch (cam_obj->state) {
case CAM_STATE_IDLE: {
if (cam_event == CAM_VSYNC_EVENT) {
//DBG_PIN_SET(1);
if(cam_start_frame(&frame_pos)){
cam_obj->frames[frame_pos].fb.len = 0;
cam_obj->state = CAM_STATE_READ_BUF;
}
cnt = 0;
}
}
break;
case CAM_STATE_READ_BUF: {
camera_fb_t * frame_buffer_event = &cam_obj->frames[frame_pos].fb;
size_t pixels_per_dma = (cam_obj->dma_half_buffer_size * cam_obj->fb_bytes_per_pixel) / (cam_obj->dma_bytes_per_item * cam_obj->in_bytes_per_pixel);
if (cam_event == CAM_IN_SUC_EOF_EVENT) {
if(!cam_obj->psram_mode){
if (cam_obj->fb_size < (frame_buffer_event->len + pixels_per_dma)) {
ESP_LOGW(TAG, "FB-OVF");
ll_cam_stop(cam_obj);
DBG_PIN_SET(0);
continue;
}
frame_buffer_event->len += ll_cam_memcpy(cam_obj,
&frame_buffer_event->buf[frame_buffer_event->len],
&cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size],
cam_obj->dma_half_buffer_size);
}
//Check for JPEG SOI in the first buffer. stop if not found
if (cam_obj->jpeg_mode && cnt == 0 && cam_verify_jpeg_soi(frame_buffer_event->buf, frame_buffer_event->len) != 0) {
ll_cam_stop(cam_obj);
cam_obj->state = CAM_STATE_IDLE;
}
cnt++;
} else if (cam_event == CAM_VSYNC_EVENT) {
//DBG_PIN_SET(1);
ll_cam_stop(cam_obj);
if (cnt || !cam_obj->jpeg_mode || cam_obj->psram_mode) {
if (cam_obj->jpeg_mode) {
if (!cam_obj->psram_mode) {
if (cam_obj->fb_size < (frame_buffer_event->len + pixels_per_dma)) {
ESP_LOGW(TAG, "FB-OVF");
cnt--;
} else {
frame_buffer_event->len += ll_cam_memcpy(cam_obj,
&frame_buffer_event->buf[frame_buffer_event->len],
&cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size],
cam_obj->dma_half_buffer_size);
}
}
cnt++;
}
cam_obj->frames[frame_pos].en = 0;
if (cam_obj->psram_mode) {
if (cam_obj->jpeg_mode) {
frame_buffer_event->len = cnt * cam_obj->dma_half_buffer_size;
} else {
frame_buffer_event->len = cam_obj->recv_size;
}
} else if (!cam_obj->jpeg_mode) {
if (frame_buffer_event->len != cam_obj->fb_size) {
cam_obj->frames[frame_pos].en = 1;
ESP_LOGE(TAG, "FB-SIZE: %u != %u", frame_buffer_event->len, cam_obj->fb_size);
}
}
//send frame
if(!cam_obj->frames[frame_pos].en && xQueueSend(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, 0) != pdTRUE) {
//pop frame buffer from the queue
camera_fb_t * fb2 = NULL;
if(xQueueReceive(cam_obj->frame_buffer_queue, &fb2, 0) == pdTRUE) {
//push the new frame to the end of the queue
if (xQueueSend(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, 0) != pdTRUE) {
cam_obj->frames[frame_pos].en = 1;
ESP_LOGE(TAG, "FBQ-SND");
}
//free the popped buffer
cam_give(fb2);
} else {
//queue is full and we could not pop a frame from it
cam_obj->frames[frame_pos].en = 1;
ESP_LOGE(TAG, "FBQ-RCV");
}
}
}
if(!cam_start_frame(&frame_pos)){
cam_obj->state = CAM_STATE_IDLE;
} else {
cam_obj->frames[frame_pos].fb.len = 0;
}
cnt = 0;
}
}
break;
}
DBG_PIN_SET(0);
}
}
static lldesc_t * allocate_dma_descriptors(uint32_t count, uint16_t size, uint8_t * buffer)
{
lldesc_t *dma = (lldesc_t *)heap_caps_malloc(count * sizeof(lldesc_t), MALLOC_CAP_DMA);
if (dma == NULL) {
return dma;
}
for (int x = 0; x < count; x++) {
dma[x].size = size;
dma[x].length = 0;
dma[x].sosf = 0;
dma[x].eof = 0;
dma[x].owner = 1;
dma[x].buf = (buffer + size * x);
dma[x].empty = (uint32_t)&dma[(x + 1) % count];
}
return dma;
}
static esp_err_t cam_dma_config(const camera_config_t *config)
{
bool ret = ll_cam_dma_sizes(cam_obj);
if (0 == ret) {
return ESP_FAIL;
}
cam_obj->dma_node_cnt = (cam_obj->dma_buffer_size) / cam_obj->dma_node_buffer_size; // Number of DMA nodes
cam_obj->frame_copy_cnt = cam_obj->recv_size / cam_obj->dma_half_buffer_size; // Number of interrupted copies, ping-pong copy
ESP_LOGI(TAG, "buffer_size: %d, half_buffer_size: %d, node_buffer_size: %d, node_cnt: %d, total_cnt: %d",
cam_obj->dma_buffer_size, cam_obj->dma_half_buffer_size, cam_obj->dma_node_buffer_size, cam_obj->dma_node_cnt, cam_obj->frame_copy_cnt);
cam_obj->dma_buffer = NULL;
cam_obj->dma = NULL;
cam_obj->frames = (cam_frame_t *)heap_caps_calloc(1, cam_obj->frame_cnt * sizeof(cam_frame_t), MALLOC_CAP_DEFAULT);
CAM_CHECK(cam_obj->frames != NULL, "frames malloc failed", ESP_FAIL);
uint8_t dma_align = 0;
size_t fb_size = cam_obj->fb_size;
if (cam_obj->psram_mode) {
dma_align = ll_cam_get_dma_align(cam_obj);
if (cam_obj->fb_size < cam_obj->recv_size) {
fb_size = cam_obj->recv_size;
}
}
/* Allocate memory for frame buffer */
size_t alloc_size = fb_size * sizeof(uint8_t) + dma_align;
uint32_t _caps = MALLOC_CAP_8BIT;
if (CAMERA_FB_IN_DRAM == config->fb_location) {
_caps |= MALLOC_CAP_INTERNAL;
} else {
_caps |= MALLOC_CAP_SPIRAM;
}
for (int x = 0; x < cam_obj->frame_cnt; x++) {
cam_obj->frames[x].dma = NULL;
cam_obj->frames[x].fb_offset = 0;
cam_obj->frames[x].en = 0;
ESP_LOGI(TAG, "Allocating %d Byte frame buffer in %s", alloc_size, _caps & MALLOC_CAP_SPIRAM ? "PSRAM" : "OnBoard RAM");
cam_obj->frames[x].fb.buf = (uint8_t *)heap_caps_malloc(alloc_size, _caps);
CAM_CHECK(cam_obj->frames[x].fb.buf != NULL, "frame buffer malloc failed", ESP_FAIL);
if (cam_obj->psram_mode) {
//align PSRAM buffer. TODO: save the offset so proper address can be freed later
cam_obj->frames[x].fb_offset = dma_align - ((uint32_t)cam_obj->frames[x].fb.buf & (dma_align - 1));
cam_obj->frames[x].fb.buf += cam_obj->frames[x].fb_offset;
ESP_LOGI(TAG, "Frame[%d]: Offset: %u, Addr: 0x%08X", x, cam_obj->frames[x].fb_offset, (uint32_t)cam_obj->frames[x].fb.buf);
cam_obj->frames[x].dma = allocate_dma_descriptors(cam_obj->dma_node_cnt, cam_obj->dma_node_buffer_size, cam_obj->frames[x].fb.buf);
CAM_CHECK(cam_obj->frames[x].dma != NULL, "frame dma malloc failed", ESP_FAIL);
}
cam_obj->frames[x].en = 1;
}
if (!cam_obj->psram_mode) {
cam_obj->dma_buffer = (uint8_t *)heap_caps_malloc(cam_obj->dma_buffer_size * sizeof(uint8_t), MALLOC_CAP_DMA);
if(NULL == cam_obj->dma_buffer) {
ESP_LOGE(TAG,"%s(%d): DMA buffer %d Byte malloc failed, the current largest free block:%d Byte", __FUNCTION__, __LINE__,
cam_obj->dma_buffer_size, heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
return ESP_FAIL;
}
cam_obj->dma = allocate_dma_descriptors(cam_obj->dma_node_cnt, cam_obj->dma_node_buffer_size, cam_obj->dma_buffer);
CAM_CHECK(cam_obj->dma != NULL, "dma malloc failed", ESP_FAIL);
}
return ESP_OK;
}
esp_err_t cam_init(const camera_config_t *config)
{
CAM_CHECK(NULL != config, "config pointer is invalid", ESP_ERR_INVALID_ARG);
esp_err_t ret = ESP_OK;
cam_obj = (cam_obj_t *)heap_caps_calloc(1, sizeof(cam_obj_t), MALLOC_CAP_DMA);
CAM_CHECK(NULL != cam_obj, "lcd_cam object malloc error", ESP_ERR_NO_MEM);
cam_obj->swap_data = 0;
cam_obj->vsync_pin = config->pin_vsync;
cam_obj->vsync_invert = true;
ll_cam_set_pin(cam_obj, config);
ret = ll_cam_config(cam_obj, config);
CAM_CHECK_GOTO(ret == ESP_OK, "ll_cam initialize failed", err);
#if CAMERA_DBG_PIN_ENABLE
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[DBG_PIN_NUM], PIN_FUNC_GPIO);
gpio_set_direction(DBG_PIN_NUM, GPIO_MODE_OUTPUT);
gpio_set_pull_mode(DBG_PIN_NUM, GPIO_FLOATING);
#endif
ESP_LOGI(TAG, "cam init ok");
return ESP_OK;
err:
free(cam_obj);
cam_obj = NULL;
return ESP_FAIL;
}
esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint16_t sensor_pid)
{
CAM_CHECK(NULL != config, "config pointer is invalid", ESP_ERR_INVALID_ARG);
esp_err_t ret = ESP_OK;
ret = ll_cam_set_sample_mode(cam_obj, (pixformat_t)config->pixel_format, config->xclk_freq_hz, sensor_pid);
cam_obj->jpeg_mode = config->pixel_format == PIXFORMAT_JPEG;
#if CONFIG_IDF_TARGET_ESP32
cam_obj->psram_mode = false;
#else
cam_obj->psram_mode = (config->xclk_freq_hz == 16000000);
#endif
cam_obj->frame_cnt = config->fb_count;
cam_obj->width = resolution[frame_size].width;
cam_obj->height = resolution[frame_size].height;
if(cam_obj->jpeg_mode){
cam_obj->recv_size = cam_obj->width * cam_obj->height / 5;
cam_obj->fb_size = cam_obj->recv_size;
} else {
cam_obj->recv_size = cam_obj->width * cam_obj->height * cam_obj->in_bytes_per_pixel;
cam_obj->fb_size = cam_obj->width * cam_obj->height * cam_obj->fb_bytes_per_pixel;
}
ret = cam_dma_config(config);
CAM_CHECK_GOTO(ret == ESP_OK, "cam_dma_config failed", err);
cam_obj->event_queue = xQueueCreate(cam_obj->dma_half_buffer_cnt - 1, sizeof(cam_event_t));
CAM_CHECK_GOTO(cam_obj->event_queue != NULL, "event_queue create failed", err);
size_t frame_buffer_queue_len = cam_obj->frame_cnt;
if (config->grab_mode == CAMERA_GRAB_LATEST && cam_obj->frame_cnt > 1) {
frame_buffer_queue_len = cam_obj->frame_cnt - 1;
}
cam_obj->frame_buffer_queue = xQueueCreate(frame_buffer_queue_len, sizeof(camera_fb_t*));
CAM_CHECK_GOTO(cam_obj->frame_buffer_queue != NULL, "frame_buffer_queue create failed", err);
ret = ll_cam_init_isr(cam_obj);
CAM_CHECK_GOTO(ret == ESP_OK, "cam intr alloc failed", err);
#if CONFIG_CAMERA_CORE0
xTaskCreatePinnedToCore(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 0);
#elif CONFIG_CAMERA_CORE1
xTaskCreatePinnedToCore(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 1);
#else
xTaskCreate(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle);
#endif
ESP_LOGI(TAG, "cam config ok");
return ESP_OK;
err:
cam_deinit();
return ESP_FAIL;
}
esp_err_t cam_deinit(void)
{
if (!cam_obj) {
return ESP_FAIL;
}
cam_stop();
if (cam_obj->task_handle) {
vTaskDelete(cam_obj->task_handle);
}
if (cam_obj->event_queue) {
vQueueDelete(cam_obj->event_queue);
}
if (cam_obj->frame_buffer_queue) {
vQueueDelete(cam_obj->frame_buffer_queue);
}
if (cam_obj->dma) {
free(cam_obj->dma);
}
if (cam_obj->dma_buffer) {
free(cam_obj->dma_buffer);
}
if (cam_obj->frames) {
for (int x = 0; x < cam_obj->frame_cnt; x++) {
free(cam_obj->frames[x].fb.buf - cam_obj->frames[x].fb_offset);
if (cam_obj->frames[x].dma) {
free(cam_obj->frames[x].dma);
}
}
free(cam_obj->frames);
}
ll_cam_deinit(cam_obj);
free(cam_obj);
cam_obj = NULL;
return ESP_OK;
}
void cam_stop(void)
{
ll_cam_vsync_intr_enable(cam_obj, false);
ll_cam_stop(cam_obj);
}
void cam_start(void)
{
ll_cam_vsync_intr_enable(cam_obj, true);
}
camera_fb_t *cam_take(TickType_t timeout)
{
camera_fb_t *dma_buffer = NULL;
TickType_t start = xTaskGetTickCount();
xQueueReceive(cam_obj->frame_buffer_queue, (void *)&dma_buffer, timeout);
if (dma_buffer) {
if(cam_obj->jpeg_mode){
// find the end marker for JPEG. Data after that can be discarded
int offset_e = cam_verify_jpeg_eoi(dma_buffer->buf, dma_buffer->len);
if (offset_e >= 0) {
// adjust buffer length
dma_buffer->len = offset_e + sizeof(JPEG_EOI_MARKER);
return dma_buffer;
} else {
ESP_LOGW(TAG, "NO-EOI");
cam_give(dma_buffer);
return cam_take(timeout - (xTaskGetTickCount() - start));//recurse!!!!
}
} else if(cam_obj->psram_mode && cam_obj->in_bytes_per_pixel != cam_obj->fb_bytes_per_pixel){
//currently this is used only for YUV to GRAYSCALE
dma_buffer->len = ll_cam_memcpy(cam_obj, dma_buffer->buf, dma_buffer->buf, dma_buffer->len);
}
return dma_buffer;
} else {
ESP_LOGW(TAG, "Failed to get the frame on time!");
}
return NULL;
}
void cam_give(camera_fb_t *dma_buffer)
{
for (int x = 0; x < cam_obj->frame_cnt; x++) {
if (&cam_obj->frames[x].fb == dma_buffer) {
cam_obj->frames[x].en = 1;
break;
}
}
}

View File

@@ -1,421 +0,0 @@
// Copyright 2015-2016 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "time.h"
#include "sys/time.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "sensor.h"
#include "sccb.h"
#include "cam_hal.h"
#include "esp_camera.h"
#include "xclk.h"
#if CONFIG_OV2640_SUPPORT
#include "ov2640.h"
#endif
#if CONFIG_OV7725_SUPPORT
#include "ov7725.h"
#endif
#if CONFIG_OV3660_SUPPORT
#include "ov3660.h"
#endif
#if CONFIG_OV5640_SUPPORT
#include "ov5640.h"
#endif
#if CONFIG_NT99141_SUPPORT
#include "nt99141.h"
#endif
#if CONFIG_OV7670_SUPPORT
#include "ov7670.h"
#endif
#if CONFIG_GC2145_SUPPORT
#include "gc2145.h"
#endif
#if CONFIG_GC032A_SUPPORT
#include "gc032a.h"
#endif
#if CONFIG_GC0308_SUPPORT
#include "gc0308.h"
#endif
#if CONFIG_BF3005_SUPPORT
#include "bf3005.h"
#endif
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
#include "esp32-hal-log.h"
#define TAG ""
#else
#include "esp_log.h"
static const char *TAG = "camera";
#endif
typedef struct {
sensor_t sensor;
camera_fb_t fb;
} camera_state_t;
static const char *CAMERA_SENSOR_NVS_KEY = "sensor";
static const char *CAMERA_PIXFORMAT_NVS_KEY = "pixformat";
static camera_state_t *s_state = NULL;
#if CONFIG_IDF_TARGET_ESP32S3 // LCD_CAM module of ESP32-S3 will generate xclk
#define CAMERA_ENABLE_OUT_CLOCK(v)
#define CAMERA_DISABLE_OUT_CLOCK()
#else
#define CAMERA_ENABLE_OUT_CLOCK(v) camera_enable_out_clock((v))
#define CAMERA_DISABLE_OUT_CLOCK() camera_disable_out_clock()
#endif
typedef struct {
int (*detect)(int slv_addr, sensor_id_t *id);
int (*init)(sensor_t *sensor);
} sensor_func_t;
static const sensor_func_t g_sensors[] = {
#if CONFIG_OV7725_SUPPORT
{ov7725_detect, ov7725_init},
#endif
#if CONFIG_OV7670_SUPPORT
{ov7670_detect, ov7670_init},
#endif
#if CONFIG_OV2640_SUPPORT
{ov2640_detect, ov2640_init},
#endif
#if CONFIG_OV3660_SUPPORT
{ov3660_detect, ov3660_init},
#endif
#if CONFIG_OV5640_SUPPORT
{ov5640_detect, ov5640_init},
#endif
#if CONFIG_NT99141_SUPPORT
{nt99141_detect, nt99141_init},
#endif
#if CONFIG_GC2145_SUPPORT
{gc2145_detect, gc2145_init},
#endif
#if CONFIG_GC032A_SUPPORT
{gc032a_detect, gc032a_init},
#endif
#if CONFIG_GC0308_SUPPORT
{gc0308_detect, gc0308_init},
#endif
#if CONFIG_BF3005_SUPPORT
{bf3005_detect, bf3005_init},
#endif
};
static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model)
{
*out_camera_model = CAMERA_NONE;
if (s_state != NULL) {
return ESP_ERR_INVALID_STATE;
}
s_state = (camera_state_t *) calloc(sizeof(camera_state_t), 1);
if (!s_state) {
return ESP_ERR_NO_MEM;
}
if (config->pin_xclk >= 0) {
ESP_LOGD(TAG, "Enabling XCLK output");
CAMERA_ENABLE_OUT_CLOCK(config);
}
if (config->pin_sscb_sda != -1) {
ESP_LOGD(TAG, "Initializing SSCB");
SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl);
}
if (config->pin_pwdn >= 0) {
ESP_LOGD(TAG, "Resetting camera by power down line");
gpio_config_t conf = { 0 };
conf.pin_bit_mask = 1LL << config->pin_pwdn;
conf.mode = GPIO_MODE_OUTPUT;
gpio_config(&conf);
// carefull, logic is inverted compared to reset pin
gpio_set_level(config->pin_pwdn, 1);
vTaskDelay(10 / portTICK_PERIOD_MS);
gpio_set_level(config->pin_pwdn, 0);
vTaskDelay(10 / portTICK_PERIOD_MS);
}
if (config->pin_reset >= 0) {
ESP_LOGD(TAG, "Resetting camera");
gpio_config_t conf = { 0 };
conf.pin_bit_mask = 1LL << config->pin_reset;
conf.mode = GPIO_MODE_OUTPUT;
gpio_config(&conf);
gpio_set_level(config->pin_reset, 0);
vTaskDelay(10 / portTICK_PERIOD_MS);
gpio_set_level(config->pin_reset, 1);
vTaskDelay(10 / portTICK_PERIOD_MS);
}
ESP_LOGD(TAG, "Searching for camera address");
vTaskDelay(10 / portTICK_PERIOD_MS);
uint8_t slv_addr = SCCB_Probe();
if (slv_addr == 0) {
CAMERA_DISABLE_OUT_CLOCK();
return ESP_ERR_NOT_FOUND;
}
ESP_LOGI(TAG, "Detected camera at address=0x%02x", slv_addr);
s_state->sensor.slv_addr = slv_addr;
s_state->sensor.xclk_freq_hz = config->xclk_freq_hz;
/**
* Read sensor ID and then initialize sensor
* Attention: Some sensors have the same SCCB address. Therefore, several attempts may be made in the detection process
*/
sensor_id_t *id = &s_state->sensor.id;
for (size_t i = 0; i < sizeof(g_sensors) / sizeof(sensor_func_t); i++) {
if (g_sensors[i].detect(slv_addr, id)) {
camera_sensor_info_t *info = esp_camera_sensor_get_info(id);
if (NULL != info) {
*out_camera_model = info->model;
ESP_LOGI(TAG, "Detected %s camera", info->name);
g_sensors[i].init(&s_state->sensor);
break;
}
}
}
if (CAMERA_NONE == *out_camera_model) { //If no supported sensors are detected
CAMERA_DISABLE_OUT_CLOCK();
ESP_LOGE(TAG, "Detected camera not supported.");
return ESP_ERR_NOT_SUPPORTED;
}
ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x",
id->PID, id->VER, id->MIDH, id->MIDL);
ESP_LOGD(TAG, "Doing SW reset of sensor");
vTaskDelay(10 / portTICK_PERIOD_MS);
s_state->sensor.reset(&s_state->sensor);
return ESP_OK;
}
esp_err_t esp_camera_init(const camera_config_t *config)
{
esp_err_t err;
err = cam_init(config);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Camera init failed with error 0x%x", err);
return err;
}
camera_model_t camera_model = CAMERA_NONE;
err = camera_probe(config, &camera_model);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Camera probe failed with error 0x%x(%s)", err, esp_err_to_name(err));
goto fail;
}
framesize_t frame_size = (framesize_t) config->frame_size;
pixformat_t pix_format = (pixformat_t) config->pixel_format;
if (PIXFORMAT_JPEG == pix_format && (!camera_sensor[camera_model].support_jpeg)) {
ESP_LOGE(TAG, "JPEG format is not supported on this sensor");
err = ESP_ERR_NOT_SUPPORTED;
goto fail;
}
if (frame_size > camera_sensor[camera_model].max_size) {
ESP_LOGW(TAG, "The frame size exceeds the maximum for this sensor, it will be forced to the maximum possible value");
frame_size = camera_sensor[camera_model].max_size;
}
err = cam_config(config, frame_size, s_state->sensor.id.PID);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Camera config failed with error 0x%x", err);
goto fail;
}
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");
err = ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE;
goto fail;
}
s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
if (s_state->sensor.id.PID == OV2640_PID) {
s_state->sensor.set_gainceiling(&s_state->sensor, GAINCEILING_2X);
s_state->sensor.set_bpc(&s_state->sensor, false);
s_state->sensor.set_wpc(&s_state->sensor, true);
s_state->sensor.set_lenc(&s_state->sensor, true);
}
if (pix_format == PIXFORMAT_JPEG) {
s_state->sensor.set_quality(&s_state->sensor, config->jpeg_quality);
}
s_state->sensor.init_status(&s_state->sensor);
cam_start();
return ESP_OK;
fail:
esp_camera_deinit();
return err;
}
esp_err_t esp_camera_deinit()
{
esp_err_t ret = cam_deinit();
CAMERA_DISABLE_OUT_CLOCK();
if (s_state) {
SCCB_Deinit();
free(s_state);
s_state = NULL;
}
return ret;
}
#define FB_GET_TIMEOUT (4000 / portTICK_PERIOD_MS)
camera_fb_t *esp_camera_fb_get()
{
if (s_state == NULL) {
return NULL;
}
camera_fb_t *fb = cam_take(FB_GET_TIMEOUT);
//set the frame properties
if (fb) {
fb->width = resolution[s_state->sensor.status.framesize].width;
fb->height = resolution[s_state->sensor.status.framesize].height;
fb->format = s_state->sensor.pixformat;
}
return fb;
}
void esp_camera_fb_return(camera_fb_t *fb)
{
if (s_state == NULL) {
return;
}
cam_give(fb);
}
sensor_t *esp_camera_sensor_get()
{
if (s_state == NULL) {
return NULL;
}
return &s_state->sensor;
}
esp_err_t esp_camera_save_to_nvs(const char *key)
{
#if ESP_IDF_VERSION_MAJOR > 3
nvs_handle_t handle;
#else
nvs_handle handle;
#endif
esp_err_t ret = nvs_open(key, NVS_READWRITE, &handle);
if (ret == ESP_OK) {
sensor_t *s = esp_camera_sensor_get();
if (s != NULL) {
ret = nvs_set_blob(handle, CAMERA_SENSOR_NVS_KEY, &s->status, sizeof(camera_status_t));
if (ret == ESP_OK) {
uint8_t pf = s->pixformat;
ret = nvs_set_u8(handle, CAMERA_PIXFORMAT_NVS_KEY, pf);
}
return ret;
} else {
return ESP_ERR_CAMERA_NOT_DETECTED;
}
nvs_close(handle);
return ret;
} else {
return ret;
}
}
esp_err_t esp_camera_load_from_nvs(const char *key)
{
#if ESP_IDF_VERSION_MAJOR > 3
nvs_handle_t handle;
#else
nvs_handle handle;
#endif
uint8_t pf;
esp_err_t ret = nvs_open(key, NVS_READWRITE, &handle);
if (ret == ESP_OK) {
sensor_t *s = esp_camera_sensor_get();
camera_status_t st;
if (s != NULL) {
size_t size = sizeof(camera_status_t);
ret = nvs_get_blob(handle, CAMERA_SENSOR_NVS_KEY, &st, &size);
if (ret == ESP_OK) {
s->set_ae_level(s, st.ae_level);
s->set_aec2(s, st.aec2);
s->set_aec_value(s, st.aec_value);
s->set_agc_gain(s, st.agc_gain);
s->set_awb_gain(s, st.awb_gain);
s->set_bpc(s, st.bpc);
s->set_brightness(s, st.brightness);
s->set_colorbar(s, st.colorbar);
s->set_contrast(s, st.contrast);
s->set_dcw(s, st.dcw);
s->set_denoise(s, st.denoise);
s->set_exposure_ctrl(s, st.aec);
s->set_framesize(s, st.framesize);
s->set_gain_ctrl(s, st.agc);
s->set_gainceiling(s, st.gainceiling);
s->set_hmirror(s, st.hmirror);
s->set_lenc(s, st.lenc);
s->set_quality(s, st.quality);
s->set_raw_gma(s, st.raw_gma);
s->set_saturation(s, st.saturation);
s->set_sharpness(s, st.sharpness);
s->set_special_effect(s, st.special_effect);
s->set_vflip(s, st.vflip);
s->set_wb_mode(s, st.wb_mode);
s->set_whitebal(s, st.awb);
s->set_wpc(s, st.wpc);
}
ret = nvs_get_u8(handle, CAMERA_PIXFORMAT_NVS_KEY, &pf);
if (ret == ESP_OK) {
s->set_pixformat(s, pf);
}
} else {
return ESP_ERR_CAMERA_NOT_DETECTED;
}
nvs_close(handle);
return ret;
} else {
ESP_LOGW(TAG, "Error (%d) opening nvs key \"%s\"", ret, key);
return ret;
}
}

View File

@@ -1,214 +0,0 @@
// Copyright 2015-2016 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.
/*
* Example Use
*
static camera_config_t camera_example_config = {
.pin_pwdn = PIN_PWDN,
.pin_reset = PIN_RESET,
.pin_xclk = PIN_XCLK,
.pin_sscb_sda = PIN_SIOD,
.pin_sscb_scl = PIN_SIOC,
.pin_d7 = PIN_D7,
.pin_d6 = PIN_D6,
.pin_d5 = PIN_D5,
.pin_d4 = PIN_D4,
.pin_d3 = PIN_D3,
.pin_d2 = PIN_D2,
.pin_d1 = PIN_D1,
.pin_d0 = PIN_D0,
.pin_vsync = PIN_VSYNC,
.pin_href = PIN_HREF,
.pin_pclk = PIN_PCLK,
.xclk_freq_hz = 20000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_SVGA,
.jpeg_quality = 10,
.fb_count = 2,
.grab_mode = CAMERA_GRAB_WHEN_EMPTY
};
esp_err_t camera_example_init(){
return esp_camera_init(&camera_example_config);
}
esp_err_t camera_example_capture(){
//capture a frame
camera_fb_t * fb = esp_camera_fb_get();
if (!fb) {
ESP_LOGE(TAG, "Frame buffer could not be acquired");
return ESP_FAIL;
}
//replace this with your own function
display_image(fb->width, fb->height, fb->pixformat, fb->buf, fb->len);
//return the frame buffer back to be reused
esp_camera_fb_return(fb);
return ESP_OK;
}
*/
#pragma once
#include "esp_err.h"
#include "driver/ledc.h"
#include "sensor.h"
#include "sys/time.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Configuration structure for camera initialization
*/
typedef enum {
CAMERA_GRAB_WHEN_EMPTY, /*!< Fills buffers when they are empty. Less resources but first 'fb_count' frames might be old */
CAMERA_GRAB_LATEST /*!< Except when 1 frame buffer is used, queue will always contain the last 'fb_count' frames */
} camera_grab_mode_t;
/**
* @brief Camera frame buffer location
*/
typedef enum {
CAMERA_FB_IN_PSRAM, /*!< Frame buffer is placed in external PSRAM */
CAMERA_FB_IN_DRAM /*!< Frame buffer is placed in internal DRAM */
} camera_fb_location_t;
/**
* @brief Configuration structure for camera initialization
*/
typedef struct {
int pin_pwdn; /*!< GPIO pin for camera power down line */
int pin_reset; /*!< GPIO pin for camera reset line */
int pin_xclk; /*!< GPIO pin for camera XCLK line */
int pin_sscb_sda; /*!< GPIO pin for camera SDA line */
int pin_sscb_scl; /*!< GPIO pin for camera SCL line */
int pin_d7; /*!< GPIO pin for camera D7 line */
int pin_d6; /*!< GPIO pin for camera D6 line */
int pin_d5; /*!< GPIO pin for camera D5 line */
int pin_d4; /*!< GPIO pin for camera D4 line */
int pin_d3; /*!< GPIO pin for camera D3 line */
int pin_d2; /*!< GPIO pin for camera D2 line */
int pin_d1; /*!< GPIO pin for camera D1 line */
int pin_d0; /*!< GPIO pin for camera D0 line */
int pin_vsync; /*!< GPIO pin for camera VSYNC line */
int pin_href; /*!< GPIO pin for camera HREF line */
int pin_pclk; /*!< GPIO pin for camera PCLK line */
int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode */
ledc_timer_t ledc_timer; /*!< LEDC timer to be used for generating XCLK */
ledc_channel_t ledc_channel; /*!< LEDC channel to be used for generating XCLK */
pixformat_t pixel_format; /*!< Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG */
framesize_t frame_size; /*!< Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA */
int jpeg_quality; /*!< Quality of JPEG output. 0-63 lower means higher quality */
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 */
} camera_config_t;
/**
* @brief Data structure of camera frame buffer
*/
typedef struct {
uint8_t * buf; /*!< Pointer to the pixel data */
size_t len; /*!< Length of the buffer in bytes */
size_t width; /*!< Width of the buffer in pixels */
size_t height; /*!< Height of the buffer in pixels */
pixformat_t format; /*!< Format of the pixel data */
struct timeval timestamp; /*!< Timestamp since boot of the first DMA buffer of the frame */
} camera_fb_t;
#define ESP_ERR_CAMERA_BASE 0x20000
#define ESP_ERR_CAMERA_NOT_DETECTED (ESP_ERR_CAMERA_BASE + 1)
#define ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE (ESP_ERR_CAMERA_BASE + 2)
#define ESP_ERR_CAMERA_FAILED_TO_SET_OUT_FORMAT (ESP_ERR_CAMERA_BASE + 3)
#define ESP_ERR_CAMERA_NOT_SUPPORTED (ESP_ERR_CAMERA_BASE + 4)
/**
* @brief Initialize the camera driver
*
* @note call camera_probe before calling this function
*
* This function detects and configures camera over I2C interface,
* allocates framebuffer and DMA buffers,
* initializes parallel I2S input, and sets up DMA descriptors.
*
* Currently this function can only be called once and there is
* no way to de-initialize this module.
*
* @param config Camera configuration parameters
*
* @return ESP_OK on success
*/
esp_err_t esp_camera_init(const camera_config_t* config);
/**
* @brief Deinitialize the camera driver
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE if the driver hasn't been initialized yet
*/
esp_err_t esp_camera_deinit();
/**
* @brief Obtain pointer to a frame buffer.
*
* @return pointer to the frame buffer
*/
camera_fb_t* esp_camera_fb_get();
/**
* @brief Return the frame buffer to be reused again.
*
* @param fb Pointer to the frame buffer
*/
void esp_camera_fb_return(camera_fb_t * fb);
/**
* @brief Get a pointer to the image sensor control structure
*
* @return pointer to the sensor
*/
sensor_t * esp_camera_sensor_get();
/**
* @brief Save camera settings to non-volatile-storage (NVS)
*
* @param key A unique nvs key name for the camera settings
*/
esp_err_t esp_camera_save_to_nvs(const char *key);
/**
* @brief Load camera settings from non-volatile-storage (NVS)
*
* @param key A unique nvs key name for the camera settings
*/
esp_err_t esp_camera_load_from_nvs(const char *key);
#ifdef __cplusplus
}
#endif
#include "img_converters.h"

View File

@@ -1,248 +0,0 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* Sensor abstraction layer.
*
*/
#ifndef __SENSOR_H__
#define __SENSOR_H__
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
OV9650_PID = 0x96,
OV7725_PID = 0x77,
OV2640_PID = 0x26,
OV3660_PID = 0x3660,
OV5640_PID = 0x5640,
OV7670_PID = 0x76,
NT99141_PID = 0x1410,
GC2145_PID = 0x2145,
GC032A_PID = 0x232a,
GC0308_PID = 0x9b,
BF3005_PID = 0x30,
} camera_pid_t;
typedef enum {
CAMERA_OV7725,
CAMERA_OV2640,
CAMERA_OV3660,
CAMERA_OV5640,
CAMERA_OV7670,
CAMERA_NT99141,
CAMERA_GC2145,
CAMERA_GC032A,
CAMERA_GC0308,
CAMERA_BF3005,
CAMERA_MODEL_MAX,
CAMERA_NONE,
} camera_model_t;
typedef enum {
OV2640_SCCB_ADDR = 0x30,// 0x60 >> 1
OV5640_SCCB_ADDR = 0x3C,// 0x78 >> 1
OV3660_SCCB_ADDR = 0x3C,// 0x78 >> 1
OV7725_SCCB_ADDR = 0x21,// 0x42 >> 1
OV7670_SCCB_ADDR = 0x21,// 0x42 >> 1
NT99141_SCCB_ADDR = 0x2A,// 0x54 >> 1
GC2145_SCCB_ADDR = 0x3C,// 0x78 >> 1
GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1
GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1
BF3005_SCCB_ADDR = 0x6E,
} camera_sccb_addr_t;
typedef enum {
PIXFORMAT_RGB565, // 2BPP/RGB565
PIXFORMAT_YUV422, // 2BPP/YUV422
PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
PIXFORMAT_JPEG, // JPEG/COMPRESSED
PIXFORMAT_RGB888, // 3BPP/RGB888
PIXFORMAT_RAW, // RAW
PIXFORMAT_RGB444, // 3BP2P/RGB444
PIXFORMAT_RGB555, // 3BP2P/RGB555
} pixformat_t;
typedef enum {
FRAMESIZE_96X96, // 96x96
FRAMESIZE_QQVGA, // 160x120
FRAMESIZE_QCIF, // 176x144
FRAMESIZE_HQVGA, // 240x176
FRAMESIZE_240X240, // 240x240
FRAMESIZE_QVGA, // 320x240
FRAMESIZE_CIF, // 400x296
FRAMESIZE_HVGA, // 480x320
FRAMESIZE_VGA, // 640x480
FRAMESIZE_SVGA, // 800x600
FRAMESIZE_XGA, // 1024x768
FRAMESIZE_HD, // 1280x720
FRAMESIZE_SXGA, // 1280x1024
FRAMESIZE_UXGA, // 1600x1200
// 3MP Sensors
FRAMESIZE_FHD, // 1920x1080
FRAMESIZE_P_HD, // 720x1280
FRAMESIZE_P_3MP, // 864x1536
FRAMESIZE_QXGA, // 2048x1536
// 5MP Sensors
FRAMESIZE_QHD, // 2560x1440
FRAMESIZE_WQXGA, // 2560x1600
FRAMESIZE_P_FHD, // 1080x1920
FRAMESIZE_QSXGA, // 2560x1920
FRAMESIZE_INVALID
} framesize_t;
typedef struct {
const camera_model_t model;
const char *name;
const camera_sccb_addr_t sccb_addr;
const camera_pid_t pid;
const framesize_t max_size;
const bool support_jpeg;
} camera_sensor_info_t;
typedef enum {
ASPECT_RATIO_4X3,
ASPECT_RATIO_3X2,
ASPECT_RATIO_16X10,
ASPECT_RATIO_5X3,
ASPECT_RATIO_16X9,
ASPECT_RATIO_21X9,
ASPECT_RATIO_5X4,
ASPECT_RATIO_1X1,
ASPECT_RATIO_9X16
} aspect_ratio_t;
typedef enum {
GAINCEILING_2X,
GAINCEILING_4X,
GAINCEILING_8X,
GAINCEILING_16X,
GAINCEILING_32X,
GAINCEILING_64X,
GAINCEILING_128X,
} gainceiling_t;
typedef struct {
uint16_t max_width;
uint16_t max_height;
uint16_t start_x;
uint16_t start_y;
uint16_t end_x;
uint16_t end_y;
uint16_t offset_x;
uint16_t offset_y;
uint16_t total_x;
uint16_t total_y;
} ratio_settings_t;
typedef struct {
const uint16_t width;
const uint16_t height;
const aspect_ratio_t aspect_ratio;
} resolution_info_t;
// Resolution table (in sensor.c)
extern const resolution_info_t resolution[];
// camera sensor table (in sensor.c)
extern const camera_sensor_info_t camera_sensor[];
typedef struct {
uint8_t MIDH;
uint8_t MIDL;
uint16_t PID;
uint8_t VER;
} sensor_id_t;
typedef struct {
framesize_t framesize;//0 - 10
bool scale;
bool binning;
uint8_t quality;//0 - 63
int8_t brightness;//-2 - 2
int8_t contrast;//-2 - 2
int8_t saturation;//-2 - 2
int8_t sharpness;//-2 - 2
uint8_t denoise;
uint8_t special_effect;//0 - 6
uint8_t wb_mode;//0 - 4
uint8_t awb;
uint8_t awb_gain;
uint8_t aec;
uint8_t aec2;
int8_t ae_level;//-2 - 2
uint16_t aec_value;//0 - 1200
uint8_t agc;
uint8_t agc_gain;//0 - 30
uint8_t gainceiling;//0 - 6
uint8_t bpc;
uint8_t wpc;
uint8_t raw_gma;
uint8_t lenc;
uint8_t hmirror;
uint8_t vflip;
uint8_t dcw;
uint8_t colorbar;
} camera_status_t;
typedef struct _sensor sensor_t;
typedef struct _sensor {
sensor_id_t id; // Sensor ID.
uint8_t slv_addr; // Sensor I2C slave address.
pixformat_t pixformat;
camera_status_t status;
int xclk_freq_hz;
// Sensor function pointers
int (*init_status) (sensor_t *sensor);
int (*reset) (sensor_t *sensor);
int (*set_pixformat) (sensor_t *sensor, pixformat_t pixformat);
int (*set_framesize) (sensor_t *sensor, framesize_t framesize);
int (*set_contrast) (sensor_t *sensor, int level);
int (*set_brightness) (sensor_t *sensor, int level);
int (*set_saturation) (sensor_t *sensor, int level);
int (*set_sharpness) (sensor_t *sensor, int level);
int (*set_denoise) (sensor_t *sensor, int level);
int (*set_gainceiling) (sensor_t *sensor, gainceiling_t gainceiling);
int (*set_quality) (sensor_t *sensor, int quality);
int (*set_colorbar) (sensor_t *sensor, int enable);
int (*set_whitebal) (sensor_t *sensor, int enable);
int (*set_gain_ctrl) (sensor_t *sensor, int enable);
int (*set_exposure_ctrl) (sensor_t *sensor, int enable);
int (*set_hmirror) (sensor_t *sensor, int enable);
int (*set_vflip) (sensor_t *sensor, int enable);
int (*set_aec2) (sensor_t *sensor, int enable);
int (*set_awb_gain) (sensor_t *sensor, int enable);
int (*set_agc_gain) (sensor_t *sensor, int gain);
int (*set_aec_value) (sensor_t *sensor, int gain);
int (*set_special_effect) (sensor_t *sensor, int effect);
int (*set_wb_mode) (sensor_t *sensor, int mode);
int (*set_ae_level) (sensor_t *sensor, int level);
int (*set_dcw) (sensor_t *sensor, int enable);
int (*set_bpc) (sensor_t *sensor, int enable);
int (*set_wpc) (sensor_t *sensor, int enable);
int (*set_raw_gma) (sensor_t *sensor, int enable);
int (*set_lenc) (sensor_t *sensor, int enable);
int (*get_reg) (sensor_t *sensor, int reg, int mask);
int (*set_reg) (sensor_t *sensor, int reg, int mask, int value);
int (*set_res_raw) (sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning);
int (*set_pll) (sensor_t *sensor, int bypass, int mul, int sys, int root, int pre, int seld5, int pclken, int pclk);
int (*set_xclk) (sensor_t *sensor, int timer, int xclk);
} sensor_t;
camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id);
#ifdef __cplusplus
}
#endif
#endif /* __SENSOR_H__ */

View File

@@ -1,60 +0,0 @@
// Copyright 2010-2020 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 "esp_camera.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Uninitialize the lcd_cam module
*
* @param handle Provide handle pointer to release resources
*
* @return
* - ESP_OK Success
* - ESP_FAIL Uninitialize fail
*/
esp_err_t cam_deinit(void);
/**
* @brief Initialize the lcd_cam module
*
* @param config Configurations - see lcd_cam_config_t struct
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_ERR_NO_MEM No memory to initialize lcd_cam
* - ESP_FAIL Initialize fail
*/
esp_err_t cam_init(const camera_config_t *config);
esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint16_t sensor_pid);
void cam_stop(void);
void cam_start(void);
camera_fb_t *cam_take(TickType_t timeout);
void cam_give(camera_fb_t *dma_buffer);
#ifdef __cplusplus
}
#endif

View File

@@ -1,19 +0,0 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* SCCB (I2C like) driver.
*
*/
#ifndef __SCCB_H__
#define __SCCB_H__
#include <stdint.h>
int SCCB_Init(int pin_sda, int pin_scl);
int SCCB_Deinit(void);
uint8_t SCCB_Probe();
uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg);
uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data);
uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg);
uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data);
#endif // __SCCB_H__

View File

@@ -1,9 +0,0 @@
#pragma once
#include "esp_system.h"
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
esp_err_t camera_enable_out_clock();
void camera_disable_out_clock();

View File

@@ -1,184 +0,0 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* SCCB (I2C like) driver.
*
*/
#include <stdbool.h>
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "sccb.h"
#include "sensor.h"
#include <stdio.h>
#include "sdkconfig.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 = "sccb";
#endif
#define LITTLETOBIG(x) ((x<<8)|(x>>8))
#include "driver/i2c.h"
#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 */
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */
#if CONFIG_SCCB_HARDWARE_I2C_PORT1
const int SCCB_I2C_PORT = 1;
#else
const int SCCB_I2C_PORT = 0;
#endif
int SCCB_Init(int pin_sda, int pin_scl)
{
ESP_LOGI(TAG, "pin_sda %d pin_scl %d", pin_sda, pin_scl);
i2c_config_t conf;
memset(&conf, 0, sizeof(i2c_config_t));
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = pin_sda;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_io_num = pin_scl;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = SCCB_FREQ;
i2c_param_config(SCCB_I2C_PORT, &conf);
i2c_driver_install(SCCB_I2C_PORT, conf.mode, 0, 0, 0);
return 0;
}
int SCCB_Deinit(void)
{
return i2c_driver_delete(SCCB_I2C_PORT);
}
uint8_t SCCB_Probe(void)
{
uint8_t slave_addr = 0x0;
// for (size_t i = 1; i < 0x80; i++) {
// i2c_cmd_handle_t cmd = i2c_cmd_link_create();
// i2c_master_start(cmd);
// i2c_master_write_byte(cmd, ( i << 1 ) | WRITE_BIT, ACK_CHECK_EN);
// i2c_master_stop(cmd);
// esp_err_t ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
// i2c_cmd_link_delete(cmd);
// if( ret == ESP_OK) {
// ESP_LOGW(TAG, "Found I2C Device at 0x%02X", i);
// }
// }
for (size_t i = 0; i < CAMERA_MODEL_MAX; i++) {
if (slave_addr == camera_sensor[i].sccb_addr) {
continue;
}
slave_addr = camera_sensor[i].sccb_addr;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( slave_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if( ret == ESP_OK) {
return slave_addr;
}
}
return 0;
}
uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg)
{
uint8_t data=0;
esp_err_t ret = ESP_FAIL;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if(ret != ESP_OK) return -1;
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
i2c_master_read_byte(cmd, &data, NACK_VAL);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if(ret != ESP_OK) {
ESP_LOGE(TAG, "SCCB_Read Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret);
}
return data;
}
uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data)
{
esp_err_t ret = ESP_FAIL;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if(ret != ESP_OK) {
ESP_LOGE(TAG, "SCCB_Write Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret);
}
return ret == ESP_OK ? 0 : -1;
}
uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg)
{
uint8_t data=0;
esp_err_t ret = ESP_FAIL;
uint16_t reg_htons = LITTLETOBIG(reg);
uint8_t *reg_u8 = (uint8_t *)&reg_htons;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if(ret != ESP_OK) return -1;
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
i2c_master_read_byte(cmd, &data, NACK_VAL);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if(ret != ESP_OK) {
ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data);
}
return data;
}
uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data)
{
static uint16_t i = 0;
esp_err_t ret = ESP_FAIL;
uint16_t reg_htons = LITTLETOBIG(reg);
uint8_t *reg_u8 = (uint8_t *)&reg_htons;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN);
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if(ret != ESP_OK) {
ESP_LOGE(TAG, "W [%04x]=%02x %d fail\n", reg, data, i++);
}
return ret == ESP_OK ? 0 : -1;
}

View File

@@ -1,53 +0,0 @@
#include <stdio.h>
#include "sensor.h"
const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = {
// The sequence must be consistent with camera_model_t
{CAMERA_OV7725, "OV7725", OV7725_SCCB_ADDR, OV7725_PID, FRAMESIZE_VGA, false},
{CAMERA_OV2640, "OV2640", OV2640_SCCB_ADDR, OV2640_PID, FRAMESIZE_UXGA, true},
{CAMERA_OV3660, "OV3660", OV3660_SCCB_ADDR, OV3660_PID, FRAMESIZE_QXGA, true},
{CAMERA_OV5640, "OV5640", OV5640_SCCB_ADDR, OV5640_PID, FRAMESIZE_QSXGA, true},
{CAMERA_OV7670, "OV7670", OV7670_SCCB_ADDR, OV7670_PID, FRAMESIZE_VGA, false},
{CAMERA_NT99141, "NT99141", NT99141_SCCB_ADDR, NT99141_PID, FRAMESIZE_HD, true},
{CAMERA_GC2145, "GC2145", GC2145_SCCB_ADDR, GC2145_PID, FRAMESIZE_UXGA, false},
{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},
};
const resolution_info_t resolution[FRAMESIZE_INVALID] = {
{ 96, 96, ASPECT_RATIO_1X1 }, /* 96x96 */
{ 160, 120, ASPECT_RATIO_4X3 }, /* QQVGA */
{ 176, 144, ASPECT_RATIO_5X4 }, /* QCIF */
{ 240, 176, ASPECT_RATIO_4X3 }, /* HQVGA */
{ 240, 240, ASPECT_RATIO_1X1 }, /* 240x240 */
{ 320, 240, ASPECT_RATIO_4X3 }, /* QVGA */
{ 400, 296, ASPECT_RATIO_4X3 }, /* CIF */
{ 480, 320, ASPECT_RATIO_3X2 }, /* HVGA */
{ 640, 480, ASPECT_RATIO_4X3 }, /* VGA */
{ 800, 600, ASPECT_RATIO_4X3 }, /* SVGA */
{ 1024, 768, ASPECT_RATIO_4X3 }, /* XGA */
{ 1280, 720, ASPECT_RATIO_16X9 }, /* HD */
{ 1280, 1024, ASPECT_RATIO_5X4 }, /* SXGA */
{ 1600, 1200, ASPECT_RATIO_4X3 }, /* UXGA */
// 3MP Sensors
{ 1920, 1080, ASPECT_RATIO_16X9 }, /* FHD */
{ 720, 1280, ASPECT_RATIO_9X16 }, /* Portrait HD */
{ 864, 1536, ASPECT_RATIO_9X16 }, /* Portrait 3MP */
{ 2048, 1536, ASPECT_RATIO_4X3 }, /* QXGA */
// 5MP Sensors
{ 2560, 1440, ASPECT_RATIO_16X9 }, /* QHD */
{ 2560, 1600, ASPECT_RATIO_16X10 }, /* WQXGA */
{ 1088, 1920, ASPECT_RATIO_9X16 }, /* Portrait FHD */
{ 2560, 1920, ASPECT_RATIO_4X3 }, /* QSXGA */
};
camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id)
{
for (int i = 0; i < CAMERA_MODEL_MAX; i++) {
if (id->PID == camera_sensor[i].pid) {
return (camera_sensor_info_t *)&camera_sensor[i];
}
}
return NULL;
}

View File

@@ -1,9 +0,0 @@
# 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 "../")
add_compile_options(-fdiagnostics-color=always)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(camera_example)

View File

@@ -1,3 +0,0 @@
set(COMPONENT_SRCS take_picture.c)
set(COMPONENT_ADD_INCLUDEDIRS .)
register_component()

View File

@@ -1,5 +0,0 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -1,155 +0,0 @@
/**
* This example takes a picture every 5s and print its size on serial monitor.
*/
// =============================== SETUP ======================================
// 1. Board setup (Uncomment):
// #define BOARD_WROVER_KIT
// #define BOARD_ESP32CAM_AITHINKER
/**
* 2. Kconfig setup
*
* If you have a Kconfig file, copy the content from
* https://github.com/espressif/esp32-camera/blob/master/Kconfig into it.
* In case you haven't, copy and paste this Kconfig file inside the src directory.
* This Kconfig file has definitions that allows more control over the camera and
* how it will be initialized.
*/
/**
* 3. Enable PSRAM on sdkconfig:
*
* CONFIG_ESP32_SPIRAM_SUPPORT=y
*
* More info on
* https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html#config-esp32-spiram-support
*/
// ================================ CODE ======================================
#include <esp_log.h>
#include <esp_system.h>
#include <nvs_flash.h>
#include <sys/param.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_camera.h"
#define BOARD_WROVER_KIT 1
// WROVER-KIT PIN Map
#ifdef BOARD_WROVER_KIT
#define CAM_PIN_PWDN -1 //power down is not used
#define CAM_PIN_RESET -1 //software reset will be performed
#define CAM_PIN_XCLK 21
#define CAM_PIN_SIOD 26
#define CAM_PIN_SIOC 27
#define CAM_PIN_D7 35
#define CAM_PIN_D6 34
#define CAM_PIN_D5 39
#define CAM_PIN_D4 36
#define CAM_PIN_D3 19
#define CAM_PIN_D2 18
#define CAM_PIN_D1 5
#define CAM_PIN_D0 4
#define CAM_PIN_VSYNC 25
#define CAM_PIN_HREF 23
#define CAM_PIN_PCLK 22
#endif
// ESP32Cam (AiThinker) PIN Map
#ifdef BOARD_ESP32CAM_AITHINKER
#define CAM_PIN_PWDN 32
#define CAM_PIN_RESET -1 //software reset will be performed
#define CAM_PIN_XCLK 0
#define CAM_PIN_SIOD 26
#define CAM_PIN_SIOC 27
#define CAM_PIN_D7 35
#define CAM_PIN_D6 34
#define CAM_PIN_D5 39
#define CAM_PIN_D4 36
#define CAM_PIN_D3 21
#define CAM_PIN_D2 19
#define CAM_PIN_D1 18
#define CAM_PIN_D0 5
#define CAM_PIN_VSYNC 25
#define CAM_PIN_HREF 23
#define CAM_PIN_PCLK 22
#endif
static const char *TAG = "example:take_picture";
static camera_config_t camera_config = {
.pin_pwdn = CAM_PIN_PWDN,
.pin_reset = CAM_PIN_RESET,
.pin_xclk = CAM_PIN_XCLK,
.pin_sscb_sda = CAM_PIN_SIOD,
.pin_sscb_scl = CAM_PIN_SIOC,
.pin_d7 = CAM_PIN_D7,
.pin_d6 = CAM_PIN_D6,
.pin_d5 = CAM_PIN_D5,
.pin_d4 = CAM_PIN_D4,
.pin_d3 = CAM_PIN_D3,
.pin_d2 = CAM_PIN_D2,
.pin_d1 = CAM_PIN_D1,
.pin_d0 = CAM_PIN_D0,
.pin_vsync = CAM_PIN_VSYNC,
.pin_href = CAM_PIN_HREF,
.pin_pclk = CAM_PIN_PCLK,
//XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
.xclk_freq_hz = 20000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_RGB565, //YUV422,GRAYSCALE,RGB565,JPEG
.frame_size = FRAMESIZE_QVGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
.jpeg_quality = 12, //0-63 lower number means higher quality
.fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
.grab_mode = CAMERA_GRAB_WHEN_EMPTY,
};
static esp_err_t init_camera()
{
//initialize the camera
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Camera Init Failed");
return err;
}
return ESP_OK;
}
void app_main()
{
if(ESP_OK != init_camera()) {
return;
}
while (1)
{
ESP_LOGI(TAG, "Taking picture...");
camera_fb_t *pic = esp_camera_fb_get();
// use pic->buf to access the image
ESP_LOGI(TAG, "Picture taken! Its size was: %zu bytes", pic->len);
esp_camera_fb_return(pic);
vTaskDelay(5000 / portTICK_RATE_MS);
}
}

View File

@@ -1,17 +0,0 @@
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_PARTITION_TABLE_OFFSET=0x10000
CONFIG_FREERTOS_HZ=1000
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_SPIRAM_SUPPORT=y
CONFIG_ESP32_SPIRAM_SUPPORT=y
CONFIG_ESP32S2_SPIRAM_SUPPORT=y
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
CONFIG_SPIRAM_SPEED_80M=y

View File

@@ -1,5 +0,0 @@
description: ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors.
targets:
- esp32
- esp32s2
- esp32s3

View File

@@ -1,26 +0,0 @@
{
"name": "esp32-camera",
"version": "2.0.0",
"keywords": "esp32, camera, espressif, esp32-cam",
"description": "ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors.",
"repository": {
"type": "git",
"url": "https://github.com/espressif/esp32-camera"
},
"frameworks": "espidf",
"platforms": "*",
"build": {
"flags": [
"-Idriver/include",
"-Iconversions/include",
"-Idriver/private_include",
"-Iconversions/private_include",
"-Isensors/private_include",
"-Itarget/private_include",
"-fno-rtti"
],
"includeDir": ".",
"srcDir": ".",
"srcFilter": ["-<*>", "+<driver>", "+<conversions>", "+<sensors>"]
}
}

View File

@@ -1,541 +0,0 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* BF3005 driver.
*
* Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sccb.h"
#include "xclk.h"
#include "bf3005.h"
#include "bf3005_regs.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.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 = "bf3005";
#endif
static const uint8_t default_regs[][2] = {
{0x12, 0x40}, //soft reset
{0xff, 0xff}, //delay
{0xff, 0xff}, //delay
{0xff, 0xff}, //delay
{0xff, 0xff}, //delay
{0x13, 0x10},
{0x8c, 0x00},
{0x8d, 0x64},
{0x87, 0x10},
{0x13, 0x17},
{0x00, 0x20},
{0x01, 0x1a},
{0x02, 0x22},
{0x09, 0x03},
{0x0c, 0x80},
{0x0d, 0x24},
{0x0e, 0x21},
{0x0f, 0x28},
{0x11, 0x08},
{0x15, 0x10}, // 0X10
{0x16, 0x03},
{0x1e, 0x30},
{0x20, 0x8a},
{0x21, 0x03},
{0x23, 0x55},
{0x24, 0x68},
{0x25, 0x78},
{0x2a, 0x00},
{0x2b, 0x00},
{0x2d, 0x4f},
{0x2e, 0x98},
{0x2f, 0x04},
{0x30, 0xad},
{0x31, 0x17},
{0x32, 0x6e},
{0x33, 0x20},
{0x35, 0xa6},
{0x3b, 0x00},
{0x3e, 0x00},
{0x3f, 0xA8},
{0x40, 0x38},
{0x41, 0x32},
{0x42, 0x2b},
{0x43, 0x26},
{0x44, 0x1a},
{0x45, 0x16},
{0x46, 0x10},
{0x47, 0x0f},
{0x48, 0x0c},
{0x49, 0x0a},
{0x4b, 0x09},
{0x4c, 0x08},
{0x4d, 0x3c},
{0x4e, 0x06},
{0x4f, 0x05},
{0x50, 0x03},
{0x51, 0x25},
{0x52, 0x88},
{0x53, 0x03},
{0x63, 0x20},
{0x64, 0x02},
{0x65, 0xa6},
{0x66, 0xb6},
{0x69, 0x00},
{0x70, 0xFF},
{0x71, 0xa6},
{0x72, 0x2f},
{0x73, 0x2f},
{0x74, 0x2F},
{0x75, 0x0e},
{0x76, 0x1e},
{0x77, 0x00},
{0x78, 0x1e},
{0x79, 0x8a},
{0x7d, 0xe2},
{0x80, 0x44},
{0x81, 0x00},
{0x82, 0x18},
{0x83, 0x1b},
{0x84, 0x24},
{0x85, 0x2a},
{0x86, 0x4f},
{0x89, 0x82}, //0x82
{0x8b, 0x02},
{0x8e, 0x03},
{0x8f, 0xFC},
{0x9d, 0x4d},
{0x9e, 0x41},
{0xa1, 0x21},
{0xa2, 0x12},
{0xa3, 0x32},
{0xa4, 0x05},
{0xa5, 0x32},
{0xa6, 0x04},
{0xa7, 0x7f},
{0xa8, 0x7f},
{0xa9, 0x21},
{0xaa, 0x21},
{0xab, 0x21},
{0xac, 0x0a},
{0xad, 0xf0},
{0xae, 0xff},
{0xaf, 0x1d},
{0xb0, 0x94},
{0xb1, 0xc0},
{0xb2, 0xc0},
{0xd2, 0x30},
{0xe0, 0x0d},
{0xe1, 0x44},
{0xe7, 0x7c},
{0xe8, 0x89},
{0xe9, 0x01},
{0xea, 0x01},
{0xf0, 0x01},
{0xf3, 0x49},
{0xf4, 0xff},
{0xf5, 0x01},
{0xf6, 0xf2},
{0xf7, 0x6f},
{0x1b, 0x80},
{0x00, 0x00},
};
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = SCCB_Read(sensor->slv_addr, reg & 0xFF);
if(ret > 0){
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
ret = SCCB_Read(sensor->slv_addr, reg & 0xFF);
if(ret < 0){
return ret;
}
value = (ret & ~mask) | (value & mask);
ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
return ret;
}
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 get_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length)
{
int ret = 0;
ret = SCCB_Read(sensor->slv_addr, reg);
if(ret < 0){
return ret;
}
uint8_t mask = ((1 << length) - 1) << offset;
return (ret & mask) >> offset;
}
static int reset(sensor_t *sensor)
{
int i=0;
const uint8_t (*regs)[2];
// Write default regsiters
for (i=0, regs = default_regs; regs[i][0]; i++) {
SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
}
// Delay
vTaskDelay(50 / portTICK_PERIOD_MS);
return 0;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret=0;
sensor->pixformat = pixformat;
switch (pixformat) {
case PIXFORMAT_RGB565:
set_reg_bits(sensor, 0x12, 2, 1, 1);
break;
case PIXFORMAT_RAW:
set_reg_bits(sensor, 0x12, 0, 3, 0x4);
break;
case PIXFORMAT_YUV422:
case PIXFORMAT_GRAYSCALE:
set_reg_bits(sensor, 0x12, 2, 1, 0);
break;
default:
return -1;
}
// Delay
vTaskDelay(30 / portTICK_PERIOD_MS);
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;
// uint8_t reg = SCCB_Read(sensor->slv_addr, COM7);
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, 0x03, 0);
printf("%s %d\r\n", __func__, __LINE__);
if((w<=320)&&(h<=240))
{
printf("%s %d\r\n", __func__, __LINE__);
// Enable auto-scaling/zooming factors
//ret |= SCCB_Write(sensor->slv_addr, 0x12, 0x50);
set_reg_bits(sensor, 0x12, 4, 1, 1);
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));
ret |= SCCB_Write(sensor->slv_addr, 0x03, 0);
} else if((w<=640)&&(h<=480))
{
// Enable auto-scaling/zooming factors
//ret |= SCCB_Write(sensor->slv_addr, 0x12, 0x40);
set_reg_bits(sensor, 0x12, 4, 1, 0);
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));
ret |= SCCB_Write(sensor->slv_addr, 0x03, 0);
}
// Delay
vTaskDelay(30 / portTICK_PERIOD_MS);
return ret;
}
static int set_colorbar(sensor_t *sensor, int value)
{
int ret=0;
sensor->status.colorbar = value;
ret |= SCCB_Write(sensor->slv_addr, 0xb9, value);
return ret;
}
static int set_whitebal(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x13, 1, 1, enable) >= 0){
sensor->status.awb = !!enable;
}
return sensor->status.awb;
}
static int set_gain_ctrl(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x13, 2, 1, enable) >= 0){
sensor->status.agc = !!enable;
}
return sensor->status.agc;
}
static int set_exposure_ctrl(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x13, 0, 1, enable) >= 0){
sensor->status.aec = !!enable;
}
return sensor->status.aec;
}
static int set_hmirror(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x1e, 5, 1, enable) >= 0){
sensor->status.hmirror = !!enable;
}
return sensor->status.hmirror;
}
static int set_vflip(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x1e, 4, 1, enable) >= 0){
sensor->status.vflip = !!enable;
}
return sensor->status.vflip;
}
static int set_raw_gma_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = set_reg_bits(sensor, 0xf1, 1, 1, !enable);
if (ret == 0) {
ESP_LOGD(TAG, "Set raw_gma to: %d", !enable);
sensor->status.raw_gma = !enable;
}
return ret;
}
static int set_lenc_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = set_reg_bits(sensor, 0xf1, 0, 1, !enable);
if (ret == 0) {
ESP_LOGD(TAG, "Set lenc to: %d", !enable);
sensor->status.lenc = !enable;
}
return ret;
}
static int set_agc_gain(sensor_t *sensor, int option)
{
int ret = 0;
ret = set_reg_bits(sensor, 0x13, 4, 1, !!option);
if (ret == 0) {
ESP_LOGD(TAG, "Set gain to: %d", !!option);
sensor->status.agc_gain = !!option;
}
return ret;
}
static int set_awb_gain_dsp(sensor_t *sensor, int value)
{
int ret = 0;
ret = SCCB_Write(sensor->slv_addr, 0xa6, value);
if (ret == 0) {
ESP_LOGD(TAG, "Set awb gain threthold to: %d", value);
sensor->status.awb_gain = value;
}
return ret;
}
static int set_brightness(sensor_t *sensor, int level)
{
int ret = 0;
ret = SCCB_Write(sensor->slv_addr, 0x55, level);
if (ret == 0) {
ESP_LOGD(TAG, "Set brightness to: %d", level);
sensor->status.brightness = level;
}
return ret;
}
static int set_contrast(sensor_t *sensor, int level)
{
int ret = 0;
ret = SCCB_Write(sensor->slv_addr, 0x56, level);
if (ret == 0) {
ESP_LOGD(TAG, "Set contrast to: %d", level);
sensor->status.contrast = level;
}
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 init_status(sensor_t *sensor)
{
sensor->status.brightness = SCCB_Read(sensor->slv_addr, 0x55);
sensor->status.contrast = SCCB_Read(sensor->slv_addr, 0x56);
sensor->status.saturation = 0;
sensor->status.ae_level = 0;
sensor->status.gainceiling = SCCB_Read(sensor->slv_addr, 0x87);
sensor->status.awb = get_reg_bits(sensor, 0x13, 1, 1);
sensor->status.awb_gain = SCCB_Read(sensor->slv_addr, 0xa6);
sensor->status.aec = get_reg_bits(sensor, 0x13, 0, 1);
sensor->status.agc = get_reg_bits(sensor, 0x13, 2, 1);
sensor->status.raw_gma = get_reg_bits(sensor, 0xf1, 1, 1);
sensor->status.lenc = get_reg_bits(sensor, 0xf1, 0, 1);
sensor->status.hmirror = get_reg_bits(sensor, 0x1e, 5, 1);
sensor->status.vflip = get_reg_bits(sensor, 0x1e, 4, 1);
sensor->status.colorbar = SCCB_Read(sensor->slv_addr, 0xb9);
sensor->status.sharpness = SCCB_Read(sensor->slv_addr, 0x70);
return 0;
}
static int set_dummy(sensor_t *sensor, int val){ return -1; }
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; }
static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning){return -1;}
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div){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 bf3005_detect(int slv_addr, sensor_id_t *id)
{
if (BF3005_SCCB_ADDR == slv_addr) {
uint16_t PID = SCCB_Read(slv_addr, 0xFC);
if (BF3005_PID == PID) {
id->PID = PID;
id->VER = SCCB_Read(slv_addr, 0xFD);
id->MIDL = SCCB_Read(slv_addr, 0xFC);
id->MIDH = SCCB_Read(slv_addr, 0xFD);
return PID;
} else {
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
}
}
return 0;
}
int bf3005_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_brightness = set_brightness;
sensor->set_contrast = set_contrast;
sensor->set_colorbar = set_colorbar;
sensor->set_gain_ctrl = set_gain_ctrl;
sensor->set_exposure_ctrl = set_exposure_ctrl;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->set_whitebal = set_whitebal;
sensor->set_awb_gain = set_awb_gain_dsp;
sensor->set_agc_gain = set_agc_gain;
sensor->set_raw_gma = set_raw_gma_dsp;
sensor->set_lenc = set_lenc_dsp;
sensor->set_sharpness = set_sharpness;
//not supported
sensor->set_saturation= set_dummy;
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->set_gainceiling = set_gainceiling_dummy;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = set_res_raw;
sensor->set_pll = _set_pll;
sensor->set_xclk = set_xclk;
ESP_LOGD(TAG, "BF3005 Attached");
return 0;
}

View File

@@ -1,467 +0,0 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sccb.h"
#include "gc0308.h"
#include "gc0308_regs.h"
#include "gc0308_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 = "gc0308";
#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);
#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 = 0;
#ifndef REG_DEBUG_ON
ret = SCCB_Write(slv_addr, reg, value);
#else
int old_value = read_reg(slv_addr, reg);
if (old_value < 0) {
return old_value;
}
if ((uint8_t)old_value != value) {
ESP_LOGI(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
ret = SCCB_Write(slv_addr, reg, value);
} else {
ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
ret = SCCB_Write(slv_addr, reg, value);//maybe not?
}
if (ret < 0) {
ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
{
return (read_reg(slv_addr, reg) & mask) == mask;
}
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
{
int ret = 0;
uint8_t c_value, new_value;
ret = read_reg(slv_addr, reg);
if (ret < 0) {
return ret;
}
c_value = ret;
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
ret = write_reg(slv_addr, reg, new_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 void print_regs(uint8_t slv_addr)
{
#ifdef DEBUG_PRINT_REG
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 <= 0xa2; 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));
}
#endif
}
static int reset(sensor_t *sensor)
{
int ret = 0;
// Software Reset: clear all registers and reset them to their default values
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0xf0);
if (ret) {
ESP_LOGE(TAG, "Software Reset FAILED!");
return ret;
}
vTaskDelay(100 / portTICK_PERIOD_MS);
ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs);
if (ret == 0) {
ESP_LOGD(TAG, "Camera defaults loaded");
vTaskDelay(100 / 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
#endif
}
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
switch (pixformat) {
case PIXFORMAT_RGB565:
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = set_reg_bits(sensor->slv_addr, 0x24, 0, 0x0f, 6); //RGB565
break;
case PIXFORMAT_YUV422:
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = set_reg_bits(sensor->slv_addr, 0x24, 0, 0x0f, 2); //yuv422 Y Cb Y Cr
break;
default:
ESP_LOGW(TAG, "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) {
ESP_LOGW(TAG, "Invalid framesize: %u", framesize);
framesize = FRAMESIZE_VGA;
}
sensor->status.framesize = framesize;
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
uint16_t row_s = (resolution[FRAMESIZE_VGA].height - h) / 2;
uint16_t col_s = (resolution[FRAMESIZE_VGA].width - w) / 2;
(void)row_s;
(void)col_s;
#if CONFIG_GC_SENSOR_SUBSAMPLE_MODE
struct subsample_cfg {
uint16_t ratio_numerator;
uint16_t ratio_denominator;
uint8_t reg0x54;
uint8_t reg0x56;
uint8_t reg0x57;
uint8_t reg0x58;
uint8_t reg0x59;
};
const struct subsample_cfg subsample_cfgs[] = { // define some subsample ratio
{84, 420, 0x55, 0x00, 0x00, 0x00, 0x00}, //1/5
{105, 420, 0x44, 0x00, 0x00, 0x00, 0x00},//1/4
{140, 420, 0x33, 0x00, 0x00, 0x00, 0x00},//1/3
{210, 420, 0x22, 0x00, 0x00, 0x00, 0x00},//1/2
{240, 420, 0x77, 0x02, 0x46, 0x02, 0x46},//4/7
{252, 420, 0x55, 0x02, 0x04, 0x02, 0x04},//3/5
{280, 420, 0x33, 0x02, 0x00, 0x02, 0x00},//2/3
{420, 420, 0x11, 0x00, 0x00, 0x00, 0x00},//1/1
};
uint16_t win_w = 640;
uint16_t win_h = 480;
const struct subsample_cfg *cfg = NULL;
/**
* Strategy: try to keep the maximum perspective
*/
for (size_t i = 0; i < sizeof(subsample_cfgs) / sizeof(struct subsample_cfg); i++) {
cfg = &subsample_cfgs[i];
if ((win_w * cfg->ratio_numerator / cfg->ratio_denominator >= w) && (win_h * cfg->ratio_numerator / cfg->ratio_denominator >= h)) {
win_w = w * cfg->ratio_denominator / cfg->ratio_numerator;
win_h = h * cfg->ratio_denominator / cfg->ratio_numerator;
row_s = (resolution[FRAMESIZE_VGA].height - win_h) / 2;
col_s = (resolution[FRAMESIZE_VGA].width - win_w) / 2;
ESP_LOGI(TAG, "subsample win:%dx%d, ratio:%f", win_w, win_h, (float)cfg->ratio_numerator / (float)cfg->ratio_denominator);
break;
}
}
write_reg(sensor->slv_addr, 0xfe, 0x00);
write_reg(sensor->slv_addr, 0x05, H8(row_s));
write_reg(sensor->slv_addr, 0x06, L8(row_s));
write_reg(sensor->slv_addr, 0x07, H8(col_s));
write_reg(sensor->slv_addr, 0x08, L8(col_s));
write_reg(sensor->slv_addr, 0x09, H8(win_h + 8));
write_reg(sensor->slv_addr, 0x0a, L8(win_h + 8));
write_reg(sensor->slv_addr, 0x0b, H8(win_w + 8));
write_reg(sensor->slv_addr, 0x0c, L8(win_w + 8));
write_reg(sensor->slv_addr, 0xfe, 0x01);
set_reg_bits(sensor->slv_addr, 0x53, 7, 0x01, 1);
set_reg_bits(sensor->slv_addr, 0x55, 0, 0x01, 1);
write_reg(sensor->slv_addr, 0x54, cfg->reg0x54);
write_reg(sensor->slv_addr, 0x56, cfg->reg0x56);
write_reg(sensor->slv_addr, 0x57, cfg->reg0x57);
write_reg(sensor->slv_addr, 0x58, cfg->reg0x58);
write_reg(sensor->slv_addr, 0x59, cfg->reg0x59);
write_reg(sensor->slv_addr, 0xfe, 0x00);
#elif CONFIG_GC_SENSOR_WINDOWING_MODE
write_reg(sensor->slv_addr, 0xfe, 0x00);
write_reg(sensor->slv_addr, 0xf7, col_s / 4);
write_reg(sensor->slv_addr, 0xf8, row_s / 4);
write_reg(sensor->slv_addr, 0xf9, (col_s + h) / 4);
write_reg(sensor->slv_addr, 0xfa, (row_s + w) / 4);
write_reg(sensor->slv_addr, 0x05, H8(row_s));
write_reg(sensor->slv_addr, 0x06, L8(row_s));
write_reg(sensor->slv_addr, 0x07, H8(col_s));
write_reg(sensor->slv_addr, 0x08, L8(col_s));
write_reg(sensor->slv_addr, 0x09, H8(h + 8));
write_reg(sensor->slv_addr, 0x0a, L8(h + 8));
write_reg(sensor->slv_addr, 0x0b, H8(w + 8));
write_reg(sensor->slv_addr, 0x0c, L8(w + 8));
#endif
if (ret == 0) {
ESP_LOGD(TAG, "Set framesize to: %ux%u", w, h);
}
return 0;
}
static int set_contrast(sensor_t *sensor, int contrast)
{
if (contrast != 0) {
write_reg(sensor->slv_addr, 0xfe, 0x00);
write_reg(sensor->slv_addr, 0xb3, contrast);
}
return 0;
}
static int set_global_gain(sensor_t *sensor, int gain_level)
{
if (gain_level != 0) {
write_reg(sensor->slv_addr, 0xfe, 0x00);
write_reg(sensor->slv_addr, 0x50, gain_level);
}
return 0;
}
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->slv_addr, 0x14, 0, 0x01, enable != 0);
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->slv_addr, 0x14, 1, 0x01, enable != 0);
if (ret == 0) {
ESP_LOGD(TAG, "Set v-flip to: %d", enable);
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
ret |= set_reg_bits(sensor->slv_addr, 0x2e, 0, 0x01, enable);
if (ret == 0) {
sensor->status.colorbar = enable;
ESP_LOGD(TAG, "Set colorbar to: %d", enable);
}
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 = 0;
sensor->status.contrast = 0;
sensor->status.saturation = 0;
sensor->status.sharpness = 0;
sensor->status.denoise = 0;
sensor->status.ae_level = 0;
sensor->status.gainceiling = 0;
sensor->status.awb = 0;
sensor->status.dcw = 0;
sensor->status.agc = 0;
sensor->status.aec = 0;
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, 0x14, 0x01);
sensor->status.vflip = check_reg_mask(sensor->slv_addr, 0x14, 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;
print_regs(sensor->slv_addr);
return 0;
}
static int set_dummy(sensor_t *sensor, int val)
{
ESP_LOGW(TAG, "Unsupported");
return -1;
}
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
{
ESP_LOGW(TAG, "Unsupported");
return -1;
}
int gc0308_detect(int slv_addr, sensor_id_t *id)
{
if (GC0308_SCCB_ADDR == slv_addr) {
write_reg(slv_addr, 0xfe, 0x00);
uint8_t PID = SCCB_Read(slv_addr, 0x00);
if (GC0308_PID == PID) {
id->PID = PID;
return PID;
} else {
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
}
}
return 0;
}
int gc0308_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_contrast;
sensor->set_brightness = set_dummy;
sensor->set_saturation = set_dummy;
sensor->set_sharpness = set_dummy;
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_global_gain;
sensor->set_exposure_ctrl = set_dummy;
sensor->set_hmirror = set_hmirror;
sensor->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, "GC0308 Attached");
return 0;
}

View File

@@ -1,391 +0,0 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sccb.h"
#include "gc032a.h"
#include "gc032a_regs.h"
#include "gc032a_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 = "gc032a";
#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);
#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 = 0;
#ifndef REG_DEBUG_ON
ret = SCCB_Write(slv_addr, reg, value);
#else
int old_value = read_reg(slv_addr, reg);
if (old_value < 0) {
return old_value;
}
if ((uint8_t)old_value != value) {
ESP_LOGI(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
ret = SCCB_Write(slv_addr, reg, value);
} else {
ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
ret = SCCB_Write(slv_addr, reg, value);//maybe not?
}
if (ret < 0) {
ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
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)
{
#ifdef DEBUG_PRINT_REG
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));
}
#endif
}
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
{
int ret = 0;
uint8_t c_value, new_value;
ret = read_reg(slv_addr, reg);
if (ret < 0) {
return ret;
}
c_value = ret;
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
ret = write_reg(slv_addr, reg, new_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, 0xf0);
if (ret) {
ESP_LOGE(TAG, "Software Reset FAILED!");
return ret;
}
vTaskDelay(100 / portTICK_PERIOD_MS);
ret = write_regs(sensor->slv_addr, gc032a_default_regs);
if (ret == 0) {
ESP_LOGD(TAG, "Camera defaults loaded");
vTaskDelay(100 / portTICK_PERIOD_MS);
write_reg(sensor->slv_addr, 0xfe, 0x00);
set_reg_bits(sensor->slv_addr, 0xf7, 1, 0x01, 1); // PLL_mode1:div2en
set_reg_bits(sensor->slv_addr, 0xf7, 7, 0x01, 1); // PLL_mode1:dvp mode
set_reg_bits(sensor->slv_addr, 0xf8, 0, 0x3f, 8); //PLL_mode2 :divx4
set_reg_bits(sensor->slv_addr, 0xfa, 4, 0x0f, 2); //vlk div mode :divide_by
}
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
switch (pixformat) {
case PIXFORMAT_RGB565:
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = set_reg_bits(sensor->slv_addr, 0x44, 0, 0x1f, 6); //RGB565
break;
case PIXFORMAT_YUV422:
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = set_reg_bits(sensor->slv_addr, 0x44, 0, 0x1f, 3);
break;
default:
ESP_LOGW(TAG, "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)
{
ESP_LOGI(TAG, "set_framesize");
int ret = 0;
if (framesize > FRAMESIZE_VGA) {
ESP_LOGW(TAG, "Invalid framesize: %u", framesize);
framesize = FRAMESIZE_VGA;
}
sensor->status.framesize = framesize;
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
uint16_t row_s = (resolution[FRAMESIZE_VGA].height - h) / 2;
uint16_t col_s = (resolution[FRAMESIZE_VGA].width - w) / 2;
write_reg(sensor->slv_addr, 0xfe, 0x00);
write_reg(sensor->slv_addr, P0_ROW_START_HIGH, H8(row_s)); // Row_start[8]
write_reg(sensor->slv_addr, P0_ROW_START_LOW, L8(row_s)); // Row_start[7:0]
write_reg(sensor->slv_addr, P0_COLUMN_START_HIGH, H8(col_s)); // Column_start[9:8]
write_reg(sensor->slv_addr, P0_COLUMN_START_LOW, L8(col_s)); // Column_start[7:0]
write_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_HIGH, H8(h + 8)); //window_height [8]
write_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_LOW, L8(h + 8)); //window_height [7:0]
write_reg(sensor->slv_addr, P0_WINDOW_WIDTH_HIGH, H8(w + 8)); //window_width [9:8]
write_reg(sensor->slv_addr, P0_WINDOW_WIDTH_LOW, L8(w + 8)); //window_width [7:0]
write_reg(sensor->slv_addr, P0_WIN_MODE, 0x01);
write_reg(sensor->slv_addr, P0_OUT_WIN_HEIGHT_HIGH, H8(h));
write_reg(sensor->slv_addr, P0_OUT_WIN_HEIGHT_LOW, L8(h));
write_reg(sensor->slv_addr, P0_OUT_WIN_WIDTH_HIGH, H8(w));
write_reg(sensor->slv_addr, P0_OUT_WIN_WIDTH_LOW, L8(w));
if (ret == 0) {
ESP_LOGD(TAG, "Set framesize to: %ux%u", w, h);
}
print_regs(sensor->slv_addr);
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->slv_addr, P0_CISCTL_MODE1, 0, 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->slv_addr, P0_CISCTL_MODE1, 1, 0x01, enable);
if (ret == 0) {
ESP_LOGD(TAG, "Set v-flip to: %d", enable);
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
ret |= set_reg_bits(sensor->slv_addr, P0_DEBUG_MODE2, 3, 0x01, enable);
if (ret == 0) {
sensor->status.colorbar = enable;
ESP_LOGD(TAG, "Set colorbar to: %d", enable);
}
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 = 0;
sensor->status.contrast = 0;
sensor->status.saturation = 0;
sensor->status.sharpness = 0;
sensor->status.denoise = 0;
sensor->status.ae_level = 0;
sensor->status.gainceiling = 0;
sensor->status.awb = 0;
sensor->status.dcw = 0;
sensor->status.agc = 0;
sensor->status.aec = 0;
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x01);
sensor->status.vflip = 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, "Unsupported");
return -1;
}
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
{
ESP_LOGW(TAG, "Unsupported");
return -1;
}
int gc032a_detect(int slv_addr, sensor_id_t *id)
{
if (GC032A_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 (GC032A_PID == PID) {
id->PID = PID;
return PID;
} else {
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
}
}
return 0;
}
int gc032a_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_dummy;
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;
sensor->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, "GC032A Attached");
return 0;
}

View File

@@ -1,477 +0,0 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sccb.h"
#include "gc2145.h"
#include "gc2145_regs.h"
#include "gc2145_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 = "gc2145";
#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);
#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 = 0;
#ifndef REG_DEBUG_ON
ret = SCCB_Write(slv_addr, reg, value);
#else
int old_value = read_reg(slv_addr, reg);
if (old_value < 0) {
return old_value;
}
if ((uint8_t)old_value != value) {
ESP_LOGI(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
ret = SCCB_Write(slv_addr, reg, value);
} else {
ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
ret = SCCB_Write(slv_addr, reg, value);//maybe not?
}
if (ret < 0) {
ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
{
return (read_reg(slv_addr, reg) & mask) == mask;
}
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
{
int ret = 0;
uint8_t c_value, new_value;
ret = read_reg(slv_addr, reg);
if (ret < 0) {
return ret;
}
c_value = ret;
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
ret = write_reg(slv_addr, reg, new_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 void print_regs(uint8_t slv_addr)
{
#ifdef DEBUG_PRINT_REG
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 = 0x80; i <= 0xa2; 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));
}
#endif
}
static int reset(sensor_t *sensor)
{
int ret = 0;
// Software Reset: clear all registers and reset them to their default values
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0xe0);
if (ret) {
ESP_LOGE(TAG, "Software Reset FAILED!");
return ret;
}
vTaskDelay(100 / portTICK_PERIOD_MS);
ret = write_regs(sensor->slv_addr, gc2145_default_init_regs);
if (ret == 0) {
ESP_LOGD(TAG, "Camera defaults loaded");
vTaskDelay(100 / portTICK_PERIOD_MS);
#ifdef CONFIG_IDF_TARGET_ESP32
write_reg(sensor->slv_addr, 0xfe, 0x00);
//ensure pclk <= 15MHz for esp32
set_reg_bits(sensor->slv_addr, 0xf8, 0, 0x3f, 2); // divx4
set_reg_bits(sensor->slv_addr, 0xfa, 4, 0x0f, 2); // divide_by
#endif
}
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
switch (pixformat) {
case PIXFORMAT_RGB565:
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = set_reg_bits(sensor->slv_addr, P0_OUTPUT_FORMAT, 0, 0x1f, 6); //RGB565
break;
case PIXFORMAT_YUV422:
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = set_reg_bits(sensor->slv_addr, P0_OUTPUT_FORMAT, 0, 0x1f, 2); //yuv422
break;
default:
ESP_LOGW(TAG, "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_UXGA) {
ESP_LOGW(TAG, "Invalid framesize: %u", framesize);
framesize = FRAMESIZE_UXGA;
}
sensor->status.framesize = framesize;
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
uint16_t row_s = (resolution[FRAMESIZE_UXGA].height - h) / 2;
uint16_t col_s = (resolution[FRAMESIZE_UXGA].width - w) / 2;
(void)row_s;
(void)col_s;
#if CONFIG_GC_SENSOR_SUBSAMPLE_MODE
struct subsample_cfg {
uint16_t ratio_numerator;
uint16_t ratio_denominator;
uint8_t reg0x99;
uint8_t reg0x9b;
uint8_t reg0x9c;
uint8_t reg0x9d;
uint8_t reg0x9e;
uint8_t reg0x9f;
uint8_t reg0xa0;
uint8_t reg0xa1;
uint8_t reg0xa2;
};
const struct subsample_cfg subsample_cfgs[] = { // define some subsample ratio
// {60, 420, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, //1/7 // A smaller ratio brings a larger view, but it reduces the frame rate
// {84, 420, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, //1/5
// {105, 420, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/4
{140, 420, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/3
{210, 420, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/2
{240, 420, 0x77, 0x02, 0x46, 0x02, 0x46, 0x02, 0x46, 0x02, 0x46},//4/7
{252, 420, 0x55, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04},//3/5
{280, 420, 0x33, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00},//2/3
{420, 420, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/1
};
uint16_t win_w = resolution[FRAMESIZE_UXGA].width;
uint16_t win_h = resolution[FRAMESIZE_UXGA].height;
const struct subsample_cfg *cfg = NULL;
/**
* Strategy: try to keep the maximum perspective
*/
uint8_t i = 0;
if (framesize >= FRAMESIZE_QVGA) {
i = 1;
}
for (; i < sizeof(subsample_cfgs) / sizeof(struct subsample_cfg); i++) {
cfg = &subsample_cfgs[i];
if ((win_w * cfg->ratio_numerator / cfg->ratio_denominator >= w) && (win_h * cfg->ratio_numerator / cfg->ratio_denominator >= h)) {
win_w = w * cfg->ratio_denominator / cfg->ratio_numerator;
win_h = h * cfg->ratio_denominator / cfg->ratio_numerator;
row_s = (resolution[FRAMESIZE_UXGA].height - win_h) / 2;
col_s = (resolution[FRAMESIZE_UXGA].width - win_w) / 2;
ESP_LOGI(TAG, "subsample win:%dx%d, ratio:%f", win_w, win_h, (float)cfg->ratio_numerator / (float)cfg->ratio_denominator);
break;
}
}
write_reg(sensor->slv_addr, 0xfe, 0x00);
write_reg(sensor->slv_addr, P0_CROP_ENABLE, 0x01);
write_reg(sensor->slv_addr, 0x09, H8(row_s));
write_reg(sensor->slv_addr, 0x0a, L8(row_s));
write_reg(sensor->slv_addr, 0x0b, H8(col_s));
write_reg(sensor->slv_addr, 0x0c, L8(col_s));
write_reg(sensor->slv_addr, 0x0d, H8(win_h + 8));
write_reg(sensor->slv_addr, 0x0e, L8(win_h + 8));
write_reg(sensor->slv_addr, 0x0f, H8(win_w + 16));
write_reg(sensor->slv_addr, 0x10, L8(win_w + 16));
write_reg(sensor->slv_addr, 0x99, cfg->reg0x99);
write_reg(sensor->slv_addr, 0x9b, cfg->reg0x9b);
write_reg(sensor->slv_addr, 0x9c, cfg->reg0x9c);
write_reg(sensor->slv_addr, 0x9d, cfg->reg0x9d);
write_reg(sensor->slv_addr, 0x9e, cfg->reg0x9e);
write_reg(sensor->slv_addr, 0x9f, cfg->reg0x9f);
write_reg(sensor->slv_addr, 0xa0, cfg->reg0xa0);
write_reg(sensor->slv_addr, 0xa1, cfg->reg0xa1);
write_reg(sensor->slv_addr, 0xa2, cfg->reg0xa2);
write_reg(sensor->slv_addr, 0x95, H8(h));
write_reg(sensor->slv_addr, 0x96, L8(h));
write_reg(sensor->slv_addr, 0x97, H8(w));
write_reg(sensor->slv_addr, 0x98, L8(w));
#elif CONFIG_GC_SENSOR_WINDOWING_MODE
write_reg(sensor->slv_addr, 0xfe, 0x00);
write_reg(sensor->slv_addr, P0_CROP_ENABLE, 0x01);
// write_reg(sensor->slv_addr, 0xec, col_s / 8); //measure window
// write_reg(sensor->slv_addr, 0xed, row_s / 8);
// write_reg(sensor->slv_addr, 0xee, (col_s + h) / 8);
// write_reg(sensor->slv_addr, 0xef, (row_s + w) / 8);
write_reg(sensor->slv_addr, 0x09, H8(row_s));
write_reg(sensor->slv_addr, 0x0a, L8(row_s));
write_reg(sensor->slv_addr, 0x0b, H8(col_s));
write_reg(sensor->slv_addr, 0x0c, L8(col_s));
write_reg(sensor->slv_addr, 0x0d, H8(h + 8));
write_reg(sensor->slv_addr, 0x0e, L8(h + 8));
write_reg(sensor->slv_addr, 0x0f, H8(w + 8));
write_reg(sensor->slv_addr, 0x10, L8(w + 8));
write_reg(sensor->slv_addr, 0x95, H8(h));
write_reg(sensor->slv_addr, 0x96, L8(h));
write_reg(sensor->slv_addr, 0x97, H8(w));
write_reg(sensor->slv_addr, 0x98, L8(w));
#endif
if (ret == 0) {
ESP_LOGD(TAG, "Set framesize to: %ux%u", w, h);
}
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->slv_addr, P0_ANALOG_MODE1, 0, 0x01, enable != 0);
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->slv_addr, P0_ANALOG_MODE1, 1, 0x01, enable != 0);
if (ret == 0) {
ESP_LOGD(TAG, "Set v-flip to: %d", enable);
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret = 0;
// ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
// ret |= set_reg_bits(sensor->slv_addr, P0_DEBUG_MODE3, 3, 0x01, enable);
if (ret == 0) {
sensor->status.colorbar = enable;
ESP_LOGD(TAG, "Set colorbar to: %d", enable);
}
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 = 0;
sensor->status.contrast = 0;
sensor->status.saturation = 0;
sensor->status.sharpness = 0;
sensor->status.denoise = 0;
sensor->status.ae_level = 0;
sensor->status.gainceiling = 0;
sensor->status.awb = 0;
sensor->status.dcw = 0;
sensor->status.agc = 0;
sensor->status.aec = 0;
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, P0_ANALOG_MODE1, 0x01);
sensor->status.vflip = check_reg_mask(sensor->slv_addr, P0_ANALOG_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;
print_regs(sensor->slv_addr);
return 0;
}
static int set_dummy(sensor_t *sensor, int val)
{
ESP_LOGW(TAG, "Unsupported");
return -1;
}
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
{
ESP_LOGW(TAG, "Unsupported");
return -1;
}
int gc2145_detect(int slv_addr, sensor_id_t *id)
{
if (GC2145_SCCB_ADDR == slv_addr) {
uint8_t MIDL = SCCB_Read(slv_addr, CHIP_ID_LOW);
uint8_t MIDH = SCCB_Read(slv_addr, CHIP_ID_HIGH);
uint16_t PID = MIDH << 8 | MIDL;
if (GC2145_PID == PID) {
id->PID = PID;
return PID;
} else {
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
}
}
return 0;
}
int gc2145_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_dummy;
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;
sensor->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, "GC2145 Attached");
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,612 +0,0 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* OV2640 driver.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "sccb.h"
#include "xclk.h"
#include "ov2640.h"
#include "ov2640_regs.h"
#include "ov2640_settings.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.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 = "ov2640";
#endif
static volatile ov2640_bank_t reg_bank = BANK_MAX;
static int set_bank(sensor_t *sensor, ov2640_bank_t bank)
{
int res = 0;
if (bank != reg_bank) {
reg_bank = bank;
res = SCCB_Write(sensor->slv_addr, BANK_SEL, bank);
}
return res;
}
static int write_regs(sensor_t *sensor, const uint8_t (*regs)[2])
{
int i=0, res = 0;
while (regs[i][0]) {
if (regs[i][0] == BANK_SEL) {
res = set_bank(sensor, regs[i][1]);
} else {
res = SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
}
if (res) {
return res;
}
i++;
}
return res;
}
static int write_reg(sensor_t *sensor, ov2640_bank_t bank, uint8_t reg, uint8_t value)
{
int ret = set_bank(sensor, bank);
if(!ret) {
ret = SCCB_Write(sensor->slv_addr, reg, value);
}
return ret;
}
static int set_reg_bits(sensor_t *sensor, uint8_t bank, uint8_t reg, uint8_t offset, uint8_t mask, uint8_t value)
{
int ret = 0;
uint8_t c_value, new_value;
ret = set_bank(sensor, bank);
if(ret) {
return ret;
}
c_value = SCCB_Read(sensor->slv_addr, reg);
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
ret = SCCB_Write(sensor->slv_addr, reg, new_value);
return ret;
}
static int read_reg(sensor_t *sensor, ov2640_bank_t bank, uint8_t reg)
{
if(set_bank(sensor, bank)){
return 0;
}
return SCCB_Read(sensor->slv_addr, reg);
}
static uint8_t get_reg_bits(sensor_t *sensor, uint8_t bank, uint8_t reg, uint8_t offset, uint8_t mask)
{
return (read_reg(sensor, bank, reg) >> offset) & mask;
}
static int write_reg_bits(sensor_t *sensor, uint8_t bank, uint8_t reg, uint8_t mask, int enable)
{
return set_reg_bits(sensor, bank, reg, 0, mask, enable?mask:0);
}
#define WRITE_REGS_OR_RETURN(regs) ret = write_regs(sensor, regs); if(ret){return ret;}
#define WRITE_REG_OR_RETURN(bank, reg, val) ret = write_reg(sensor, bank, reg, val); if(ret){return ret;}
#define SET_REG_BITS_OR_RETURN(bank, reg, offset, mask, val) ret = set_reg_bits(sensor, bank, reg, offset, mask, val); if(ret){return ret;}
static int reset(sensor_t *sensor)
{
int ret = 0;
WRITE_REG_OR_RETURN(BANK_SENSOR, COM7, COM7_SRST);
vTaskDelay(10 / portTICK_PERIOD_MS);
WRITE_REGS_OR_RETURN(ov2640_settings_cif);
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
sensor->pixformat = pixformat;
switch (pixformat) {
case PIXFORMAT_RGB565:
case PIXFORMAT_RGB888:
WRITE_REGS_OR_RETURN(ov2640_settings_rgb565);
break;
case PIXFORMAT_YUV422:
case PIXFORMAT_GRAYSCALE:
WRITE_REGS_OR_RETURN(ov2640_settings_yuv422);
break;
case PIXFORMAT_JPEG:
WRITE_REGS_OR_RETURN(ov2640_settings_jpeg3);
break;
default:
ret = -1;
break;
}
if(!ret) {
vTaskDelay(10 / portTICK_PERIOD_MS);
}
return ret;
}
static int set_window(sensor_t *sensor, ov2640_sensor_mode_t mode, int offset_x, int offset_y, int max_x, int max_y, int w, int h){
int ret = 0;
const uint8_t (*regs)[2];
ov2640_clk_t c;
c.reserved = 0;
max_x /= 4;
max_y /= 4;
w /= 4;
h /= 4;
uint8_t win_regs[][2] = {
{BANK_SEL, BANK_DSP},
{HSIZE, max_x & 0xFF},
{VSIZE, max_y & 0xFF},
{XOFFL, offset_x & 0xFF},
{YOFFL, offset_y & 0xFF},
{VHYX, ((max_y >> 1) & 0X80) | ((offset_y >> 4) & 0X70) | ((max_x >> 5) & 0X08) | ((offset_x >> 8) & 0X07)},
{TEST, (max_x >> 2) & 0X80},
{ZMOW, (w)&0xFF},
{ZMOH, (h)&0xFF},
{ZMHH, ((h>>6)&0x04)|((w>>8)&0x03)},
{0, 0}
};
if (sensor->pixformat == PIXFORMAT_JPEG) {
c.clk_2x = 0;
c.clk_div = 0;
c.pclk_auto = 0;
c.pclk_div = 8;
if(mode == OV2640_MODE_UXGA) {
c.pclk_div = 12;
}
// if (sensor->xclk_freq_hz == 16000000) {
// c.pclk_div = c.pclk_div / 2;
// }
} else {
#if CONFIG_IDF_TARGET_ESP32
c.clk_2x = 0;
#else
c.clk_2x = 1;
#endif
c.clk_div = 7;
c.pclk_auto = 1;
c.pclk_div = 8;
if (mode == OV2640_MODE_CIF) {
c.clk_div = 3;
} else if(mode == OV2640_MODE_UXGA) {
c.pclk_div = 12;
}
}
ESP_LOGI(TAG, "Set PLL: clk_2x: %u, clk_div: %u, pclk_auto: %u, pclk_div: %u", c.clk_2x, c.clk_div, c.pclk_auto, c.pclk_div);
if (mode == OV2640_MODE_CIF) {
regs = ov2640_settings_to_cif;
} else if (mode == OV2640_MODE_SVGA) {
regs = ov2640_settings_to_svga;
} else {
regs = ov2640_settings_to_uxga;
}
WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_BYPAS);
WRITE_REGS_OR_RETURN(regs);
WRITE_REGS_OR_RETURN(win_regs);
WRITE_REG_OR_RETURN(BANK_SENSOR, CLKRC, c.clk);
WRITE_REG_OR_RETURN(BANK_DSP, R_DVP_SP, c.pclk);
WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_EN);
vTaskDelay(10 / portTICK_PERIOD_MS);
//required when changing resolution
set_pixformat(sensor, sensor->pixformat);
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
int ret = 0;
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
aspect_ratio_t ratio = resolution[framesize].aspect_ratio;
uint16_t max_x = ratio_table[ratio].max_x;
uint16_t max_y = ratio_table[ratio].max_y;
uint16_t offset_x = ratio_table[ratio].offset_x;
uint16_t offset_y = ratio_table[ratio].offset_y;
ov2640_sensor_mode_t mode = OV2640_MODE_UXGA;
sensor->status.framesize = framesize;
if (framesize <= FRAMESIZE_CIF) {
mode = OV2640_MODE_CIF;
max_x /= 4;
max_y /= 4;
offset_x /= 4;
offset_y /= 4;
if(max_y > 296){
max_y = 296;
}
} else if (framesize <= FRAMESIZE_SVGA) {
mode = OV2640_MODE_SVGA;
max_x /= 2;
max_y /= 2;
offset_x /= 2;
offset_y /= 2;
}
ret = set_window(sensor, mode, offset_x, offset_y, max_x, max_y, w, h);
return ret;
}
static int set_contrast(sensor_t *sensor, int level)
{
int ret=0;
level += 3;
if (level <= 0 || level > NUM_CONTRAST_LEVELS) {
return -1;
}
sensor->status.contrast = level-3;
for (int i=0; i<7; i++) {
WRITE_REG_OR_RETURN(BANK_DSP, contrast_regs[0][i], contrast_regs[level][i]);
}
return ret;
}
static int set_brightness(sensor_t *sensor, int level)
{
int ret=0;
level += 3;
if (level <= 0 || level > NUM_BRIGHTNESS_LEVELS) {
return -1;
}
sensor->status.brightness = level-3;
for (int i=0; i<5; i++) {
WRITE_REG_OR_RETURN(BANK_DSP, brightness_regs[0][i], brightness_regs[level][i]);
}
return ret;
}
static int set_saturation(sensor_t *sensor, int level)
{
int ret=0;
level += 3;
if (level <= 0 || level > NUM_SATURATION_LEVELS) {
return -1;
}
sensor->status.saturation = level-3;
for (int i=0; i<5; i++) {
WRITE_REG_OR_RETURN(BANK_DSP, saturation_regs[0][i], saturation_regs[level][i]);
}
return ret;
}
static int set_special_effect(sensor_t *sensor, int effect)
{
int ret=0;
effect++;
if (effect <= 0 || effect > NUM_SPECIAL_EFFECTS) {
return -1;
}
sensor->status.special_effect = effect-1;
for (int i=0; i<5; i++) {
WRITE_REG_OR_RETURN(BANK_DSP, special_effects_regs[0][i], special_effects_regs[effect][i]);
}
return ret;
}
static int set_wb_mode(sensor_t *sensor, int mode)
{
int ret=0;
if (mode < 0 || mode > NUM_WB_MODES) {
return -1;
}
sensor->status.wb_mode = mode;
SET_REG_BITS_OR_RETURN(BANK_DSP, 0XC7, 6, 1, mode?1:0);
if(mode) {
for (int i=0; i<3; i++) {
WRITE_REG_OR_RETURN(BANK_DSP, wb_modes_regs[0][i], wb_modes_regs[mode][i]);
}
}
return ret;
}
static int set_ae_level(sensor_t *sensor, int level)
{
int ret=0;
level += 3;
if (level <= 0 || level > NUM_AE_LEVELS) {
return -1;
}
sensor->status.ae_level = level-3;
for (int i=0; i<3; i++) {
WRITE_REG_OR_RETURN(BANK_SENSOR, ae_levels_regs[0][i], ae_levels_regs[level][i]);
}
return ret;
}
static int set_quality(sensor_t *sensor, int quality)
{
if(quality < 0) {
quality = 0;
} else if(quality > 63) {
quality = 63;
}
sensor->status.quality = quality;
return write_reg(sensor, BANK_DSP, QS, quality);
}
static int set_agc_gain(sensor_t *sensor, int gain)
{
if(gain < 0) {
gain = 0;
} else if(gain > 30) {
gain = 30;
}
sensor->status.agc_gain = gain;
return write_reg(sensor, BANK_SENSOR, GAIN, agc_gain_tbl[gain]);
}
static int set_gainceiling_sensor(sensor_t *sensor, gainceiling_t gainceiling)
{
sensor->status.gainceiling = gainceiling;
//return write_reg(sensor, BANK_SENSOR, COM9, COM9_AGC_SET(gainceiling));
return set_reg_bits(sensor, BANK_SENSOR, COM9, 5, 7, gainceiling);
}
static int set_aec_value(sensor_t *sensor, int value)
{
if(value < 0) {
value = 0;
} else if(value > 1200) {
value = 1200;
}
sensor->status.aec_value = value;
return set_reg_bits(sensor, BANK_SENSOR, REG04, 0, 3, value & 0x3)
|| write_reg(sensor, BANK_SENSOR, AEC, (value >> 2) & 0xFF)
|| set_reg_bits(sensor, BANK_SENSOR, REG45, 0, 0x3F, value >> 10);
}
static int set_aec2(sensor_t *sensor, int enable)
{
sensor->status.aec2 = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL0, 6, 1, enable?0:1);
}
static int set_colorbar(sensor_t *sensor, int enable)
{
sensor->status.colorbar = enable;
return write_reg_bits(sensor, BANK_SENSOR, COM7, COM7_COLOR_BAR, enable?1:0);
}
static int set_agc_sensor(sensor_t *sensor, int enable)
{
sensor->status.agc = enable;
return write_reg_bits(sensor, BANK_SENSOR, COM8, COM8_AGC_EN, enable?1:0);
}
static int set_aec_sensor(sensor_t *sensor, int enable)
{
sensor->status.aec = enable;
return write_reg_bits(sensor, BANK_SENSOR, COM8, COM8_AEC_EN, enable?1:0);
}
static int set_hmirror_sensor(sensor_t *sensor, int enable)
{
sensor->status.hmirror = enable;
return write_reg_bits(sensor, BANK_SENSOR, REG04, REG04_HFLIP_IMG, enable?1:0);
}
static int set_vflip_sensor(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.vflip = enable;
ret = write_reg_bits(sensor, BANK_SENSOR, REG04, REG04_VREF_EN, enable?1:0);
return ret & write_reg_bits(sensor, BANK_SENSOR, REG04, REG04_VFLIP_IMG, enable?1:0);
}
static int set_raw_gma_dsp(sensor_t *sensor, int enable)
{
sensor->status.raw_gma = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL1, 5, 1, enable?1:0);
}
static int set_awb_dsp(sensor_t *sensor, int enable)
{
sensor->status.awb = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL1, 3, 1, enable?1:0);
}
static int set_awb_gain_dsp(sensor_t *sensor, int enable)
{
sensor->status.awb_gain = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL1, 2, 1, enable?1:0);
}
static int set_lenc_dsp(sensor_t *sensor, int enable)
{
sensor->status.lenc = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL1, 1, 1, enable?1:0);
}
static int set_dcw_dsp(sensor_t *sensor, int enable)
{
sensor->status.dcw = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL2, 5, 1, enable?1:0);
}
static int set_bpc_dsp(sensor_t *sensor, int enable)
{
sensor->status.bpc = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL3, 7, 1, enable?1:0);
}
static int set_wpc_dsp(sensor_t *sensor, int enable)
{
sensor->status.wpc = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL3, 6, 1, enable?1:0);
}
//unsupported
static int set_sharpness(sensor_t *sensor, int level)
{
return -1;
}
static int set_denoise(sensor_t *sensor, int level)
{
return -1;
}
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = read_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF);
if(ret > 0){
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
ret = read_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF);
if(ret < 0){
return ret;
}
value = (ret & ~mask) | (value & mask);
ret = write_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF, value);
return ret;
}
static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning)
{
return set_window(sensor, (ov2640_sensor_mode_t)startX, offsetX, offsetY, totalX, totalY, outputX, outputY);
}
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div)
{
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;
}
static int init_status(sensor_t *sensor){
sensor->status.brightness = 0;
sensor->status.contrast = 0;
sensor->status.saturation = 0;
sensor->status.ae_level = 0;
sensor->status.special_effect = 0;
sensor->status.wb_mode = 0;
sensor->status.agc_gain = 30;
int agc_gain = read_reg(sensor, BANK_SENSOR, GAIN);
for (int i=0; i<30; i++){
if(agc_gain >= agc_gain_tbl[i] && agc_gain < agc_gain_tbl[i+1]){
sensor->status.agc_gain = i;
break;
}
}
sensor->status.aec_value = ((uint16_t)get_reg_bits(sensor, BANK_SENSOR, REG45, 0, 0x3F) << 10)
| ((uint16_t)read_reg(sensor, BANK_SENSOR, AEC) << 2)
| get_reg_bits(sensor, BANK_SENSOR, REG04, 0, 3);//0 - 1200
sensor->status.quality = read_reg(sensor, BANK_DSP, QS);
sensor->status.gainceiling = get_reg_bits(sensor, BANK_SENSOR, COM9, 5, 7);
sensor->status.awb = get_reg_bits(sensor, BANK_DSP, CTRL1, 3, 1);
sensor->status.awb_gain = get_reg_bits(sensor, BANK_DSP, CTRL1, 2, 1);
sensor->status.aec = get_reg_bits(sensor, BANK_SENSOR, COM8, 0, 1);
sensor->status.aec2 = get_reg_bits(sensor, BANK_DSP, CTRL0, 6, 1);
sensor->status.agc = get_reg_bits(sensor, BANK_SENSOR, COM8, 2, 1);
sensor->status.bpc = get_reg_bits(sensor, BANK_DSP, CTRL3, 7, 1);
sensor->status.wpc = get_reg_bits(sensor, BANK_DSP, CTRL3, 6, 1);
sensor->status.raw_gma = get_reg_bits(sensor, BANK_DSP, CTRL1, 5, 1);
sensor->status.lenc = get_reg_bits(sensor, BANK_DSP, CTRL1, 1, 1);
sensor->status.hmirror = get_reg_bits(sensor, BANK_SENSOR, REG04, 7, 1);
sensor->status.vflip = get_reg_bits(sensor, BANK_SENSOR, REG04, 6, 1);
sensor->status.dcw = get_reg_bits(sensor, BANK_DSP, CTRL2, 5, 1);
sensor->status.colorbar = get_reg_bits(sensor, BANK_SENSOR, COM7, 1, 1);
sensor->status.sharpness = 0;//not supported
sensor->status.denoise = 0;
return 0;
}
int ov2640_detect(int slv_addr, sensor_id_t *id)
{
if (OV2640_SCCB_ADDR == slv_addr) {
SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor
uint16_t PID = SCCB_Read(slv_addr, 0x0A);
if (OV2640_PID == PID) {
id->PID = PID;
id->VER = SCCB_Read(slv_addr, REG_VER);
id->MIDL = SCCB_Read(slv_addr, REG_MIDL);
id->MIDH = SCCB_Read(slv_addr, REG_MIDH);
return PID;
} else {
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
}
}
return 0;
}
int ov2640_init(sensor_t *sensor)
{
sensor->reset = reset;
sensor->init_status = init_status;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_contrast = set_contrast;
sensor->set_brightness= set_brightness;
sensor->set_saturation= set_saturation;
sensor->set_quality = set_quality;
sensor->set_colorbar = set_colorbar;
sensor->set_gainceiling = set_gainceiling_sensor;
sensor->set_gain_ctrl = set_agc_sensor;
sensor->set_exposure_ctrl = set_aec_sensor;
sensor->set_hmirror = set_hmirror_sensor;
sensor->set_vflip = set_vflip_sensor;
sensor->set_whitebal = set_awb_dsp;
sensor->set_aec2 = set_aec2;
sensor->set_aec_value = set_aec_value;
sensor->set_special_effect = set_special_effect;
sensor->set_wb_mode = set_wb_mode;
sensor->set_ae_level = set_ae_level;
sensor->set_dcw = set_dcw_dsp;
sensor->set_bpc = set_bpc_dsp;
sensor->set_wpc = set_wpc_dsp;
sensor->set_awb_gain = set_awb_gain_dsp;
sensor->set_agc_gain = set_agc_gain;
sensor->set_raw_gma = set_raw_gma_dsp;
sensor->set_lenc = set_lenc_dsp;
//not supported
sensor->set_sharpness = set_sharpness;
sensor->set_denoise = set_denoise;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = set_res_raw;
sensor->set_pll = _set_pll;
sensor->set_xclk = set_xclk;
ESP_LOGD(TAG, "OV2640 Attached");
return 0;
}

Some files were not shown because too many files have changed in this diff Show More