From 4905663933793f163ef159822358c3327ab30e42 Mon Sep 17 00:00:00 2001 From: michael Date: Sat, 17 Jan 2026 02:49:32 +0100 Subject: [PATCH] test1 --- .gitmodules | 3 - code/.gitignore | 12 +- code/CMakeLists.txt | 2 +- code/components/esp-protocols | 1 - .../jomjol_configfile/CMakeLists.txt | 7 - .../jomjol_configfile/configFile.cpp | 86 - .../components/jomjol_configfile/configFile.h | 23 - .../jomjol_controlGPIO/CMakeLists.txt | 4 +- .../jomjol_controlGPIO/server_GPIO.cpp | 835 +- .../jomjol_controlGPIO/server_GPIO.h | 79 +- .../jomjol_controlcamera/CMakeLists.txt | 6 +- .../ClassControllCamera.cpp | 867 +- .../ClassControllCamera.h | 98 +- .../jomjol_controlcamera/server_camera.cpp | 308 - .../jomjol_controlcamera/server_camera.h | 15 - .../jomjol_fileserver_ota/CMakeLists.txt | 4 +- .../jomjol_fileserver_ota/{miniz => }/miniz.c | 334 +- .../jomjol_fileserver_ota/{miniz => }/miniz.h | 126 +- .../jomjol_fileserver_ota/miniz/ChangeLog.md | 235 - .../jomjol_fileserver_ota/miniz/LICENSE | 22 - .../miniz/examples/example1.c | 105 - .../miniz/examples/example2.c | 164 - .../miniz/examples/example3.c | 269 - .../miniz/examples/example4.c | 102 - .../miniz/examples/example5.c | 327 - .../miniz/examples/example6.c | 166 - .../jomjol_fileserver_ota/miniz/readme.md | 46 - .../jomjol_fileserver_ota/miniz/readme2.md | 8 - .../jomjol_fileserver_ota/server_file.cpp | 448 +- .../jomjol_fileserver_ota/server_file.h | 9 +- .../jomjol_fileserver_ota/server_help.cpp | 107 +- .../jomjol_fileserver_ota/server_help.h | 8 +- .../jomjol_fileserver_ota/server_ota.cpp | 580 +- .../jomjol_fileserver_ota/server_ota.h | 25 +- .../jomjol_flowcontroll/CMakeLists.txt | 4 +- .../jomjol_flowcontroll/ClassFlow.cpp | 186 +- .../jomjol_flowcontroll/ClassFlow.h | 31 +- .../ClassFlowAlignment.cpp | 438 +- .../jomjol_flowcontroll/ClassFlowAlignment.h | 20 +- .../ClassFlowCNNGeneral.cpp | 1497 ++-- .../jomjol_flowcontroll/ClassFlowCNNGeneral.h | 70 +- .../jomjol_flowcontroll/ClassFlowControll.cpp | 1108 ++- .../jomjol_flowcontroll/ClassFlowControll.h | 92 +- .../ClassFlowDefineTypes.h | 116 +- .../jomjol_flowcontroll/ClassFlowImage.cpp | 138 +- .../jomjol_flowcontroll/ClassFlowImage.h | 17 +- .../jomjol_flowcontroll/ClassFlowInfluxDB.cpp | 262 +- .../jomjol_flowcontroll/ClassFlowInfluxDB.h | 52 +- .../ClassFlowInfluxDBv2.cpp | 329 +- .../jomjol_flowcontroll/ClassFlowInfluxDBv2.h | 61 +- .../jomjol_flowcontroll/ClassFlowMQTT.cpp | 593 +- .../jomjol_flowcontroll/ClassFlowMQTT.h | 89 +- .../ClassFlowPostProcessing.cpp | 1350 +-- .../ClassFlowPostProcessing.h | 76 +- .../ClassFlowTakeImage.cpp | 775 +- .../jomjol_flowcontroll/ClassFlowTakeImage.h | 18 +- .../jomjol_flowcontroll/ClassFlowWebhook.cpp | 182 +- .../jomjol_flowcontroll/ClassFlowWebhook.h | 47 +- .../jomjol_flowcontroll/MainFlowControl.cpp | 862 +- .../jomjol_flowcontroll/MainFlowControl.h | 67 +- code/components/jomjol_helper/CMakeLists.txt | 4 +- code/components/jomjol_helper/Helper.cpp | 1925 +++-- code/components/jomjol_helper/Helper.h | 213 +- code/components/jomjol_helper/esp_sys.cpp | 179 - code/components/jomjol_helper/esp_sys.h | 54 - .../jomjol_helper/himem_memory_check.cpp | 115 - .../jomjol_helper/himem_memory_check.h | 41 - code/components/jomjol_helper/perfmon.c | 87 - code/components/jomjol_helper/perfmon.h | 24 - code/components/jomjol_helper/psram.cpp | 209 +- code/components/jomjol_helper/psram.h | 3 - code/components/jomjol_helper/sdcard_check.h | 2 +- code/components/jomjol_helper/sdcard_init.c | 646 -- code/components/jomjol_helper/sdcard_init.h | 111 - code/components/jomjol_helper/statusled.cpp | 105 +- code/components/jomjol_helper/statusled.h | 17 +- .../jomjol_image_proc/CAlignAndCutImage.cpp | 191 +- .../jomjol_image_proc/CAlignAndCutImage.h | 14 +- .../jomjol_image_proc/CFindTemplate.cpp | 210 +- .../jomjol_image_proc/CFindTemplate.h | 40 +- .../jomjol_image_proc/CImageBasis.cpp | 405 +- .../jomjol_image_proc/CImageBasis.h | 108 +- .../jomjol_image_proc/CMakeLists.txt | 4 +- .../jomjol_image_proc/CRotateImage.cpp | 172 +- .../jomjol_image_proc/CRotateImage.h | 42 +- .../components/jomjol_image_proc/make_stb.cpp | 24 +- .../components/jomjol_influxdb/CMakeLists.txt | 4 +- .../jomjol_influxdb/interface_influxdb.cpp | 189 +- .../jomjol_influxdb/interface_influxdb.h | 48 +- code/components/jomjol_logfile/CMakeLists.txt | 2 +- .../jomjol_logfile/ClassLogFile.cpp | 307 +- code/components/jomjol_logfile/ClassLogFile.h | 10 +- code/components/jomjol_mqtt/CMakeLists.txt | 4 +- .../components/jomjol_mqtt/interface_mqtt.cpp | 596 +- code/components/jomjol_mqtt/interface_mqtt.h | 14 +- code/components/jomjol_mqtt/mqtt_outbox.c | 305 - code/components/jomjol_mqtt/mqtt_outbox.h | 66 - code/components/jomjol_mqtt/server_mqtt.cpp | 410 +- code/components/jomjol_mqtt/server_mqtt.h | 24 +- code/components/jomjol_network/CMakeLists.txt | 7 + .../basic_auth.cpp | 66 +- .../basic_auth.h | 2 +- .../components/jomjol_network/connect_eth.cpp | 420 + code/components/jomjol_network/connect_eth.h | 29 + .../jomjol_network/connect_roaming.cpp | 430 + .../jomjol_network/connect_roaming.h | 43 + .../jomjol_network/connect_wifi_ap.cpp | 167 + .../jomjol_network/connect_wifi_ap.h} | 15 +- .../jomjol_network/connect_wifi_sta.cpp | 515 ++ .../jomjol_network/connect_wifi_sta.h | 28 + .../jomjol_network/network_init.cpp | 272 + code/components/jomjol_network/network_init.h | 25 + .../jomjol_network/read_network_config.cpp | 502 ++ .../jomjol_network/read_network_config.h | 49 + .../jomjol_network/server_camera.cpp | 184 + .../components/jomjol_network/server_camera.h | 11 + .../jomjol_network}/server_main.cpp | 569 +- .../jomjol_network}/server_main.h | 17 +- .../jomjol_network/server_remote.cpp} | 367 +- .../components/jomjol_network/server_remote.h | 25 + .../jomjol_tfliteclass/CMakeLists.txt | 2 +- .../jomjol_tfliteclass/CTfLiteClass.cpp | 466 +- .../jomjol_tfliteclass/CTfLiteClass.h | 60 +- .../jomjol_time_sntp/CMakeLists.txt | 4 +- .../components/jomjol_time_sntp/time_sntp.cpp | 213 +- code/components/jomjol_time_sntp/time_sntp.h | 15 +- code/components/jomjol_webhook/CMakeLists.txt | 4 +- .../jomjol_webhook/interface_webhook.cpp | 96 +- .../jomjol_webhook/interface_webhook.h | 8 +- code/components/jomjol_wlan/CMakeLists.txt | 7 - code/components/jomjol_wlan/connect_wlan.cpp | 720 -- code/components/jomjol_wlan/connect_wlan.h | 25 - code/components/jomjol_wlan/read_wlanini.cpp | 396 - code/components/jomjol_wlan/read_wlanini.h | 28 - code/components/mdns/.cz.yaml | 8 + code/components/mdns/CHANGELOG.md | 493 ++ code/components/mdns/CMakeLists.txt | 38 + code/components/mdns/Kconfig | 148 + code/components/mdns/LICENSE | 202 + code/components/mdns/README.md | 14 + .../examples/query_advertise/CMakeLists.txt | 6 + .../mdns/examples/query_advertise/README.md | 91 + .../query_advertise/main/CMakeLists.txt | 2 + .../query_advertise/main/Kconfig.projbuild | 55 + .../query_advertise/main/idf_component.yml | 8 + .../query_advertise/main/mdns_example_main.c | 421 + .../examples/query_advertise/pytest_mdns.py | 203 + .../sdkconfig.ci.eth_custom_netif | 20 + .../query_advertise/sdkconfig.ci.eth_def | 15 + .../query_advertise/sdkconfig.ci.eth_no_ipv4 | 15 + .../query_advertise/sdkconfig.ci.eth_no_ipv6 | 16 + .../query_advertise/sdkconfig.ci.eth_socket | 16 + code/components/mdns/idf_component.yml | 9 + code/components/mdns/include/mdns.h | 937 ++ code/components/mdns/include/mdns_console.h | 14 + code/components/mdns/mdns.c | 7699 +++++++++++++++++ code/components/mdns/mdns_console.c | 1451 ++++ code/components/mdns/mdns_networking_lwip.c | 397 + code/components/mdns/mdns_networking_socket.c | 498 ++ .../mdns/private_include/mdns_networking.h | 60 + .../mdns/private_include/mdns_private.h | 468 + .../mdns/tests/host_test/CMakeLists.txt | 16 + .../components/mdns/tests/host_test/README.md | 33 + .../components/esp_netif_linux/CMakeLists.txt | 8 + .../components/esp_netif_linux/Kconfig | 15 + .../esp_netif_linux/esp_netif_linux.c | 197 + .../esp_netif_linux/include/esp_wifi_types.h | 7 + .../esp_netif_linux/include/machine/endian.h | 8 + .../mdns/tests/host_test/dnsfixture.py | 130 + .../mdns/tests/host_test/main/CMakeLists.txt | 4 + .../tests/host_test/main/Kconfig.projbuild | 21 + .../tests/host_test/main/idf_component.yml | 7 + .../mdns/tests/host_test/main/main.c | 126 + .../mdns/tests/host_test/pytest_mdns.py | 180 + .../mdns/tests/host_test/sdkconfig.ci.app | 4 + .../mdns/tests/host_test/sdkconfig.ci.console | 4 + .../mdns/tests/host_test/sdkconfig.ci.target | 1 + .../mdns/tests/host_test/sdkconfig.defaults | 6 + .../tests/test_afl_fuzz_host/CMakeLists.txt | 7 + .../mdns/tests/test_afl_fuzz_host/README.md | 80 + .../tests/test_afl_fuzz_host/esp32_mock.c | 127 + .../tests/test_afl_fuzz_host/esp32_mock.h | 136 + .../mdns/tests/test_afl_fuzz_host/esp_attr.h | 14 + .../tests/test_afl_fuzz_host/esp_netif_mock.c | 55 + .../tests/test_afl_fuzz_host/in/file2.bin | Bin 0 -> 33 bytes .../test_afl_fuzz_host/in/minif_4a_txt.bin | Bin 0 -> 39 bytes .../test_afl_fuzz_host/in/minif_aaaa.bin | Bin 0 -> 33 bytes .../tests/test_afl_fuzz_host/in/minif_any.bin | Bin 0 -> 109 bytes .../test_afl_fuzz_host/in/minif_disc.bin | Bin 0 -> 65 bytes .../tests/test_afl_fuzz_host/in/minif_ptr.bin | Bin 0 -> 36 bytes .../test_afl_fuzz_host/in/minif_query.bin | Bin 0 -> 77 bytes .../test_afl_fuzz_host/in/minif_query2.bin | Bin 0 -> 64 bytes .../test_afl_fuzz_host/in/sub_fritz_m.bin | Bin 0 -> 72 bytes .../test_afl_fuzz_host/in/telnet_ptr.bin | Bin 0 -> 72 bytes .../tests/test_afl_fuzz_host/in/test-14.bin | Bin 0 -> 568 bytes .../tests/test_afl_fuzz_host/in/test-15.bin | Bin 0 -> 524 bytes .../tests/test_afl_fuzz_host/in/test-16.bin | Bin 0 -> 254 bytes .../tests/test_afl_fuzz_host/in/test-28.bin | Bin 0 -> 62 bytes .../tests/test_afl_fuzz_host/in/test-29.bin | Bin 0 -> 39 bytes .../tests/test_afl_fuzz_host/in/test-31.bin | Bin 0 -> 91 bytes .../tests/test_afl_fuzz_host/in/test-53.bin | Bin 0 -> 140 bytes .../tests/test_afl_fuzz_host/in/test-56.bin | Bin 0 -> 262 bytes .../tests/test_afl_fuzz_host/in/test-63.bin | Bin 0 -> 147 bytes .../tests/test_afl_fuzz_host/in/test-83.bin | Bin 0 -> 105 bytes .../tests/test_afl_fuzz_host/in/test-88.bin | Bin 0 -> 48 bytes .../tests/test_afl_fuzz_host/in/test-89.bin | Bin 0 -> 459 bytes .../tests/test_afl_fuzz_host/in/test-95.bin | Bin 0 -> 286 bytes .../tests/test_afl_fuzz_host/in/test-96.bin | Bin 0 -> 319 bytes .../test_afl_fuzz_host/input_packets.txt | 166 + .../mdns/tests/test_afl_fuzz_host/mdns_di.h | 61 + .../mdns/tests/test_afl_fuzz_host/mdns_mock.h | 31 + .../mdns/tests/test_afl_fuzz_host/sdkconfig.h | 424 + .../mdns/tests/test_afl_fuzz_host/test.c | 276 + .../mdns/tests/test_apps/CMakeLists.txt | 7 + .../components/mdns/tests/test_apps/README.md | 49 + .../mdns/tests/test_apps/main/CMakeLists.txt | 2 + .../tests/test_apps/main/Kconfig.projbuild | 40 + .../tests/test_apps/main/idf_component.yml | 8 + .../mdns/tests/test_apps/main/main.c | 117 + .../mdns/tests/test_apps/main/mdns_test.c | 189 + .../mdns/tests/test_apps/pytest_mdns_app.py | 320 + .../mdns/tests/test_apps/sdkconfig.ci | 13 + .../mdns/tests/test_apps/sdkconfig.defaults | 2 + .../mdns/tests/unit_test/CMakeLists.txt | 7 + .../mdns/tests/unit_test/main/CMakeLists.txt | 5 + .../mdns/tests/unit_test/main/test_mdns.c | 299 + .../mdns/tests/unit_test/pytest_app_mdns.py | 8 + .../mdns/tests/unit_test/sdkconfig.ci | 3 + .../mdns/tests/unit_test/sdkconfig.defaults | 2 + code/components/openmetrics/CMakeLists.txt | 4 +- code/components/openmetrics/openmetrics.cpp | 35 +- code/dependencies.lock | 16 +- code/include/defines.h | 529 +- code/main/CMakeLists.txt | 5 +- code/main/main.cpp | 1184 +-- code/main/version.h | 4 +- code/partitions.csv | 8 - code/partitions_custom_16mb.csv | 9 + code/partitions_custom_4mb.csv | 8 + code/partitions_custom_8mb.csv | 9 + code/platformio.ini | 256 +- code/sdkconfig.defaults | 113 +- code/sdkconfig.esp32cam-board-rev3.defaults | 7 - code/sdkconfig.esp32cam-cpu-freq-240.defaults | 4 - code/sdkconfig.esp32cam-debug.defaults | 33 - code/sdkconfig.esp32cam-dev-himem.defaults | 34 - ...config.esp32cam-dev-task-analysis.defaults | 18 - code/sdkconfig.esp32cam-dev.defaults | 36 - code/sdkconfig.esp32cam.defaults | 175 + code/sdkconfig.esp32s3-eth-v1.defaults | 248 + code/sdkconfig.esp32s3-eth-v2.defaults | 248 + code/sdkconfig.freenove-esp32s3.defaults | 230 + code/sdkconfig.wrover-kit-esp32.defaults | 177 + param-docs/expert-params.txt | 21 +- .../generate-template-param-doc-pages.py | 4 - .../Alignment/SearchMaxAngle.md | 14 + .../NUMBER.CheckDigitIncreaseConsistency.md | 11 - .../PostProcessing/NUMBER.MaxFlowRate.md | 13 + .../TakeImage/CamXclkFreqMhz.md | 13 + .../parameter-pages/TakeImage/SaveAllFiles.md | 7 + sd-card/config/config.ini | 27 +- sd-card/html/Flowstate_at_work.jpg | Bin 0 -> 48431 bytes sd-card/html/common.js | 11 +- sd-card/html/edit_alignment.html | 4 +- sd-card/html/edit_analog.html | 3 +- sd-card/html/edit_config_template.html | 350 +- sd-card/html/edit_explain_7_abort.html | 1 - sd-card/html/edit_reference.html | 3 +- sd-card/html/firework.js | 2 +- sd-card/html/graph.html | 2 + sd-card/html/img/ChangeRateThreshold.png.gz | Bin 38912 -> 0 bytes sd-card/html/index.html | 244 +- sd-card/html/info.html | 2 +- sd-card/html/log.html | 1 + sd-card/html/ota_page.html | 2 +- sd-card/html/overview.html | 6 +- sd-card/html/prevalue_set.html | 2 +- sd-card/html/readconfigcommon.js | 203 +- sd-card/html/readconfigparam.js | 222 +- sd-card/html/reboot_page.html | 5 +- sd-card/html/setup.html | 4 +- sd-card/html/wlan_config.html | 115 - .../generate-param-doc-tooltips.py | 10 +- 283 files changed, 32074 insertions(+), 15759 deletions(-) delete mode 160000 code/components/esp-protocols delete mode 100644 code/components/jomjol_configfile/CMakeLists.txt delete mode 100644 code/components/jomjol_configfile/configFile.cpp delete mode 100644 code/components/jomjol_configfile/configFile.h delete mode 100644 code/components/jomjol_controlcamera/server_camera.cpp delete mode 100644 code/components/jomjol_controlcamera/server_camera.h rename code/components/jomjol_fileserver_ota/{miniz => }/miniz.c (96%) rename code/components/jomjol_fileserver_ota/{miniz => }/miniz.h (96%) delete mode 100644 code/components/jomjol_fileserver_ota/miniz/ChangeLog.md delete mode 100644 code/components/jomjol_fileserver_ota/miniz/LICENSE delete mode 100644 code/components/jomjol_fileserver_ota/miniz/examples/example1.c delete mode 100644 code/components/jomjol_fileserver_ota/miniz/examples/example2.c delete mode 100644 code/components/jomjol_fileserver_ota/miniz/examples/example3.c delete mode 100644 code/components/jomjol_fileserver_ota/miniz/examples/example4.c delete mode 100644 code/components/jomjol_fileserver_ota/miniz/examples/example5.c delete mode 100644 code/components/jomjol_fileserver_ota/miniz/examples/example6.c delete mode 100644 code/components/jomjol_fileserver_ota/miniz/readme.md delete mode 100644 code/components/jomjol_fileserver_ota/miniz/readme2.md delete mode 100644 code/components/jomjol_helper/esp_sys.cpp delete mode 100644 code/components/jomjol_helper/esp_sys.h delete mode 100644 code/components/jomjol_helper/himem_memory_check.cpp delete mode 100644 code/components/jomjol_helper/himem_memory_check.h delete mode 100644 code/components/jomjol_helper/perfmon.c delete mode 100644 code/components/jomjol_helper/perfmon.h delete mode 100644 code/components/jomjol_helper/sdcard_init.c delete mode 100644 code/components/jomjol_helper/sdcard_init.h delete mode 100644 code/components/jomjol_mqtt/mqtt_outbox.c delete mode 100644 code/components/jomjol_mqtt/mqtt_outbox.h create mode 100644 code/components/jomjol_network/CMakeLists.txt rename code/components/{jomjol_wlan => jomjol_network}/basic_auth.cpp (75%) rename code/components/{jomjol_wlan => jomjol_network}/basic_auth.h (59%) create mode 100644 code/components/jomjol_network/connect_eth.cpp create mode 100644 code/components/jomjol_network/connect_eth.h create mode 100644 code/components/jomjol_network/connect_roaming.cpp create mode 100644 code/components/jomjol_network/connect_roaming.h create mode 100644 code/components/jomjol_network/connect_wifi_ap.cpp rename code/{main/softAP.h => components/jomjol_network/connect_wifi_ap.h} (56%) create mode 100644 code/components/jomjol_network/connect_wifi_sta.cpp create mode 100644 code/components/jomjol_network/connect_wifi_sta.h create mode 100644 code/components/jomjol_network/network_init.cpp create mode 100644 code/components/jomjol_network/network_init.h create mode 100644 code/components/jomjol_network/read_network_config.cpp create mode 100644 code/components/jomjol_network/read_network_config.h create mode 100644 code/components/jomjol_network/server_camera.cpp create mode 100644 code/components/jomjol_network/server_camera.h rename code/{main => components/jomjol_network}/server_main.cpp (53%) rename code/{main => components/jomjol_network}/server_main.h (53%) rename code/{main/softAP.cpp => components/jomjol_network/server_remote.cpp} (58%) create mode 100644 code/components/jomjol_network/server_remote.h delete mode 100644 code/components/jomjol_wlan/CMakeLists.txt delete mode 100644 code/components/jomjol_wlan/connect_wlan.cpp delete mode 100644 code/components/jomjol_wlan/connect_wlan.h delete mode 100644 code/components/jomjol_wlan/read_wlanini.cpp delete mode 100644 code/components/jomjol_wlan/read_wlanini.h create mode 100644 code/components/mdns/.cz.yaml create mode 100644 code/components/mdns/CHANGELOG.md create mode 100644 code/components/mdns/CMakeLists.txt create mode 100644 code/components/mdns/Kconfig create mode 100644 code/components/mdns/LICENSE create mode 100644 code/components/mdns/README.md create mode 100644 code/components/mdns/examples/query_advertise/CMakeLists.txt create mode 100644 code/components/mdns/examples/query_advertise/README.md create mode 100644 code/components/mdns/examples/query_advertise/main/CMakeLists.txt create mode 100644 code/components/mdns/examples/query_advertise/main/Kconfig.projbuild create mode 100644 code/components/mdns/examples/query_advertise/main/idf_component.yml create mode 100644 code/components/mdns/examples/query_advertise/main/mdns_example_main.c create mode 100644 code/components/mdns/examples/query_advertise/pytest_mdns.py create mode 100644 code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_custom_netif create mode 100644 code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_def create mode 100644 code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_no_ipv4 create mode 100644 code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_no_ipv6 create mode 100644 code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_socket create mode 100644 code/components/mdns/idf_component.yml create mode 100644 code/components/mdns/include/mdns.h create mode 100644 code/components/mdns/include/mdns_console.h create mode 100644 code/components/mdns/mdns.c create mode 100644 code/components/mdns/mdns_console.c create mode 100644 code/components/mdns/mdns_networking_lwip.c create mode 100644 code/components/mdns/mdns_networking_socket.c create mode 100644 code/components/mdns/private_include/mdns_networking.h create mode 100644 code/components/mdns/private_include/mdns_private.h create mode 100644 code/components/mdns/tests/host_test/CMakeLists.txt create mode 100644 code/components/mdns/tests/host_test/README.md create mode 100644 code/components/mdns/tests/host_test/components/esp_netif_linux/CMakeLists.txt create mode 100644 code/components/mdns/tests/host_test/components/esp_netif_linux/Kconfig create mode 100644 code/components/mdns/tests/host_test/components/esp_netif_linux/esp_netif_linux.c create mode 100644 code/components/mdns/tests/host_test/components/esp_netif_linux/include/esp_wifi_types.h create mode 100644 code/components/mdns/tests/host_test/components/esp_netif_linux/include/machine/endian.h create mode 100644 code/components/mdns/tests/host_test/dnsfixture.py create mode 100644 code/components/mdns/tests/host_test/main/CMakeLists.txt create mode 100644 code/components/mdns/tests/host_test/main/Kconfig.projbuild create mode 100644 code/components/mdns/tests/host_test/main/idf_component.yml create mode 100644 code/components/mdns/tests/host_test/main/main.c create mode 100644 code/components/mdns/tests/host_test/pytest_mdns.py create mode 100644 code/components/mdns/tests/host_test/sdkconfig.ci.app create mode 100644 code/components/mdns/tests/host_test/sdkconfig.ci.console create mode 100644 code/components/mdns/tests/host_test/sdkconfig.ci.target create mode 100644 code/components/mdns/tests/host_test/sdkconfig.defaults create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/CMakeLists.txt create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/README.md create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/esp32_mock.c create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/esp32_mock.h create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/esp_attr.h create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/esp_netif_mock.c create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/file2.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/minif_4a_txt.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/minif_aaaa.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/minif_any.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/minif_disc.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/minif_ptr.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/minif_query.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/minif_query2.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/sub_fritz_m.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/telnet_ptr.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-14.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-15.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-16.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-28.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-29.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-31.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-53.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-56.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-63.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-83.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-88.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-89.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-95.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/in/test-96.bin create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/input_packets.txt create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/mdns_di.h create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/mdns_mock.h create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/sdkconfig.h create mode 100644 code/components/mdns/tests/test_afl_fuzz_host/test.c create mode 100644 code/components/mdns/tests/test_apps/CMakeLists.txt create mode 100644 code/components/mdns/tests/test_apps/README.md create mode 100644 code/components/mdns/tests/test_apps/main/CMakeLists.txt create mode 100644 code/components/mdns/tests/test_apps/main/Kconfig.projbuild create mode 100644 code/components/mdns/tests/test_apps/main/idf_component.yml create mode 100644 code/components/mdns/tests/test_apps/main/main.c create mode 100644 code/components/mdns/tests/test_apps/main/mdns_test.c create mode 100644 code/components/mdns/tests/test_apps/pytest_mdns_app.py create mode 100644 code/components/mdns/tests/test_apps/sdkconfig.ci create mode 100644 code/components/mdns/tests/test_apps/sdkconfig.defaults create mode 100644 code/components/mdns/tests/unit_test/CMakeLists.txt create mode 100644 code/components/mdns/tests/unit_test/main/CMakeLists.txt create mode 100644 code/components/mdns/tests/unit_test/main/test_mdns.c create mode 100644 code/components/mdns/tests/unit_test/pytest_app_mdns.py create mode 100644 code/components/mdns/tests/unit_test/sdkconfig.ci create mode 100644 code/components/mdns/tests/unit_test/sdkconfig.defaults delete mode 100644 code/partitions.csv create mode 100644 code/partitions_custom_16mb.csv create mode 100644 code/partitions_custom_4mb.csv create mode 100644 code/partitions_custom_8mb.csv delete mode 100644 code/sdkconfig.esp32cam-board-rev3.defaults delete mode 100644 code/sdkconfig.esp32cam-cpu-freq-240.defaults delete mode 100644 code/sdkconfig.esp32cam-debug.defaults delete mode 100644 code/sdkconfig.esp32cam-dev-himem.defaults delete mode 100644 code/sdkconfig.esp32cam-dev-task-analysis.defaults delete mode 100644 code/sdkconfig.esp32cam-dev.defaults create mode 100644 code/sdkconfig.esp32cam.defaults create mode 100644 code/sdkconfig.esp32s3-eth-v1.defaults create mode 100644 code/sdkconfig.esp32s3-eth-v2.defaults create mode 100644 code/sdkconfig.freenove-esp32s3.defaults create mode 100644 code/sdkconfig.wrover-kit-esp32.defaults create mode 100644 param-docs/parameter-pages/Alignment/SearchMaxAngle.md delete mode 100644 param-docs/parameter-pages/PostProcessing/NUMBER.CheckDigitIncreaseConsistency.md create mode 100644 param-docs/parameter-pages/PostProcessing/NUMBER.MaxFlowRate.md create mode 100644 param-docs/parameter-pages/TakeImage/CamXclkFreqMhz.md create mode 100644 param-docs/parameter-pages/TakeImage/SaveAllFiles.md create mode 100644 sd-card/html/Flowstate_at_work.jpg delete mode 100644 sd-card/html/img/ChangeRateThreshold.png.gz delete mode 100644 sd-card/html/wlan_config.html diff --git a/.gitmodules b/.gitmodules index 595462aa..9e869709 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,3 @@ [submodule "code/components/stb"] path = code/components/stb url = https://github.com/nothings/stb.git -[submodule "code/components/esp-protocols"] - path = code/components/esp-protocols - url = https://github.com/espressif/esp-protocols.git diff --git a/code/.gitignore b/code/.gitignore index 34589556..e2e03358 100644 --- a/code/.gitignore +++ b/code/.gitignore @@ -6,11 +6,7 @@ .vscode/ipch version.cpp sdkconfig.esp32cam -sdkconfig.esp32cam-dev -sdkconfig.esp32cam-debug -sdkconfig.esp32cam-board-rev3 -sdkconfig.esp32cam-cpu-freq-240 -sdkconfig.esp32cam-board-rev3-cpu-freq-240 -sdkconfig.esp32cam-dev-himem -sdkconfig.esp32cam-dev-task-analysis -sdkconfig.esp32cam-no-softap +sdkconfig.wrover-kit-esp32 +sdkconfig.freenove-esp32s3 +sdkconfig.esp32s3-eth-v1 +sdkconfig.esp32s3-eth-v2 diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index bd58e8ef..0ec36fe9 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.0) -list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/esp-tflite-micro components/esp-protocols/components/mdns) +list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/esp-tflite-micro) ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp diff --git a/code/components/esp-protocols b/code/components/esp-protocols deleted file mode 160000 index 9b74256b..00000000 --- a/code/components/esp-protocols +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9b74256b518b009cd94c104e952af37bb79be273 diff --git a/code/components/jomjol_configfile/CMakeLists.txt b/code/components/jomjol_configfile/CMakeLists.txt deleted file mode 100644 index dc5e71d5..00000000 --- a/code/components/jomjol_configfile/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) - -idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." - REQUIRES jomjol_logfile) - - diff --git a/code/components/jomjol_configfile/configFile.cpp b/code/components/jomjol_configfile/configFile.cpp deleted file mode 100644 index de7c29d7..00000000 --- a/code/components/jomjol_configfile/configFile.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" - -#include "Helper.h" -#include "configFile.h" -#include - -#include "../../include/defines.h" - -static const char *TAG = "CONFIG"; - -ConfigFile::ConfigFile(std::string filePath) -{ - std::string config = FormatFileName(filePath); - pFile = fopen(config.c_str(), "r"); -} - -ConfigFile::~ConfigFile() -{ - fclose(pFile); -} - -bool ConfigFile::isNewParagraph(std::string input) -{ - if ((input[0] == '[') || ((input[0] == ';') && (input[1] == '['))) - { - return true; - } - return false; -} - -bool ConfigFile::GetNextParagraph(std::string& aktparamgraph, bool &disabled, bool &eof) -{ - while (getNextLine(&aktparamgraph, disabled, eof) && !isNewParagraph(aktparamgraph)); - - if (isNewParagraph(aktparamgraph)) - return true; - return false; -} - -bool ConfigFile::getNextLine(std::string *rt, bool &disabled, bool &eof) -{ - eof = false; - char zw[1024] = ""; - if (pFile == NULL) - { - *rt = ""; - return false; - } - - if (fgets(zw, 1024, pFile)) - { - ESP_LOGD(TAG, "%s", zw); - if ((strlen(zw) == 0) && feof(pFile)) - { - *rt = ""; - eof = true; - return false; - } - } - else - { - *rt = ""; - eof = true; - return false; - } - *rt = zw; - *rt = trim(*rt); - while ((zw[0] == ';' || zw[0] == '#' || (rt->size() == 0)) && !(zw[1] == '[')) - { - fgets(zw, 1024, pFile); - ESP_LOGD(TAG, "%s", zw); - if (feof(pFile)) - { - *rt = ""; - eof = true; - return false; - } - *rt = zw; - *rt = trim(*rt); - } - - disabled = ((*rt)[0] == ';'); - return true; -} diff --git a/code/components/jomjol_configfile/configFile.h b/code/components/jomjol_configfile/configFile.h deleted file mode 100644 index 41310077..00000000 --- a/code/components/jomjol_configfile/configFile.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#ifndef CONFIGFILE_H -#define CONFIGFILE_H - -#include -#include - -class ConfigFile { -public: - ConfigFile(std::string filePath); - ~ConfigFile(); - - bool isNewParagraph(std::string input); - bool GetNextParagraph(std::string& aktparamgraph, bool &disabled, bool &eof); - bool getNextLine(std::string* rt, bool &disabled, bool &eof); - bool ConfigFileExists(){return pFile;}; - -private: - FILE* pFile; -}; - -#endif //CONFIGFILE_H \ No newline at end of file diff --git a/code/components/jomjol_controlGPIO/CMakeLists.txt b/code/components/jomjol_controlGPIO/CMakeLists.txt index b287de81..e76b4635 100644 --- a/code/components/jomjol_controlGPIO/CMakeLists.txt +++ b/code/components/jomjol_controlGPIO/CMakeLists.txt @@ -1,9 +1,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) -list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) - idf_component_register(SRCS ${app_sources} INCLUDE_DIRS "." "../../include" - REQUIRES esp_http_server jomjol_logfile jomjol_configfile jomjol_mqtt jomjol_flowcontroll) + REQUIRES esp_http_server jomjol_logfile jomjol_mqtt jomjol_flowcontroll jomjol_helper) diff --git a/code/components/jomjol_controlGPIO/server_GPIO.cpp b/code/components/jomjol_controlGPIO/server_GPIO.cpp index f86efe93..19512f0e 100644 --- a/code/components/jomjol_controlGPIO/server_GPIO.cpp +++ b/code/components/jomjol_controlGPIO/server_GPIO.cpp @@ -1,215 +1,236 @@ #include -#include -#include "string.h" - #include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_system.h" +#include -#include "esp_log.h" +#include +#include +#include + +#include #include #include -#include "../../include/defines.h" +#include "defines.h" +#include "Helper.h" #include "server_GPIO.h" +#include "ClassFlow.h" #include "ClassLogFile.h" -#include "configFile.h" -#include "Helper.h" -#ifdef ENABLE_MQTT #include "interface_mqtt.h" #include "server_mqtt.h" -#endif //ENABLE_MQTT #include "basic_auth.h" static const char *TAG = "GPIO"; QueueHandle_t gpio_queue_handle = NULL; -GpioPin::GpioPin(gpio_num_t gpio, const char* name, gpio_pin_mode_t mode, gpio_int_type_t interruptType, uint8_t dutyResolution, std::string mqttTopic, bool httpEnable) +GpioPin::GpioPin(gpio_num_t gpio, const char *name, gpio_pin_mode_t mode, gpio_int_type_t interruptType, uint8_t dutyResolution, std::string mqttTopic, bool httpEnable) { _gpio = gpio; - _name = name; + _name = name; _mode = mode; - _interruptType = interruptType; + _interruptType = interruptType; _mqttTopic = mqttTopic; } GpioPin::~GpioPin() { - ESP_LOGD(TAG,"reset GPIO pin %d", _gpio); - if (_interruptType != GPIO_INTR_DISABLE) { - //hook isr handler for specific gpio pin + ESP_LOGD(TAG, "reset GPIO pin %d", _gpio); + if (_interruptType != GPIO_INTR_DISABLE) + { + // hook isr handler for specific gpio pin gpio_isr_handler_remove(_gpio); } gpio_reset_pin(_gpio); } -static void IRAM_ATTR gpio_isr_handler(void* arg) +static void IRAM_ATTR gpio_isr_handler(void *arg) { GpioResult gpioResult; - gpioResult.gpio = *(gpio_num_t*) arg; + gpioResult.gpio = *(gpio_num_t *)arg; gpioResult.value = gpio_get_level(gpioResult.gpio); BaseType_t ContextSwitchRequest = pdFALSE; - - xQueueSendToBackFromISR(gpio_queue_handle,(void*)&gpioResult,&ContextSwitchRequest); - - if(ContextSwitchRequest){ + + xQueueSendToBackFromISR(gpio_queue_handle, (void *)&gpioResult, &ContextSwitchRequest); + + if (ContextSwitchRequest) + { taskYIELD(); } } -static void gpioHandlerTask(void *arg) { - ESP_LOGD(TAG,"start interrupt task"); - while(1){ - if(uxQueueMessagesWaiting(gpio_queue_handle)){ - while(uxQueueMessagesWaiting(gpio_queue_handle)){ +static void gpioHandlerTask(void *arg) +{ + ESP_LOGD(TAG, "start interrupt task"); + while (1) + { + if (uxQueueMessagesWaiting(gpio_queue_handle)) + { + while (uxQueueMessagesWaiting(gpio_queue_handle)) + { GpioResult gpioResult; - xQueueReceive(gpio_queue_handle,(void*)&gpioResult,10); - ESP_LOGD(TAG,"gpio: %d state: %d", gpioResult.gpio, gpioResult.value); - ((GpioHandler*)arg)->gpioInterrupt(&gpioResult); - } + xQueueReceive(gpio_queue_handle, (void *)&gpioResult, 10); + ESP_LOGD(TAG, "gpio: %d state: %d", gpioResult.gpio, gpioResult.value); + ((GpioHandler *)arg)->gpioInterrupt(&gpioResult); + } } - ((GpioHandler*)arg)->taskHandler(); + ((GpioHandler *)arg)->taskHandler(); vTaskDelay(pdMS_TO_TICKS(1000)); } } -void GpioPin::gpioInterrupt(int value) { -#ifdef ENABLE_MQTT - if (_mqttTopic.compare("") != 0) { +void GpioPin::gpioInterrupt(int value) +{ + if (_mqttTopic.compare("") != 0) + { ESP_LOGD(TAG, "gpioInterrupt %s %d", _mqttTopic.c_str(), value); - MQTTPublish(_mqttTopic, value ? "true" : "false", 1); + MQTTPublish(_mqttTopic, value ? "true" : "false", 1); } -#endif //ENABLE_MQTT + currentState = value; } void GpioPin::init() { gpio_config_t io_conf; - //set interrupt + + // set interrupt io_conf.intr_type = _interruptType; - //set as output mode - io_conf.mode = (_mode == GPIO_PIN_MODE_OUTPUT) || (_mode == GPIO_PIN_MODE_BUILT_IN_FLASH_LED) ? gpio_mode_t::GPIO_MODE_OUTPUT : gpio_mode_t::GPIO_MODE_INPUT; - //bit mask of the pins that you want to set,e.g.GPIO18/19 + + // set as output mode + io_conf.mode = (_mode == GPIO_PIN_MODE_OUTPUT) || (_mode == GPIO_PIN_MODE_BUILTIN_FLASH) ? gpio_mode_t::GPIO_MODE_OUTPUT : gpio_mode_t::GPIO_MODE_INPUT; + + // bit mask of the pins that you want to set,e.g.GPIO18/19 io_conf.pin_bit_mask = (1ULL << _gpio); - //set pull-down mode + + // set pull-down mode io_conf.pull_down_en = _mode == GPIO_PIN_MODE_INPUT_PULLDOWN ? gpio_pulldown_t::GPIO_PULLDOWN_ENABLE : gpio_pulldown_t::GPIO_PULLDOWN_DISABLE; - //set pull-up mode + + // set pull-up mode io_conf.pull_up_en = _mode == GPIO_PIN_MODE_INPUT_PULLDOWN ? gpio_pullup_t::GPIO_PULLUP_ENABLE : gpio_pullup_t::GPIO_PULLUP_DISABLE; - //configure GPIO with the given settings + + // configure GPIO with the given settings gpio_config(&io_conf); -// if (_interruptType != GPIO_INTR_DISABLE) { // ohne GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X, wenn das genutzt wird, dann soll auch der Handler hier nicht initialisiert werden, da das dann über SmartLED erfolgt. - if ((_interruptType != GPIO_INTR_DISABLE) && (_interruptType != GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)) { - //hook isr handler for specific gpio pin + if ((_interruptType != GPIO_INTR_DISABLE) && (_mode != GPIO_PIN_MODE_OUTPUT_WS281X)) + { + // hook isr handler for specific gpio pin ESP_LOGD(TAG, "GpioPin::init add isr handler for GPIO %d", _gpio); - gpio_isr_handler_add(_gpio, gpio_isr_handler, (void*)&_gpio); + gpio_isr_handler_add(_gpio, gpio_isr_handler, (void *)&_gpio); } -#ifdef ENABLE_MQTT - if ((_mqttTopic.compare("") != 0) && ((_mode == GPIO_PIN_MODE_OUTPUT) || (_mode == GPIO_PIN_MODE_OUTPUT_PWM) || (_mode == GPIO_PIN_MODE_BUILT_IN_FLASH_LED))) { - std::function f = std::bind(&GpioPin::handleMQTT, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); + if ((_mqttTopic.compare("") != 0) && ((_mode == GPIO_PIN_MODE_OUTPUT) || (_mode == GPIO_PIN_MODE_BUILTIN_FLASH_PWM) || (_mode == GPIO_PIN_MODE_BUILTIN_FLASH))) + { + std::function f = std::bind(&GpioPin::handleMQTT, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); MQTTregisterSubscribeFunction(_mqttTopic, f); } -#endif //ENABLE_MQTT } -bool GpioPin::getValue(std::string* errorText) -{ - if ((_mode != GPIO_PIN_MODE_INPUT) && (_mode != GPIO_PIN_MODE_INPUT_PULLUP) && (_mode != GPIO_PIN_MODE_INPUT_PULLDOWN)) { +bool GpioPin::getValue(std::string *errorText) +{ + if ((_mode != GPIO_PIN_MODE_INPUT) && (_mode != GPIO_PIN_MODE_INPUT_PULLUP) && (_mode != GPIO_PIN_MODE_INPUT_PULLDOWN)) + { (*errorText) = "GPIO is not in input mode"; } return gpio_get_level(_gpio) == 1; } -void GpioPin::setValue(bool value, gpio_set_source setSource, std::string* errorText) +void GpioPin::setValue(bool value, gpio_set_source setSource, std::string *errorText) { ESP_LOGD(TAG, "GpioPin::setValue %d", value); - if ((_mode != GPIO_PIN_MODE_OUTPUT) && (_mode != GPIO_PIN_MODE_OUTPUT_PWM) && (_mode != GPIO_PIN_MODE_BUILT_IN_FLASH_LED)) { + if ((_mode != GPIO_PIN_MODE_OUTPUT) && (_mode != GPIO_PIN_MODE_BUILTIN_FLASH_PWM) && (_mode != GPIO_PIN_MODE_BUILTIN_FLASH)) + { (*errorText) = "GPIO is not in output mode"; - } else { + } + else + { gpio_set_level(_gpio, value); -#ifdef ENABLE_MQTT - if ((_mqttTopic.compare("") != 0) && (setSource != GPIO_SET_SOURCE_MQTT)) { + if ((_mqttTopic.compare("") != 0) && (setSource != GPIO_SET_SOURCE_MQTT)) + { MQTTPublish(_mqttTopic, value ? "true" : "false", 1); } -#endif //ENABLE_MQTT } } -void GpioPin::publishState() { +void GpioPin::publishState() +{ int newState = gpio_get_level(_gpio); - if (newState != currentState) { - ESP_LOGD(TAG,"publish state of GPIO %d new state %d", _gpio, newState); -#ifdef ENABLE_MQTT - if (_mqttTopic.compare("") != 0) - MQTTPublish(_mqttTopic, newState ? "true" : "false", 1); -#endif //ENABLE_MQTT + if (newState != currentState) + { + ESP_LOGD(TAG, "publish state of GPIO %d new state %d", _gpio, newState); + if (_mqttTopic.compare("") != 0) + { + MQTTPublish(_mqttTopic, newState ? "true" : "false", 1); + } + currentState = newState; } } -#ifdef ENABLE_MQTT -bool GpioPin::handleMQTT(std::string, char* data, int data_len) { +bool GpioPin::handleMQTT(std::string, char *data, int data_len) +{ ESP_LOGD(TAG, "GpioPin::handleMQTT data %.*s", data_len, data); std::string dataStr(data, data_len); - dataStr = toLower(dataStr); + dataStr = to_lower(dataStr); std::string errorText = ""; - if ((dataStr == "true") || (dataStr == "1")) { + if ((dataStr == "true") || (dataStr == "1")) + { setValue(true, GPIO_SET_SOURCE_MQTT, &errorText); - } else if ((dataStr == "false") || (dataStr == "0")) { - setValue(false, GPIO_SET_SOURCE_MQTT, &errorText); - } else { + } + else if ((dataStr == "false") || (dataStr == "0")) + { + setValue(false, GPIO_SET_SOURCE_MQTT, &errorText); + } + else + { errorText = "wrong value "; errorText.append(data, data_len); } - if (errorText != "") { + if (errorText != "") + { ESP_LOGE(TAG, "%s", errorText.c_str()); } return (errorText == ""); } -#endif //ENABLE_MQTT esp_err_t callHandleHttpRequest(httpd_req_t *req) { - ESP_LOGD(TAG,"callHandleHttpRequest"); + ESP_LOGD(TAG, "callHandleHttpRequest"); - GpioHandler *gpioHandler = (GpioHandler*)req->user_ctx; + GpioHandler *gpioHandler = (GpioHandler *)req->user_ctx; return gpioHandler->handleHttpRequest(req); } void taskGpioHandler(void *pvParameter) { - ESP_LOGD(TAG,"taskGpioHandler"); - ((GpioHandler*)pvParameter)->init(); + ESP_LOGD(TAG, "taskGpioHandler"); + ((GpioHandler *)pvParameter)->init(); } -GpioHandler::GpioHandler(std::string configFile, httpd_handle_t httpServer) +GpioHandler::GpioHandler(std::string configFile, httpd_handle_t httpServer) { - ESP_LOGI(TAG,"start GpioHandler"); - _configFile = configFile; + ESP_LOGI(TAG, "start GpioHandler"); _httpServer = httpServer; ESP_LOGI(TAG, "register GPIO Uri"); registerGpioUri(); } -GpioHandler::~GpioHandler() { - if (gpioMap != NULL) { +GpioHandler::~GpioHandler() +{ + if (gpioMap != NULL) + { clear(); delete gpioMap; } @@ -217,21 +238,20 @@ GpioHandler::~GpioHandler() { void GpioHandler::init() { - // TickType_t xDelay = 60000 / portTICK_PERIOD_MS; - // ESP_LOGD(TAG, "wait before start %ldms", (long) xDelay); - // vTaskDelay( xDelay ); - ESP_LOGD(TAG, "*************** Start GPIOHandler_Init *****************"); - if (gpioMap == NULL) { - gpioMap = new std::map(); - } else { + if (gpioMap == NULL) + { + gpioMap = new std::map(); + } + else + { clear(); } - - + ESP_LOGI(TAG, "read GPIO config and init GPIO"); - if (!readConfig()) { + if (!readConfig()) + { clear(); delete gpioMap; gpioMap = NULL; @@ -239,22 +259,24 @@ void GpioHandler::init() return; } - - for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { + for (std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) + { it->second->init(); } -#ifdef ENABLE_MQTT std::function f = std::bind(&GpioHandler::handleMQTTconnect, this); MQTTregisterConnectFunction("gpio-handler", f); -#endif //ENABLE_MQTT - if (xHandleTaskGpio == NULL) { - gpio_queue_handle = xQueueCreate(10,sizeof(GpioResult)); - BaseType_t xReturned = xTaskCreate(&gpioHandlerTask, "gpio_int", 3 * 1024, (void *)this, tskIDLE_PRIORITY + 4, &xHandleTaskGpio); - if(xReturned == pdPASS ) { + if (xHandleTaskGpio == NULL) + { + gpio_queue_handle = xQueueCreate(10, sizeof(GpioResult)); + BaseType_t xReturned = xTaskCreate(&gpioHandlerTask, "gpio_int", 3 * 1024, (void *)this, tskIDLE_PRIORITY + 4, &xHandleTaskGpio); + if (xReturned == pdPASS) + { ESP_LOGD(TAG, "xHandletaskGpioHandler started"); - } else { + } + else + { ESP_LOGD(TAG, "xHandletaskGpioHandler not started %d ", (int)xHandleTaskGpio); } } @@ -262,258 +284,275 @@ void GpioHandler::init() ESP_LOGI(TAG, "GPIO init completed, is enabled"); } -void GpioHandler::taskHandler() { - if (gpioMap != NULL) { - for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { +void GpioHandler::taskHandler() +{ + if (gpioMap != NULL) + { + for (std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) + { if ((it->second->getInterruptType() == GPIO_INTR_DISABLE)) it->second->publishState(); } } } -#ifdef ENABLE_MQTT void GpioHandler::handleMQTTconnect() { - if (gpioMap != NULL) { - for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { + if (gpioMap != NULL) + { + for (std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) + { if ((it->second->getMode() == GPIO_PIN_MODE_INPUT) || (it->second->getMode() == GPIO_PIN_MODE_INPUT_PULLDOWN) || (it->second->getMode() == GPIO_PIN_MODE_INPUT_PULLUP)) it->second->publishState(); } } } -#endif //ENABLE_MQTT -void GpioHandler::deinit() { -#ifdef ENABLE_MQTT +void GpioHandler::deinit() +{ MQTTunregisterConnectFunction("gpio-handler"); -#endif //ENABLE_MQTT + clear(); - if (xHandleTaskGpio != NULL) { + if (xHandleTaskGpio != NULL) + { vTaskDelete(xHandleTaskGpio); xHandleTaskGpio = NULL; } } -void GpioHandler::gpioInterrupt(GpioResult* gpioResult) { - if ((gpioMap != NULL) && (gpioMap->find(gpioResult->gpio) != gpioMap->end())) { +void GpioHandler::gpioInterrupt(GpioResult *gpioResult) +{ + if ((gpioMap != NULL) && (gpioMap->find(gpioResult->gpio) != gpioMap->end())) + { (*gpioMap)[gpioResult->gpio]->gpioInterrupt(gpioResult->value); } } -bool GpioHandler::readConfig() +bool GpioHandler::readConfig() { if (!gpioMap->empty()) + { clear(); + } - ConfigFile configFile = ConfigFile(_configFile); - - std::vector splitted; - std::string line = ""; - bool disabledLine = false; - bool eof = false; - gpio_num_t gpioExtLED = (gpio_num_t) 0; - -// ESP_LOGD(TAG, "readConfig - Start 1"); - - while ((!configFile.GetNextParagraph(line, disabledLine, eof) || (line.compare("[GPIO]") != 0)) && !eof) {} - if (eof) + FILE *pFile = fopen(CONFIG_FILE, "r"); + if (pFile == NULL) + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "No ConfigFile defined - exit GpioHandler::readConfig()!"); return false; + } -// ESP_LOGD(TAG, "readConfig - Start 2 line: %s, disabbledLine: %d", line.c_str(), (int) disabledLine); + ClassFlow classFlow; + std::string aktparamgraph = ""; + while (classFlow.GetNextParagraph(pFile, aktparamgraph)) + { + if ((to_upper(aktparamgraph).compare("[GPIO]") == 0) || (to_upper(aktparamgraph).compare(";[GPIO]") == 0)) + { + break; + } + } - - _isEnabled = !disabledLine; - - if (!_isEnabled) + if ((to_upper(aktparamgraph).compare("[GPIO]") != 0) || (to_upper(aktparamgraph).compare(";[GPIO]") == 0)) + { + _isEnabled = false; + fclose(pFile); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "GpioHandler disabled."); return false; + } + else + { + _isEnabled = true; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "GpioHandler enabled."); + } -// ESP_LOGD(TAG, "readConfig - Start 3"); - -#ifdef ENABLE_MQTT -// std::string mainTopicMQTT = ""; std::string mainTopicMQTT = mqttServer_getMainTopic(); if (mainTopicMQTT.length() > 0) { mainTopicMQTT = mainTopicMQTT + "/GPIO"; ESP_LOGD(TAG, "MAINTOPICMQTT found"); } -#endif // ENABLE_MQTT + bool registerISR = false; - while (configFile.getNextLine(&line, disabledLine, eof) && !configFile.isNewParagraph(line)) + gpio_num_t gpioExtLED = GPIO_NUM_NC; + + std::vector splitted; + + while (classFlow.getNextLine(pFile, &aktparamgraph) && !classFlow.isNewParagraph(aktparamgraph)) { - splitted = ZerlegeZeile(line); - // const std::regex pieces_regex("IO([0-9]{1,2})"); - // std::smatch pieces_match; - // if (std::regex_match(splitted[0], pieces_match, pieces_regex) && (pieces_match.size() == 2)) - // { - // std::string gpioStr = pieces_match[1]; - ESP_LOGD(TAG, "conf param %s", toUpper(splitted[0]).c_str()); - if (toUpper(splitted[0]) == "MAINTOPICMQTT") { -// ESP_LOGD(TAG, "MAINTOPICMQTT found"); -// mainTopicMQTT = splitted[1]; - } else if ((splitted[0].rfind("IO", 0) == 0) && (splitted.size() >= 6)) - { - ESP_LOGI(TAG,"Enable GP%s in %s mode", splitted[0].c_str(), splitted[1].c_str()); - std::string gpioStr = splitted[0].substr(2, 2); - gpio_num_t gpioNr = (gpio_num_t)atoi(gpioStr.c_str()); - gpio_pin_mode_t pinMode = resolvePinMode(toLower(splitted[1])); - gpio_int_type_t intType = resolveIntType(toLower(splitted[2])); - uint16_t dutyResolution = (uint8_t)atoi(splitted[3].c_str()); -#ifdef ENABLE_MQTT - bool mqttEnabled = (toLower(splitted[4]) == "true"); -#endif // ENABLE_MQTT - bool httpEnabled = (toLower(splitted[5]) == "true"); - char gpioName[100]; - if (splitted.size() >= 7) { - strcpy(gpioName, trim(splitted[6]).c_str()); - } else { - sprintf(gpioName, "GPIO%d", gpioNr); - } -#ifdef ENABLE_MQTT - std::string mqttTopic = mqttEnabled ? (mainTopicMQTT + "/" + gpioName) : ""; -#else // ENABLE_MQTT - std::string mqttTopic = ""; -#endif // ENABLE_MQTT - GpioPin* gpioPin = new GpioPin(gpioNr, gpioName, pinMode, intType,dutyResolution, mqttTopic, httpEnabled); - (*gpioMap)[gpioNr] = gpioPin; + splitted = split_line(aktparamgraph); - if (pinMode == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X) + if (splitted.size() > 1) + { + std::string _param = to_upper(splitted[0]); + + if (_param == "MAINTOPICMQTT") { - ESP_LOGD(TAG, "Set WS2812 to GPIO %d", gpioNr); - gpioExtLED = gpioNr; } + else if ((_param.rfind("IO", 0) == 0) && (splitted.size() >= 6)) + { + ESP_LOGI(TAG, "Enable GP%s in %s mode", splitted[0].c_str(), splitted[1].c_str()); + std::string gpioStr = splitted[0].substr(2, 2); + gpio_num_t gpioNr = (gpio_num_t)atoi(gpioStr.c_str()); + gpio_pin_mode_t pinMode = resolvePinMode(to_lower(splitted[1])); + gpio_int_type_t intType = resolveIntType(to_lower(splitted[2])); - if (intType != GPIO_INTR_DISABLE) { - registerISR = true; + uint16_t dutyResolution = (uint8_t)atoi(splitted[3].c_str()); + bool mqttEnabled = (to_lower(splitted[4]) == "true"); + bool httpEnabled = (to_lower(splitted[5]) == "true"); + char gpioName[100]; + + if (splitted.size() >= 7) + { + strcpy(gpioName, trim_string_left_right(splitted[6]).c_str()); + } + else + { + sprintf(gpioName, "GPIO%d", gpioNr); + } + + std::string mqttTopic = mqttEnabled ? (mainTopicMQTT + "/" + gpioName) : ""; + + GpioPin *gpioPin = new GpioPin(gpioNr, gpioName, pinMode, intType, dutyResolution, mqttTopic, httpEnabled); + (*gpioMap)[gpioNr] = gpioPin; + + if (pinMode == GPIO_PIN_MODE_OUTPUT_WS281X) + { + ESP_LOGD(TAG, "Set WS2812 to GPIO %d", gpioNr); + gpioExtLED = gpioNr; + } + + if (intType != GPIO_INTR_DISABLE) + { + registerISR = true; + } } - } - if (toUpper(splitted[0]) == "LEDNUMBERS") - { - LEDNumbers = stoi(splitted[1]); - } - if (toUpper(splitted[0]) == "LEDCOLOR") - { - uint8_t _r, _g, _b; - _r = stoi(splitted[1]); - _g = stoi(splitted[2]); - _b = stoi(splitted[3]); + else if (_param == "LEDNUMBERS") + { + LEDNumbers = stoi(splitted[1]); + } + else if (_param == "LEDCOLOR") + { + uint8_t _r, _g, _b; + _r = stoi(splitted[1]); + _g = stoi(splitted[2]); + _b = stoi(splitted[3]); - LEDColor = Rgb{_r, _g, _b}; - } - if (toUpper(splitted[0]) == "LEDTYPE") - { - if (splitted[1] == "WS2812") - LEDType = LED_WS2812; - if (splitted[1] == "WS2812B") - LEDType = LED_WS2812B; - if (splitted[1] == "SK6812") - LEDType = LED_SK6812; - if (splitted[1] == "WS2813") - LEDType = LED_WS2813; + LEDColor = Rgb{_r, _g, _b}; + } + else if (_param == "LEDTYPE") + { + if (splitted[1] == "WS2812") + { + LEDType = LED_WS2812; + } + else if (splitted[1] == "WS2812B") + { + LEDType = LED_WS2812B; + } + else if (splitted[1] == "SK6812") + { + LEDType = LED_SK6812; + } + else if (splitted[1] == "WS2813") + { + LEDType = LED_WS2813; + } + } } } + fclose(pFile); - if (registerISR) { - //install gpio isr service + if (registerISR) + { + // install gpio isr service gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM); } if (gpioExtLED > 0) { - // LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Startsequence 06"); // Nremove -// vTaskDelay( xDelay ); -// xDelay = 5000 / portTICK_PERIOD_MS; -// ESP_LOGD(TAG, "main: sleep for: %ldms", (long) xDelay); - -// SmartLed leds( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer ); - - -// leds[ 0 ] = Rgb{ 255, 0, 0 }; -// leds[ 1 ] = Rgb{ 255, 255, 255 }; -// leds.show(); -// SmartLed leds = new SmartLed(LEDType, LEDNumbers, gpioExtLED, 0, DoubleBuffer); -// _SmartLED = new SmartLed( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer ); } return true; } -void GpioHandler::clear() +void GpioHandler::clear() { ESP_LOGD(TAG, "GpioHandler::clear"); - if (gpioMap != NULL) { - for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { + if (gpioMap != NULL) + { + for (std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) + { delete it->second; } gpioMap->clear(); } - - // gpio_uninstall_isr_service(); can't uninstall, isr service is used by camera } - -void GpioHandler::registerGpioUri() + +void GpioHandler::registerGpioUri() { ESP_LOGI(TAG, "server_GPIO - Registering URI handlers"); - - httpd_uri_t camuri = { }; - camuri.method = HTTP_GET; - camuri.uri = "/GPIO"; - camuri.handler = APPLY_BASIC_AUTH_FILTER(callHandleHttpRequest); - camuri.user_ctx = (void*)this; + + httpd_uri_t camuri = {}; + camuri.method = HTTP_GET; + camuri.uri = "/GPIO"; + camuri.handler = APPLY_BASIC_AUTH_FILTER(callHandleHttpRequest); + camuri.user_ctx = (void *)this; httpd_register_uri_handler(_httpServer, &camuri); } esp_err_t GpioHandler::handleHttpRequest(httpd_req_t *req) { - ESP_LOGD(TAG, "handleHttpRequest"); + ESP_LOGD(TAG, "handleHttpRequest()"); - if (gpioMap == NULL) { + if (gpioMap == NULL) + { std::string resp_str = "GPIO handler not initialized"; - httpd_resp_send(req, resp_str.c_str(), resp_str.length()); + httpd_resp_send(req, resp_str.c_str(), resp_str.length()); return ESP_OK; } -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_switch_GPIO - Start"); -#endif - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_switch_GPIO"); char _query[200]; - char _valueGPIO[30]; - char _valueStatus[30]; + char _valueGPIO[30]; + char _valueStatus[30]; std::string gpio, status; - if (httpd_req_get_url_query_str(req, _query, 200) == ESP_OK) { + if (httpd_req_get_url_query_str(req, _query, 200) == ESP_OK) + { ESP_LOGD(TAG, "Query: %s", _query); - + if (httpd_query_key_value(_query, "GPIO", _valueGPIO, 30) == ESP_OK) { - ESP_LOGD(TAG, "GPIO is found %s", _valueGPIO); + ESP_LOGD(TAG, "GPIO is found %s", _valueGPIO); gpio = std::string(_valueGPIO); - } else { + } + else + { std::string resp_str = "GPIO No is not defined"; - httpd_resp_send(req, resp_str.c_str(), resp_str.length()); + httpd_resp_send(req, resp_str.c_str(), resp_str.length()); return ESP_OK; } if (httpd_query_key_value(_query, "Status", _valueStatus, 30) == ESP_OK) { - ESP_LOGD(TAG, "Status is found %s", _valueStatus); + ESP_LOGD(TAG, "Status is found %s", _valueStatus); status = std::string(_valueStatus); } - } else { - const char* resp_str = "Error in call. Use /GPIO?GPIO=12&Status=high"; - httpd_resp_send(req, resp_str, strlen(resp_str)); + } + else + { + const char *resp_str = "Error in call. Use /GPIO?GPIO=12&Status=high"; + httpd_resp_send(req, resp_str, strlen(resp_str)); return ESP_OK; } - status = toUpper(status); + status = to_upper(status); if ((status != "HIGH") && (status != "LOW") && (status != "TRUE") && (status != "FALSE") && (status != "0") && (status != "1") && (status != "")) { - std::string zw = "Status not valid: " + status; - httpd_resp_sendstr_chunk(req, zw.c_str()); - httpd_resp_sendstr_chunk(req, NULL); - return ESP_OK; + std::string temp_string = "Status not valid: " + status; + httpd_resp_sendstr_chunk(req, temp_string.c_str()); + httpd_resp_sendstr_chunk(req, NULL); + return ESP_OK; } int gpionum = stoi(gpio); @@ -522,24 +561,26 @@ esp_err_t GpioHandler::handleHttpRequest(httpd_req_t *req) gpio_num_t gpio_num = resolvePinNr(gpionum); if (gpio_num == GPIO_NUM_NC) { - std::string zw = "GPIO" + std::to_string(gpionum) + " unsupported - only 12 & 13 free"; - httpd_resp_sendstr_chunk(req, zw.c_str()); - httpd_resp_sendstr_chunk(req, NULL); - return ESP_OK; + std::string temp_string = "GPIO" + std::to_string(gpionum) + " unsupported - only 12 & 13 free"; + httpd_resp_sendstr_chunk(req, temp_string.c_str()); + httpd_resp_sendstr_chunk(req, NULL); + return ESP_OK; } - if (gpioMap->count(gpio_num) == 0) { - char resp_str [30]; + if (gpioMap->count(gpio_num) == 0) + { + char resp_str[30]; sprintf(resp_str, "GPIO%d is not registred", gpio_num); - httpd_resp_send(req, resp_str, strlen(resp_str)); - return ESP_OK; + httpd_resp_send(req, resp_str, strlen(resp_str)); + return ESP_OK; } - - if (status == "") + + if (status == "") { std::string resp_str = ""; status = (*gpioMap)[gpio_num]->getValue(&resp_str) ? "HIGH" : "LOW"; - if (resp_str == "") { + if (resp_str == "") + { resp_str = status; } httpd_resp_sendstr_chunk(req, resp_str.c_str()); @@ -549,158 +590,234 @@ esp_err_t GpioHandler::handleHttpRequest(httpd_req_t *req) { std::string resp_str = ""; (*gpioMap)[gpio_num]->setValue((status == "HIGH") || (status == "TRUE") || (status == "1"), GPIO_SET_SOURCE_HTTP, &resp_str); - if (resp_str == "") { + if (resp_str == "") + { resp_str = "GPIO" + std::to_string(gpionum) + " switched to " + status; } httpd_resp_sendstr_chunk(req, resp_str.c_str()); httpd_resp_sendstr_chunk(req, NULL); } - - return ESP_OK; + + return ESP_OK; }; -void GpioHandler::flashLightEnable(bool value) +void GpioHandler::flashLightEnable(bool value) { ESP_LOGD(TAG, "GpioHandler::flashLightEnable %s", value ? "true" : "false"); - if (gpioMap != NULL) { - for(std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) + if (gpioMap != NULL) + { + for (std::map::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) { - if (it->second->getMode() == GPIO_PIN_MODE_BUILT_IN_FLASH_LED) //|| (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_PWM) || (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)) + if (it->second->getMode() == GPIO_PIN_MODE_BUILTIN_FLASH) { std::string resp_str = ""; it->second->setValue(value, GPIO_SET_SOURCE_INTERNAL, &resp_str); - if (resp_str == "") { + if (resp_str == "") + { ESP_LOGD(TAG, "Flash light pin GPIO %d switched to %s", (int)it->first, (value ? "on" : "off")); - } else { + } + else + { ESP_LOGE(TAG, "Can't set flash light pin GPIO %d. Error: %s", (int)it->first, resp_str.c_str()); } - } else + } + else + { + if (it->second->getMode() == GPIO_PIN_MODE_OUTPUT_WS281X) { - if (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X) +#ifdef __LEDGLOBAL + if (leds_global == NULL) { -#ifdef __LEDGLOBAL - if (leds_global == NULL) { - ESP_LOGI(TAG, "init SmartLed: LEDNumber=%d, GPIO=%d", LEDNumbers, (int)it->second->getGPIO()); - leds_global = new SmartLed( LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer ); - } else { - // wait until we can update: https://github.com/RoboticsBrno/SmartLeds/issues/10#issuecomment-386921623 - leds_global->wait(); - } -#else - SmartLed leds( LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer ); -#endif - - if (value) - { - for (int i = 0; i < LEDNumbers; ++i) -#ifdef __LEDGLOBAL - (*leds_global)[i] = LEDColor; -#else - leds[i] = LEDColor; -#endif - } - else - { - for (int i = 0; i < LEDNumbers; ++i) -#ifdef __LEDGLOBAL - (*leds_global)[i] = Rgb{0, 0, 0}; -#else - leds[i] = Rgb{0, 0, 0}; -#endif - } -#ifdef __LEDGLOBAL - leds_global->show(); -#else - leds.show(); -#endif + ESP_LOGI(TAG, "init SmartLed: LEDNumber=%d, GPIO=%d", LEDNumbers, (int)it->second->getGPIO()); + leds_global = new SmartLed(LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer); } + else + { + // wait until we can update: https://github.com/RoboticsBrno/SmartLeds/issues/10#issuecomment-386921623 + leds_global->wait(); + } +#else + SmartLed leds(LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer); +#endif + + if (value) + { + for (int i = 0; i < LEDNumbers; ++i) +#ifdef __LEDGLOBAL + (*leds_global)[i] = LEDColor; +#else + leds[i] = LEDColor; +#endif + } + else + { + for (int i = 0; i < LEDNumbers; ++i) +#ifdef __LEDGLOBAL + (*leds_global)[i] = Rgb{0, 0, 0}; +#else + leds[i] = Rgb{0, 0, 0}; +#endif + } +#ifdef __LEDGLOBAL + leds_global->show(); +#else + leds.show(); +#endif } + } } } } -gpio_num_t GpioHandler::resolvePinNr(uint8_t pinNr) +gpio_num_t GpioHandler::resolvePinNr(uint8_t pinNr) { - switch(pinNr) { - case 0: - return GPIO_NUM_0; - case 1: - return GPIO_NUM_1; - case 3: - return GPIO_NUM_3; - case 4: - return GPIO_NUM_4; - case 12: - return GPIO_NUM_12; - case 13: - return GPIO_NUM_13; - default: - return GPIO_NUM_NC; + switch (pinNr) + { + case 0: + return GPIO_NUM_0; + case 1: + return GPIO_NUM_1; + case 3: + return GPIO_NUM_3; + case 4: + return GPIO_NUM_4; + case 12: + return GPIO_NUM_12; + case 13: + return GPIO_NUM_13; + default: + return GPIO_NUM_NC; } } - -gpio_pin_mode_t GpioHandler::resolvePinMode(std::string input) +/* +gpio_num_t GpioHandler::resolvePinNr(uint8_t pinNr) { - if( input == "disabled" ) return GPIO_PIN_MODE_DISABLED; - if( input == "input" ) return GPIO_PIN_MODE_INPUT; - if( input == "input-pullup" ) return GPIO_PIN_MODE_INPUT_PULLUP; - if( input == "input-pulldown" ) return GPIO_PIN_MODE_INPUT_PULLDOWN; - if( input == "output" ) return GPIO_PIN_MODE_OUTPUT; - if( input == "built-in-led" ) return GPIO_PIN_MODE_BUILT_IN_FLASH_LED; - if( input == "output-pwm" ) return GPIO_PIN_MODE_OUTPUT_PWM; - if( input == "external-flash-pwm" ) return GPIO_PIN_MODE_EXTERNAL_FLASH_PWM; - if( input == "external-flash-ws281x" ) return GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X; + switch (pinNr) + { + case 1: + return GPIO_IO1; + case 2: + return GPIO_IO2; + case 3: + return GPIO_IO3; + case 4: + return GPIO_IO4; + } + + return GPIO_NUM_NC; +} +*/ +gpio_pin_mode_t GpioHandler::resolvePinMode(std::string input) +{ + if (input == "disabled") + { + return GPIO_PIN_MODE_DISABLED; + } + else if (input == "input") + { + return GPIO_PIN_MODE_INPUT; + } + else if (input == "input-pullup") + { + return GPIO_PIN_MODE_INPUT_PULLUP; + } + else if (input == "input-pulldown") + { + return GPIO_PIN_MODE_INPUT_PULLDOWN; + } + else if (input == "output") + { + return GPIO_PIN_MODE_OUTPUT; + } + else if (input == "built-in-led") + { + return GPIO_PIN_MODE_BUILTIN_FLASH; + } + else if (input == "output-pwm") + { + return GPIO_PIN_MODE_BUILTIN_FLASH_PWM; + } + else if (input == "external-flash-pwm") + { + return GPIO_PIN_MODE_OUTPUT_PWM; + } + else if (input == "external-flash-ws281x") + { + return GPIO_PIN_MODE_OUTPUT_WS281X; + } return GPIO_PIN_MODE_DISABLED; } -gpio_int_type_t GpioHandler::resolveIntType(std::string input) +gpio_int_type_t GpioHandler::resolveIntType(std::string input) { - if( input == "disabled" ) return GPIO_INTR_DISABLE; - if( input == "rising-edge" ) return GPIO_INTR_POSEDGE; - if( input == "falling-edge" ) return GPIO_INTR_NEGEDGE; - if( input == "rising-and-falling" ) return GPIO_INTR_ANYEDGE ; - if( input == "low-level-trigger" ) return GPIO_INTR_LOW_LEVEL; - if( input == "high-level-trigger" ) return GPIO_INTR_HIGH_LEVEL; - + if (input == "disabled") + { + return GPIO_INTR_DISABLE; + } + if (input == "rising-edge") + { + return GPIO_INTR_POSEDGE; + } + if (input == "falling-edge") + { + return GPIO_INTR_NEGEDGE; + } + if (input == "rising-and-falling") + { + return GPIO_INTR_ANYEDGE; + } + if (input == "low-level-trigger") + { + return GPIO_INTR_LOW_LEVEL; + } + if (input == "high-level-trigger") + { + return GPIO_INTR_HIGH_LEVEL; + } return GPIO_INTR_DISABLE; } static GpioHandler *gpioHandler = NULL; -void gpio_handler_create(httpd_handle_t server) +void gpio_handler_create(httpd_handle_t server) { if (gpioHandler == NULL) + { gpioHandler = new GpioHandler(CONFIG_FILE, server); + } } -void gpio_handler_init() +void gpio_handler_init() { - if (gpioHandler != NULL) { + if (gpioHandler != NULL) + { gpioHandler->init(); } } -void gpio_handler_deinit() { - if (gpioHandler != NULL) { +void gpio_handler_deinit() +{ + if (gpioHandler != NULL) + { gpioHandler->deinit(); - } + } } void gpio_handler_destroy() { - if (gpioHandler != NULL) { + if (gpioHandler != NULL) + { gpio_handler_deinit(); delete gpioHandler; gpioHandler = NULL; } } -GpioHandler* gpio_handler_get() +GpioHandler *gpio_handler_get() { return gpioHandler; } - diff --git a/code/components/jomjol_controlGPIO/server_GPIO.h b/code/components/jomjol_controlGPIO/server_GPIO.h index bf09fd98..5d022921 100644 --- a/code/components/jomjol_controlGPIO/server_GPIO.h +++ b/code/components/jomjol_controlGPIO/server_GPIO.h @@ -3,57 +3,58 @@ #ifndef SERVER_GPIO_H #define SERVER_GPIO_H -#include - -#include #include -#include "driver/gpio.h" +#include +#include +#include #include "SmartLeds.h" -typedef enum { - GPIO_PIN_MODE_DISABLED = 0x0, - GPIO_PIN_MODE_INPUT = 0x1, - GPIO_PIN_MODE_INPUT_PULLUP = 0x2, - GPIO_PIN_MODE_INPUT_PULLDOWN = 0x3, - GPIO_PIN_MODE_OUTPUT = 0x4, - GPIO_PIN_MODE_BUILT_IN_FLASH_LED = 0x5, - GPIO_PIN_MODE_OUTPUT_PWM = 0x6, - GPIO_PIN_MODE_EXTERNAL_FLASH_PWM = 0x7, - GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X = 0x8, +typedef enum +{ + GPIO_PIN_MODE_DISABLED = 0x0, + GPIO_PIN_MODE_INPUT = 0x1, + GPIO_PIN_MODE_INPUT_PULLUP = 0x2, + GPIO_PIN_MODE_INPUT_PULLDOWN = 0x3, + GPIO_PIN_MODE_OUTPUT = 0x4, + GPIO_PIN_MODE_OUTPUT_PWM = 0x5, + GPIO_PIN_MODE_OUTPUT_WS281X = 0x6, + GPIO_PIN_MODE_BUILTIN_FLASH = 0x7, + GPIO_PIN_MODE_BUILTIN_FLASH_PWM = 0x8, } gpio_pin_mode_t; -struct GpioResult { +struct GpioResult +{ gpio_num_t gpio; int value; }; -typedef enum { - GPIO_SET_SOURCE_INTERNAL = 0, - GPIO_SET_SOURCE_MQTT = 1, - GPIO_SET_SOURCE_HTTP = 2, +typedef enum +{ + GPIO_SET_SOURCE_INTERNAL = 0, + GPIO_SET_SOURCE_MQTT = 1, + GPIO_SET_SOURCE_HTTP = 2, } gpio_set_source; -class GpioPin { +class GpioPin +{ public: - GpioPin(gpio_num_t gpio, const char* name, gpio_pin_mode_t mode, gpio_int_type_t interruptType, uint8_t dutyResolution, std::string mqttTopic, bool httpEnable); + GpioPin(gpio_num_t gpio, const char *name, gpio_pin_mode_t mode, gpio_int_type_t interruptType, uint8_t dutyResolution, std::string mqttTopic, bool httpEnable); ~GpioPin(); void init(); - bool getValue(std::string* errorText); - void setValue(bool value, gpio_set_source setSource, std::string* errorText); -#ifdef ENABLE_MQTT - bool handleMQTT(std::string, char* data, int data_len); -#endif //ENABLE_MQTT + bool getValue(std::string *errorText); + void setValue(bool value, gpio_set_source setSource, std::string *errorText); + bool handleMQTT(std::string, char *data, int data_len); void publishState(); void gpioInterrupt(int value); gpio_int_type_t getInterruptType() { return _interruptType; } gpio_pin_mode_t getMode() { return _mode; } - gpio_num_t getGPIO(){return _gpio;}; + gpio_num_t getGPIO() { return _gpio; }; private: gpio_num_t _gpio; - const char* _name; + const char *_name; gpio_pin_mode_t _mode; gpio_int_type_t _interruptType; std::string _mqttTopic; @@ -63,32 +64,30 @@ private: esp_err_t callHandleHttpRequest(httpd_req_t *req); void taskGpioHandler(void *pvParameter); -class GpioHandler { +class GpioHandler +{ public: GpioHandler(std::string configFile, httpd_handle_t httpServer); ~GpioHandler(); - + void init(); void deinit(); void registerGpioUri(); esp_err_t handleHttpRequest(httpd_req_t *req); void taskHandler(); - void gpioInterrupt(GpioResult* gpioResult); + void gpioInterrupt(GpioResult *gpioResult); void flashLightEnable(bool value); bool isEnabled() { return _isEnabled; } -#ifdef ENABLE_MQTT void handleMQTTconnect(); -#endif //ENABLE_MQTT private: - std::string _configFile; httpd_handle_t _httpServer; - std::map *gpioMap = NULL; + std::map *gpioMap = NULL; TaskHandle_t xHandleTaskGpio = NULL; bool _isEnabled = false; int LEDNumbers = 2; - Rgb LEDColor = Rgb{ 255, 255, 255 }; + Rgb LEDColor = Rgb{255, 255, 255}; LedType LEDType = LED_WS2812; #ifdef __LEDGLOBAL SmartLed *leds_global = NULL; @@ -96,7 +95,7 @@ private: bool readConfig(); void clear(); - + gpio_num_t resolvePinNr(uint8_t pinNr); gpio_pin_mode_t resolvePinMode(std::string input); gpio_int_type_t resolveIntType(std::string input); @@ -106,9 +105,7 @@ void gpio_handler_create(httpd_handle_t server); void gpio_handler_init(); void gpio_handler_deinit(); void gpio_handler_destroy(); -GpioHandler* gpio_handler_get(); +GpioHandler *gpio_handler_get(); - -#endif //SERVER_GPIO_H - +#endif // SERVER_GPIO_H diff --git a/code/components/jomjol_controlcamera/CMakeLists.txt b/code/components/jomjol_controlcamera/CMakeLists.txt index 1959dd55..1f12ffbd 100644 --- a/code/components/jomjol_controlcamera/CMakeLists.txt +++ b/code/components/jomjol_controlcamera/CMakeLists.txt @@ -1,9 +1,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) -list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) - idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." - REQUIRES esp_timer esp32-camera esp_http_server jomjol_logfile jomjol_image_proc nvs_flash jomjol_fileserver_ota jomjol_controlGPIO) + INCLUDE_DIRS "." "../../include" + REQUIRES esp_timer esp32-camera esp_http_server jomjol_network jomjol_logfile jomjol_image_proc nvs_flash jomjol_fileserver_ota jomjol_controlGPIO jomjol_helper) diff --git a/code/components/jomjol_controlcamera/ClassControllCamera.cpp b/code/components/jomjol_controlcamera/ClassControllCamera.cpp index 075b2373..4d469997 100644 --- a/code/components/jomjol_controlcamera/ClassControllCamera.cpp +++ b/code/components/jomjol_controlcamera/ClassControllCamera.cpp @@ -13,7 +13,7 @@ #include "server_ota.h" #include "server_GPIO.h" -#include "../../include/defines.h" +#include "defines.h" #include #include @@ -35,21 +35,9 @@ #include "ov2640_specialEffect.h" #include "ov2640_contrast_brightness.h" -#if (ESP_IDF_VERSION_MAJOR >= 5) -#include "soc/periph_defs.h" -#include "esp_private/periph_ctrl.h" -#include "soc/gpio_sig_map.h" -#include "soc/gpio_periph.h" -#include "soc/io_mux_reg.h" -#include "esp_rom_gpio.h" -#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio -#define gpio_matrix_in(a, b, c) esp_rom_gpio_connect_in_signal(a, b, c) -#define gpio_matrix_out(a, b, c, d) esp_rom_gpio_connect_out_signal(a, b, c, d) -#define ets_delay_us(a) esp_rom_delay_us(a) -#endif - CCamera Camera; camera_controll_config_temp_t CCstatus; +camera_controll_config_temp_t CFstatus; static const char *TAG = "CAM"; @@ -61,13 +49,13 @@ static const char *_STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: % uint8_t *demoImage = NULL; // Buffer holding the demo image in bytes #define DEMO_IMAGE_SIZE 30000 // Max size of demo image in bytes +std::vector demoFiles; // Camera module bus communications frequency. // Originally: config.xclk_freq_mhz = 20000000, but this lead to visual artifacts on many modules. // See https://github.com/espressif/esp32-camera/issues/150#issuecomment-726473652 et al. #if !defined(XCLK_FREQ_MHZ) -// int xclk = 8; -int xclk = 20; // Orginal value +int xclk = 20; #else int xclk = XCLK_FREQ_MHZ; #endif @@ -92,13 +80,13 @@ static camera_config_t camera_config = { .pin_pclk = CAM_PIN_PCLK, .xclk_freq_hz = (xclk * 1000000), - .ledc_timer = LEDC_TIMER_0, // LEDC timer to be used for generating XCLK - .ledc_channel = LEDC_CHANNEL_0, // LEDC channel to be used for generating XCLK + .ledc_timer = CAM_XCLK_TIMER, // LEDC timer to be used for generating XCLK + .ledc_channel = CAM_XCLK_CHANNEL, // LEDC channel to be used for generating XCLK .pixel_format = PIXFORMAT_JPEG, // YUV422,GRAYSCALE,RGB565,JPEG .frame_size = FRAMESIZE_VGA, // QQVGA-UXGA Do not use sizes above QVGA when not JPEG // .frame_size = FRAMESIZE_UXGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG - .jpeg_quality = 12, // 0-63 lower number means higher quality + .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 .fb_location = CAMERA_FB_IN_PSRAM, /*!< The location where the frame buffer will be allocated */ .grab_mode = CAMERA_GRAB_LATEST, // only from new esp32cam version @@ -112,96 +100,91 @@ typedef struct CCamera::CCamera(void) { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "CreateClassCamera"); -#endif CCstatus.WaitBeforePicture = 2; + CamDeepSleepEnable = false; + CameraInitSuccessful = false; + changedCameraSettings = false; + CamTempImage = false; + ledc_init(); } -esp_err_t CCamera::InitCam(void) +esp_err_t CCamera::init_camera(void) { ESP_LOGD(TAG, "Init Camera"); - TickType_t cam_xDelay = 100 / portTICK_PERIOD_MS; - - CCstatus.ImageQuality = camera_config.jpeg_quality; - CCstatus.ImageFrameSize = camera_config.frame_size; - - // De-init in case it was already initialized - esp_camera_deinit(); - vTaskDelay(cam_xDelay); + // esp_err_t err = esp_camera_deinit(); // initialize the camera esp_err_t err = esp_camera_init(&camera_config); - vTaskDelay(cam_xDelay); - if (err != ESP_OK) { ESP_LOGE(TAG, "Camera Init Failed"); return err; } - CCstatus.CameraInitSuccessful = true; + Camera.CameraInitSuccessful = false; // Get a reference to the sensor - sensor_t *s = esp_camera_sensor_get(); - - if (s != NULL) + sensor_t *cam_sensor = esp_camera_sensor_get(); + if (cam_sensor != NULL) { - CCstatus.CamSensor_id = s->id.PID; + Camera.CamSensorId = cam_sensor->id.PID; // Dump camera module, warn for unsupported modules. - switch (CCstatus.CamSensor_id) + switch (Camera.CamSensorId) { case OV2640_PID: ESP_LOGI(TAG, "OV2640 camera module detected"); + Camera.CameraInitSuccessful = true; break; case OV3660_PID: ESP_LOGI(TAG, "OV3660 camera module detected"); + Camera.CameraInitSuccessful = true; break; case OV5640_PID: ESP_LOGI(TAG, "OV5640 camera module detected"); + Camera.CameraInitSuccessful = true; break; default: ESP_LOGE(TAG, "Camera module is unknown and not properly supported!"); - CCstatus.CameraInitSuccessful = false; + Camera.CameraInitSuccessful = false; } } - if (CCstatus.CameraInitSuccessful) + if (Camera.CameraInitSuccessful) { return ESP_OK; } - else - { - return ESP_FAIL; - } + + return ESP_FAIL; } -bool CCamera::testCamera(void) +void CCamera::power_reset_camera(void) { - bool success; - camera_fb_t *fb = esp_camera_fb_get(); +#if CAM_PIN_PWDN == GPIO_NUM_NC // Use reset only if pin is available + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "No power down pin availbale to reset camera"); +#else + ESP_LOGD(TAG, "Resetting camera by power down line"); + gpio_config_t conf; + conf.intr_type = GPIO_INTR_DISABLE; + conf.pin_bit_mask = 1LL << CAM_PIN_PWDN; + conf.mode = GPIO_MODE_OUTPUT; + conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + conf.pull_up_en = GPIO_PULLUP_DISABLE; + gpio_config(&conf); - if (fb) - { - success = true; - } - else - { - success = false; - } - - esp_camera_fb_return(fb); - - return success; + // carefull, logic is inverted compared to reset pin + gpio_set_level(CAM_PIN_PWDN, 1); + vTaskDelay(1000 / portTICK_PERIOD_MS); + gpio_set_level(CAM_PIN_PWDN, 0); + vTaskDelay(1000 / portTICK_PERIOD_MS); +#endif } void CCamera::ledc_init(void) { -#ifdef USE_PWM_LEDFLASH // Prepare and then apply the LEDC PWM timer configuration ledc_timer_config_t ledc_timer = {}; @@ -220,75 +203,74 @@ void CCamera::ledc_init(void) ledc_channel.channel = LEDC_CHANNEL; ledc_channel.timer_sel = LEDC_TIMER; ledc_channel.intr_type = LEDC_INTR_DISABLE; - ledc_channel.gpio_num = LEDC_OUTPUT_IO; + ledc_channel.gpio_num = FLASH_GPIO; ledc_channel.duty = 0; // Set duty to 0% ledc_channel.hpoint = 0; // ledc_channel.flags.output_invert = LEDC_OUTPUT_INVERT; ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); -#endif } -int CCamera::SetLEDIntensity(int _intrel) +int CCamera::set_led_intensity(int _intrel) { - // CCstatus.ImageLedIntensity = (int)(std::min(std::max((float)0, _intrel), (float)100) / 100 * 8191) Camera.LedIntensity = (int)((float)(std::min(std::max(0, _intrel), 100)) / 100 * 8191); ESP_LOGD(TAG, "Set led_intensity to %i of 8191", Camera.LedIntensity); return Camera.LedIntensity; } -bool CCamera::getCameraInitSuccessful(void) +bool CCamera::get_camera_init_successful(void) { - return CCstatus.CameraInitSuccessful; + return Camera.CameraInitSuccessful; } -esp_err_t CCamera::setSensorDatenFromCCstatus(void) +esp_err_t CCamera::set_sensor_controll_config(camera_controll_config_temp_t *camConfig) { - sensor_t *s = esp_camera_sensor_get(); + sensor_t *cam_sensor = esp_camera_sensor_get(); - if (s != NULL) + if (cam_sensor != NULL) { - s->set_framesize(s, CCstatus.ImageFrameSize); - - // s->set_contrast(s, CCstatus.ImageContrast); // -2 to 2 - // s->set_brightness(s, CCstatus.ImageBrightness); // -2 to 2 - SetCamContrastBrightness(s, CCstatus.ImageContrast, CCstatus.ImageBrightness); - - s->set_saturation(s, CCstatus.ImageSaturation); // -2 to 2 + cam_sensor->set_xclk(cam_sensor, CAM_XCLK_TIMER, camConfig->CamXclkFreqMhz); - s->set_quality(s, CCstatus.ImageQuality); // 0 - 63 - - // s->set_gainceiling(s, CCstatus.ImageGainceiling); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128) - SetCamGainceiling(s, CCstatus.ImageGainceiling); - - s->set_gain_ctrl(s, CCstatus.ImageAgc); // 0 = disable , 1 = enable - s->set_exposure_ctrl(s, CCstatus.ImageAec); // 0 = disable , 1 = enable - s->set_hmirror(s, CCstatus.ImageHmirror); // 0 = disable , 1 = enable - s->set_vflip(s, CCstatus.ImageVflip); // 0 = disable , 1 = enable - - s->set_whitebal(s, CCstatus.ImageAwb); // 0 = disable , 1 = enable - s->set_aec2(s, CCstatus.ImageAec2); // 0 = disable , 1 = enable - s->set_aec_value(s, CCstatus.ImageAecValue); // 0 to 1200 - // s->set_special_effect(s, CCstatus.ImageSpecialEffect); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia) - SetCamSpecialEffect(s, CCstatus.ImageSpecialEffect); - s->set_wb_mode(s, CCstatus.ImageWbMode); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home) - s->set_ae_level(s, CCstatus.ImageAeLevel); // -2 to 2 - - s->set_dcw(s, CCstatus.ImageDcw); // 0 = disable , 1 = enable - s->set_bpc(s, CCstatus.ImageBpc); // 0 = disable , 1 = enable - s->set_wpc(s, CCstatus.ImageWpc); // 0 = disable , 1 = enable - s->set_awb_gain(s, CCstatus.ImageAwbGain); // 0 = disable , 1 = enable - s->set_agc_gain(s, CCstatus.ImageAgcGain); // 0 to 30 - - s->set_raw_gma(s, CCstatus.ImageRawGma); // 0 = disable , 1 = enable - s->set_lenc(s, CCstatus.ImageLenc); // 0 = disable , 1 = enable + cam_sensor->set_framesize(cam_sensor, camConfig->ImageFrameSize); - // s->set_sharpness(s, CCstatus.ImageSharpness); // auto-sharpness is not officially supported, default to 0 - SetCamSharpness(CCstatus.ImageAutoSharpness, CCstatus.ImageSharpness); - s->set_denoise(s, CCstatus.ImageDenoiseLevel); // The OV2640 does not support it, OV3660 and OV5640 (0 to 8) + // cam_sensor->set_contrast(cam_sensor, camConfig->ImageContrast); // -2 to 2 + // cam_sensor->set_brightness(cam_sensor, camConfig->ImageBrightness); // -2 to 2 + set_camera_contrast_brightness(cam_sensor, camConfig->ImageContrast, camConfig->ImageBrightness); - TickType_t cam_xDelay = 100 / portTICK_PERIOD_MS; - vTaskDelay(cam_xDelay); + cam_sensor->set_saturation(cam_sensor, camConfig->ImageSaturation); // -2 to 2 + + cam_sensor->set_quality(cam_sensor, camConfig->ImageQuality); // 0 - 63 + + // cam_sensor->set_gainceiling(cam_sensor, camConfig->ImageGainceiling); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128) + set_camera_gainceiling(cam_sensor, camConfig->ImageGainceiling); + + cam_sensor->set_gain_ctrl(cam_sensor, camConfig->ImageAgc); // 0 = disable , 1 = enable + cam_sensor->set_exposure_ctrl(cam_sensor, camConfig->ImageAec); // 0 = disable , 1 = enable + cam_sensor->set_hmirror(cam_sensor, camConfig->ImageHmirror); // 0 = disable , 1 = enable + cam_sensor->set_vflip(cam_sensor, camConfig->ImageVflip); // 0 = disable , 1 = enable + + cam_sensor->set_whitebal(cam_sensor, camConfig->ImageAwb); // 0 = disable , 1 = enable + cam_sensor->set_aec2(cam_sensor, camConfig->ImageAec2); // 0 = disable , 1 = enable + cam_sensor->set_aec_value(cam_sensor, camConfig->ImageAecValue); // 0 to 1200 + // cam_sensor->set_special_effect(cam_sensor, camConfig->ImageSpecialEffect); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia) + set_camera_special_effect(cam_sensor, camConfig->ImageSpecialEffect); + cam_sensor->set_wb_mode(cam_sensor, camConfig->ImageWbMode); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home) + cam_sensor->set_ae_level(cam_sensor, camConfig->ImageAeLevel); // -2 to 2 + + cam_sensor->set_dcw(cam_sensor, camConfig->ImageDcw); // 0 = disable , 1 = enable + cam_sensor->set_bpc(cam_sensor, camConfig->ImageBpc); // 0 = disable , 1 = enable + cam_sensor->set_wpc(cam_sensor, camConfig->ImageWpc); // 0 = disable , 1 = enable + cam_sensor->set_awb_gain(cam_sensor, camConfig->ImageAwbGain); // 0 = disable , 1 = enable + cam_sensor->set_agc_gain(cam_sensor, camConfig->ImageAgcGain); // 0 to 30 + + cam_sensor->set_raw_gma(cam_sensor, camConfig->ImageRawGma); // 0 = disable , 1 = enable + cam_sensor->set_lenc(cam_sensor, camConfig->ImageLenc); // 0 = disable , 1 = enable + + // cam_sensor->set_sharpness(cam_sensor, camConfig->ImageSharpness); // auto-sharpness is not officially supported, default to 0 + set_camera_sharpness(camConfig->ImageAutoSharpness, camConfig->ImageSharpness); + cam_sensor->set_denoise(cam_sensor, camConfig->ImageDenoiseLevel); // The OV2640 does not support it, OV3660 and OV5640 (0 to 8) + + vTaskDelay(100 / portTICK_PERIOD_MS); return ESP_OK; } @@ -298,47 +280,48 @@ esp_err_t CCamera::setSensorDatenFromCCstatus(void) } } -esp_err_t CCamera::getSensorDatenToCCstatus(void) +esp_err_t CCamera::get_sensor_controll_config(camera_controll_config_temp_t *camConfig) { - sensor_t *s = esp_camera_sensor_get(); + sensor_t *cam_sensor = esp_camera_sensor_get(); - if (s != NULL) + if (cam_sensor != NULL) { - CCstatus.CamSensor_id = s->id.PID; + Camera.CamSensorId = cam_sensor->id.PID; + camConfig->CamXclkFreqMhz = (int)(cam_sensor->xclk_freq_hz / 1000000); - CCstatus.ImageFrameSize = (framesize_t)s->status.framesize; - - CCstatus.ImageContrast = s->status.contrast; - CCstatus.ImageBrightness = s->status.brightness; - CCstatus.ImageSaturation = s->status.saturation; - - CCstatus.ImageQuality = s->status.quality; - - CCstatus.ImageGainceiling = (gainceiling_t)s->status.gainceiling; + camConfig->ImageFrameSize = (framesize_t)cam_sensor->status.framesize; - CCstatus.ImageAgc = s->status.agc; - CCstatus.ImageAec = s->status.aec; - CCstatus.ImageHmirror = s->status.hmirror; - CCstatus.ImageVflip = s->status.vflip; - - CCstatus.ImageAwb = s->status.awb; - CCstatus.ImageAec2 = s->status.aec2; - CCstatus.ImageAecValue = s->status.aec_value; - CCstatus.ImageSpecialEffect = s->status.special_effect; - CCstatus.ImageWbMode = s->status.wb_mode; - CCstatus.ImageAeLevel = s->status.ae_level; - - CCstatus.ImageDcw = s->status.dcw; - CCstatus.ImageBpc = s->status.bpc; - CCstatus.ImageWpc = s->status.wpc; - CCstatus.ImageAwbGain = s->status.awb_gain; - CCstatus.ImageAgcGain = s->status.agc_gain; - - CCstatus.ImageRawGma = s->status.raw_gma; - CCstatus.ImageLenc = s->status.lenc; + camConfig->ImageContrast = cam_sensor->status.contrast; + camConfig->ImageBrightness = cam_sensor->status.brightness; + camConfig->ImageSaturation = cam_sensor->status.saturation; - // CCstatus.ImageSharpness = s->status.sharpness; // gibt -1 zurück, da es nicht unterstützt wird - CCstatus.ImageDenoiseLevel = s->status.denoise; + camConfig->ImageQuality = cam_sensor->status.quality; + + camConfig->ImageGainceiling = (gainceiling_t)cam_sensor->status.gainceiling; + + camConfig->ImageAgc = cam_sensor->status.agc; + camConfig->ImageAec = cam_sensor->status.aec; + camConfig->ImageHmirror = cam_sensor->status.hmirror; + camConfig->ImageVflip = cam_sensor->status.vflip; + + camConfig->ImageAwb = cam_sensor->status.awb; + camConfig->ImageAec2 = cam_sensor->status.aec2; + camConfig->ImageAecValue = cam_sensor->status.aec_value; + camConfig->ImageSpecialEffect = cam_sensor->status.special_effect; + camConfig->ImageWbMode = cam_sensor->status.wb_mode; + camConfig->ImageAeLevel = cam_sensor->status.ae_level; + + camConfig->ImageDcw = cam_sensor->status.dcw; + camConfig->ImageBpc = cam_sensor->status.bpc; + camConfig->ImageWpc = cam_sensor->status.wpc; + camConfig->ImageAwbGain = cam_sensor->status.awb_gain; + camConfig->ImageAgcGain = cam_sensor->status.agc_gain; + + camConfig->ImageRawGma = cam_sensor->status.raw_gma; + camConfig->ImageLenc = cam_sensor->status.lenc; + + camConfig->ImageSharpness = cam_sensor->status.sharpness; // gibt -1 zurück, da es nicht unterstützt wird + camConfig->ImageDenoiseLevel = cam_sensor->status.denoise; return ESP_OK; } @@ -348,48 +331,165 @@ esp_err_t CCamera::getSensorDatenToCCstatus(void) } } -// on the OV5640, gainceiling must be set with the real value (x2>>>gainceilingLevel = 2, .... x128>>>gainceilingLevel = 128) -int CCamera::SetCamGainceiling(sensor_t *s, gainceiling_t gainceilingLevel) +esp_err_t CCamera::set_camera_config_from_to(camera_controll_config_temp_t *camConfigFrom, camera_controll_config_temp_t *camConfigTo) { - int ret = 0; - - if (CCstatus.CamSensor_id == OV2640_PID) - { - ret = s->set_gainceiling(s, gainceilingLevel); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128) - } - else - { - int _level = (1 << ((int)gainceilingLevel + 1)); + camConfigTo->CamXclkFreqMhz = camConfigFrom->CamXclkFreqMhz; - ret = s->set_reg(s, 0x3A18, 0xFF, (_level >> 8) & 3) || s->set_reg(s, 0x3A19, 0xFF, _level & 0xFF); + camConfigTo->ImageFrameSize = camConfigFrom->ImageFrameSize; - if (ret == 0) + camConfigTo->ImageContrast = camConfigFrom->ImageContrast; + camConfigTo->ImageBrightness = camConfigFrom->ImageBrightness; + camConfigTo->ImageSaturation = camConfigFrom->ImageSaturation; + + camConfigTo->ImageQuality = camConfigFrom->ImageQuality; + + camConfigTo->ImageGainceiling = camConfigFrom->ImageGainceiling; + + camConfigTo->ImageAgc = camConfigFrom->ImageAgc; + camConfigTo->ImageAec = camConfigFrom->ImageAec; + camConfigTo->ImageHmirror = camConfigFrom->ImageHmirror; + camConfigTo->ImageVflip = camConfigFrom->ImageVflip; + + camConfigTo->ImageAwb = camConfigFrom->ImageAwb; + camConfigTo->ImageAec2 = camConfigFrom->ImageAec2; + camConfigTo->ImageAecValue = camConfigFrom->ImageAecValue; + camConfigTo->ImageSpecialEffect = camConfigFrom->ImageSpecialEffect; + camConfigTo->ImageWbMode = camConfigFrom->ImageWbMode; + camConfigTo->ImageAeLevel = camConfigFrom->ImageAeLevel; + + camConfigTo->ImageDcw = camConfigFrom->ImageDcw; + camConfigTo->ImageBpc = camConfigFrom->ImageBpc; + camConfigTo->ImageWpc = camConfigFrom->ImageWpc; + camConfigTo->ImageAwbGain = camConfigFrom->ImageAwbGain; + camConfigTo->ImageAgcGain = camConfigFrom->ImageAgcGain; + + camConfigTo->ImageRawGma = camConfigFrom->ImageRawGma; + camConfigTo->ImageLenc = camConfigFrom->ImageLenc; + + camConfigTo->ImageSharpness = camConfigFrom->ImageSharpness; + camConfigTo->ImageAutoSharpness = camConfigFrom->ImageAutoSharpness; + + camConfigTo->ImageDenoiseLevel = camConfigFrom->ImageDenoiseLevel; + + camConfigTo->ImageLedIntensity = camConfigFrom->ImageLedIntensity; + + camConfigTo->ImageZoomEnabled = camConfigFrom->ImageZoomEnabled; + camConfigTo->ImageZoomOffsetX = camConfigFrom->ImageZoomOffsetX; + camConfigTo->ImageZoomOffsetY = camConfigFrom->ImageZoomOffsetY; + camConfigTo->ImageZoomSize = camConfigFrom->ImageZoomSize; + + camConfigTo->WaitBeforePicture = camConfigFrom->WaitBeforePicture; + + return ESP_OK; +} + +int CCamera::check_camera_settings_changed(void) +{ + int ret = 0; + + set_camera_deep_sleep(false); + + // wenn die Kameraeinstellungen durch Erstellen eines neuen Referenzbildes verändert wurden, müssen sie neu gesetzt werden + if (Camera.changedCameraSettings) + { + if (Camera.CamTempImage) { - // ESP_LOGD(TAG, "Set gainceiling to: %d", gainceilingLevel); - s->status.gainceiling = gainceilingLevel; + Camera.set_sensor_controll_config(&CFstatus); // CFstatus >>> Kamera + Camera.set_quality_zoom_size(&CFstatus); + Camera.LedIntensity = CFstatus.ImageLedIntensity; + Camera.CamTempImage = false; + } + else + { + Camera.set_sensor_controll_config(&CCstatus); // CCstatus >>> Kamera + Camera.set_quality_zoom_size(&CCstatus); + Camera.LedIntensity = CCstatus.ImageLedIntensity; + Camera.changedCameraSettings = false; } } return ret; } -void CCamera::SetCamSharpness(bool autoSharpnessEnabled, int sharpnessLevel) +// only available on OV3660 and OV5640 +// https://github.com/espressif/esp32-camera/issues/672 +int CCamera::set_camera_deep_sleep(bool enable) { - sensor_t *s = esp_camera_sensor_get(); - - if (s != NULL) + int ret = 0; + if (Camera.CamDeepSleepEnable != enable) { - if (CCstatus.CamSensor_id == OV2640_PID) + Camera.CamDeepSleepEnable = enable; + sensor_t *cam_sensor = esp_camera_sensor_get(); + if (cam_sensor != NULL) + { + std::string state = "unsupported"; + if (Camera.CamSensorId == OV2640_PID) + { + // OV2640 Standby mode + uint8_t reg = cam_sensor->get_reg(cam_sensor, 0x09, 0xFF); + ret = cam_sensor->set_reg(cam_sensor, 0x09, 0xFF, enable ? (reg |= 0x10) : (reg &= ~0x10)); + state = enable ? "enabled" : "disabled"; + } + else if ((Camera.CamSensorId == OV3660_PID) || (Camera.CamSensorId == OV5640_PID)) + { + // OV3660/OV5640 DeepSleep mode + ret = cam_sensor->set_reg(cam_sensor, 0x3008, 0x42, enable ? 0x42 : 0x02); + state = enable ? "enabled" : "disabled"; + } + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "DeepSleep: " + state); + vTaskDelay(200 / portTICK_PERIOD_MS); + } + else + { + return -1; + } + } + + return ret; +} + +// on the OV5640, gainceiling must be set with the real value (x2>>>gainceilingLevel = 2, .... x128>>>gainceilingLevel = 128) +int CCamera::set_camera_gainceiling(sensor_t *cam_sensor, gainceiling_t gainceilingLevel) +{ + int ret = 0; + + if (Camera.CamSensorId == OV2640_PID) + { + ret = cam_sensor->set_gainceiling(cam_sensor, gainceilingLevel); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128) + } + else + { + int _level = (1 << ((int)gainceilingLevel + 1)); + ret = cam_sensor->set_reg(cam_sensor, 0x3A18, 0xFF, (_level >> 8) & 3) || cam_sensor->set_reg(cam_sensor, 0x3A19, 0xFF, _level & 0xFF); + + if (ret == 0) + { + // ESP_LOGD(TAG, "Set gainceiling to: %d", gainceilingLevel); + cam_sensor->status.gainceiling = gainceilingLevel; + } + } + + return ret; +} + +void CCamera::set_camera_sharpness(bool autoSharpnessEnabled, int sharpnessLevel) +{ + sensor_t *cam_sensor = esp_camera_sensor_get(); + + if (cam_sensor != NULL) + { + if (Camera.CamSensorId == OV2640_PID) { sharpnessLevel = min(2, max(-2, sharpnessLevel)); // The OV2640 does not officially support sharpness, so the detour is made with the ov2640_sharpness.cpp. if (autoSharpnessEnabled) { - ov2640_enable_auto_sharpness(s); + ov2640_enable_auto_sharpness(cam_sensor); } else { - ov2640_set_sharpness(s, sharpnessLevel); + ov2640_set_sharpness(cam_sensor, sharpnessLevel); } } else @@ -399,11 +499,11 @@ void CCamera::SetCamSharpness(bool autoSharpnessEnabled, int sharpnessLevel) if (autoSharpnessEnabled) { // autoSharpness is not supported, default to zero - s->set_sharpness(s, 0); + cam_sensor->set_sharpness(cam_sensor, 0); } else { - s->set_sharpness(s, sharpnessLevel); + cam_sensor->set_sharpness(cam_sensor, sharpnessLevel); } } } @@ -413,28 +513,28 @@ void CCamera::SetCamSharpness(bool autoSharpnessEnabled, int sharpnessLevel) } } -void CCamera::SetCamSpecialEffect(sensor_t *s, int specialEffect) +void CCamera::set_camera_special_effect(sensor_t *cam_sensor, int specialEffect) { - if (CCstatus.CamSensor_id == OV2640_PID) + if (Camera.CamSensorId == OV2640_PID) { - ov2640_set_special_effect(s, specialEffect); + ov2640_set_special_effect(cam_sensor, specialEffect); } else { - s->set_special_effect(s, specialEffect); + cam_sensor->set_special_effect(cam_sensor, specialEffect); } } -void CCamera::SetCamContrastBrightness(sensor_t *s, int _contrast, int _brightness) +void CCamera::set_camera_contrast_brightness(sensor_t *cam_sensor, int _contrast, int _brightness) { - if (CCstatus.CamSensor_id == OV2640_PID) + if (Camera.CamSensorId == OV2640_PID) { - ov2640_set_contrast_brightness(s, _contrast, _brightness); + ov2640_set_contrast_brightness(cam_sensor, _contrast, _brightness); } else { - s->set_contrast(s, _contrast); // -2 to 2 - s->set_brightness(s, _brightness); // -2 to 2 + cam_sensor->set_contrast(cam_sensor, _contrast); // -2 to 2 + cam_sensor->set_brightness(cam_sensor, _brightness); // -2 to 2 } } @@ -442,12 +542,12 @@ void CCamera::SetCamContrastBrightness(sensor_t *s, int _contrast, int _brightne // - if imageSize = 0 then the image is not zoomed // - if imageSize = max value, then the image is fully zoomed in // - a zoom step is >>> Width + 32 px / Height + 24 px -void CCamera::SanitizeZoomParams(int imageSize, int frameSizeX, int frameSizeY, int &imageWidth, int &imageHeight, int &zoomOffsetX, int &zoomOffsetY) +void CCamera::sanitize_zoom_params(camera_controll_config_temp_t *camConfig, int imageSize, int frameSizeX, int frameSizeY, int &imageWidth, int &imageHeight, int &zoomOffsetX, int &zoomOffsetY) { // for OV2640, This works only if the aspect ratio of 4:3 is preserved in the window size. // use only values divisible by 8 without remainder - imageWidth = CCstatus.ImageWidth + (imageSize * 4 * 8); - imageHeight = CCstatus.ImageHeight + (imageSize * 3 * 8); + imageWidth = camConfig->ImageWidth + (imageSize * 4 * 8); + imageHeight = camConfig->ImageHeight + (imageSize * 3 * 8); int _maxX = frameSizeX - imageWidth; int _maxY = frameSizeY - imageHeight; @@ -499,61 +599,61 @@ void CCamera::SanitizeZoomParams(int imageSize, int frameSizeX, int frameSizeY, } } -void CCamera::SetZoomSize(bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, int imageSize, int imageVflip) +void CCamera::set_zoom_size(camera_controll_config_temp_t *camConfig) { - sensor_t *s = esp_camera_sensor_get(); + sensor_t *cam_sensor = esp_camera_sensor_get(); - if (s != NULL) + if (cam_sensor != NULL) { - if (zoomEnabled) + if (camConfig->ImageZoomEnabled) { int _imageSize_temp = 0; - int _imageWidth = CCstatus.ImageWidth; - int _imageHeight = CCstatus.ImageHeight; - int _offsetx = zoomOffsetX; - int _offsety = zoomOffsetY; + int _imageWidth = camConfig->ImageWidth; + int _imageHeight = camConfig->ImageHeight; + int _offsetx = camConfig->ImageZoomOffsetX; + int _offsety = camConfig->ImageZoomOffsetY; int frameSizeX; int frameSizeY; - switch (CCstatus.CamSensor_id) + switch (Camera.CamSensorId) { case OV5640_PID: frameSizeX = 2592; frameSizeY = 1944; - // max imageSize = ((frameSizeX - CCstatus.ImageWidth) / 8 / 4) - 1 + // max imageSize = ((frameSizeX - camConfig->ImageWidth) / 8 / 4) - 1 // 59 = ((2560 - 640) / 8 / 4) - 1 - if (imageSize < 59) + if (camConfig->ImageZoomSize < 59) { - _imageSize_temp = (59 - imageSize); + _imageSize_temp = (59 - camConfig->ImageZoomSize); } - SanitizeZoomParams(_imageSize_temp, frameSizeX, frameSizeY, _imageWidth, _imageHeight, _offsetx, _offsety); - SetCamWindow(s, frameSizeX, frameSizeY, _offsetx, _offsety, _imageWidth, _imageHeight, CCstatus.ImageWidth, CCstatus.ImageHeight, imageVflip); + sanitize_zoom_params(camConfig, _imageSize_temp, frameSizeX, frameSizeY, _imageWidth, _imageHeight, _offsetx, _offsety); + set_camera_window(cam_sensor, frameSizeX, frameSizeY, _offsetx, _offsety, _imageWidth, _imageHeight, camConfig->ImageWidth, camConfig->ImageHeight, camConfig->ImageVflip); break; case OV3660_PID: frameSizeX = 2048; frameSizeY = 1536; - // max imageSize = ((frameSizeX - CCstatus.ImageWidth) / 8 / 4) -1 + // max imageSize = ((frameSizeX - camConfig->ImageWidth) / 8 / 4) -1 // 43 = ((2048 - 640) / 8 / 4) - 1 - if (imageSize < 43) + if (camConfig->ImageZoomSize < 43) { - _imageSize_temp = (43 - imageSize); + _imageSize_temp = (43 - camConfig->ImageZoomSize); } - SanitizeZoomParams(_imageSize_temp, frameSizeX, frameSizeY, _imageWidth, _imageHeight, _offsetx, _offsety); - SetCamWindow(s, frameSizeX, frameSizeY, _offsetx, _offsety, _imageWidth, _imageHeight, CCstatus.ImageWidth, CCstatus.ImageHeight, imageVflip); + sanitize_zoom_params(camConfig, _imageSize_temp, frameSizeX, frameSizeY, _imageWidth, _imageHeight, _offsetx, _offsety); + set_camera_window(cam_sensor, frameSizeX, frameSizeY, _offsetx, _offsety, _imageWidth, _imageHeight, camConfig->ImageWidth, camConfig->ImageHeight, camConfig->ImageVflip); break; case OV2640_PID: frameSizeX = 1600; frameSizeY = 1200; - // max imageSize = ((frameSizeX - CCstatus.ImageWidth) / 8 / 4) -1 + // max imageSize = ((frameSizeX - camConfig->ImageWidth) / 8 / 4) -1 // 29 = ((1600 - 640) / 8 / 4) - 1 - if (imageSize < 29) + if (camConfig->ImageZoomSize < 29) { - _imageSize_temp = (29 - imageSize); + _imageSize_temp = (29 - camConfig->ImageZoomSize); } - SanitizeZoomParams(_imageSize_temp, frameSizeX, frameSizeY, _imageWidth, _imageHeight, _offsetx, _offsety); - SetCamWindow(s, frameSizeX, frameSizeY, _offsetx, _offsety, _imageWidth, _imageHeight, CCstatus.ImageWidth, CCstatus.ImageHeight, imageVflip); + sanitize_zoom_params(camConfig, _imageSize_temp, frameSizeX, frameSizeY, _imageWidth, _imageHeight, _offsetx, _offsety); + set_camera_window(cam_sensor, frameSizeX, frameSizeY, _offsetx, _offsety, _imageWidth, _imageHeight, camConfig->ImageWidth, camConfig->ImageHeight, camConfig->ImageVflip); break; default: @@ -563,27 +663,27 @@ void CCamera::SetZoomSize(bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, in } else { - s->set_framesize(s, CCstatus.ImageFrameSize); + cam_sensor->set_framesize(cam_sensor, camConfig->ImageFrameSize); } } } -void CCamera::SetQualityZoomSize(int qual, framesize_t resol, bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, int imageSize, int imageVflip) +void CCamera::set_quality_zoom_size(camera_controll_config_temp_t *camConfig) { - sensor_t *s = esp_camera_sensor_get(); + sensor_t *cam_sensor = esp_camera_sensor_get(); // OV2640 has no lower limit on jpeg quality - if (CCstatus.CamSensor_id == OV5640_PID) + if (Camera.CamSensorId == OV5640_PID) { - qual = min(63, max(8, qual)); + camConfig->ImageQuality = min(63, max(8, camConfig->ImageQuality)); } - SetImageWidthHeightFromResolution(resol); + set_image_width_height_from_resolution(camConfig->ImageFrameSize); - if (s != NULL) + if (cam_sensor != NULL) { - s->set_quality(s, qual); - SetZoomSize(zoomEnabled, zoomOffsetX, zoomOffsetY, imageSize, imageVflip); + cam_sensor->set_quality(cam_sensor, camConfig->ImageQuality); + set_zoom_size(camConfig); } else { @@ -591,11 +691,11 @@ void CCamera::SetQualityZoomSize(int qual, framesize_t resol, bool zoomEnabled, } } -void CCamera::SetCamWindow(sensor_t *s, int frameSizeX, int frameSizeY, int xOffset, int yOffset, int xTotal, int yTotal, int xOutput, int yOutput, int imageVflip) +void CCamera::set_camera_window(sensor_t *cam_sensor, int frameSizeX, int frameSizeY, int xOffset, int yOffset, int xTotal, int yTotal, int xOutput, int yOutput, int imageVflip) { - if (CCstatus.CamSensor_id == OV2640_PID) + if (Camera.CamSensorId == OV2640_PID) { - s->set_res_raw(s, 0, 0, 0, 0, xOffset, yOffset, xTotal, yTotal, xOutput, yOutput, false, false); + cam_sensor->set_res_raw(cam_sensor, 0, 0, 0, 0, xOffset, yOffset, xTotal, yTotal, xOutput, yOutput, false, false); } else { @@ -605,11 +705,11 @@ void CCamera::SetCamWindow(sensor_t *s, int frameSizeX, int frameSizeY, int xOff if (imageVflip == true) { - s->set_res_raw(s, xOffset, yOffset, xOffset + xTotal - 1, yOffset + yTotal - 1, 0, 0, frameSizeX, frameSizeY, xOutput, yOutput, scale, binning); + cam_sensor->set_res_raw(cam_sensor, xOffset, yOffset, xOffset + xTotal - 1, yOffset + yTotal - 1, 0, 0, frameSizeX, frameSizeY, xOutput, yOutput, scale, binning); } else { - s->set_res_raw(s, xOffset, yOffset, xOffset + xTotal, yOffset + yTotal, 0, 0, frameSizeX, frameSizeY, xOutput, yOutput, scale, binning); + cam_sensor->set_res_raw(cam_sensor, xOffset, yOffset, xOffset + xTotal, yOffset + yTotal, 0, 0, frameSizeX, frameSizeY, xOutput, yOutput, scale, binning); } } } @@ -633,104 +733,96 @@ static size_t jpg_encode_stream(void *arg, size_t index, const void *data, size_ return len; } -esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay) +esp_err_t CCamera::capture_to_basis_image(CImageBasis *_Image, int flash_duration) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CaptureToBasisImage - Start"); -#endif + Camera.check_camera_settings_changed(); _Image->EmptyImage(); // Delete previous stored raw image -> black image - LEDOnOff(true); // Status-LED on + set_blink_led_on_off(true); // Status-LED on - if (delay > 0) + if (flash_duration > 0) { - LightOnOff(true); // Flash-LED on - const TickType_t xDelay = delay / portTICK_PERIOD_MS; + CaptureToBasisImageLed = true; + set_flash_light_on_off(true); // Flash-LED on + const TickType_t xDelay = flash_duration / portTICK_PERIOD_MS; vTaskDelay(xDelay); } -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CaptureToBasisImage - After LightOn"); -#endif - camera_fb_t *fb = esp_camera_fb_get(); esp_camera_fb_return(fb); fb = esp_camera_fb_get(); if (!fb) { - LEDOnOff(false); // Status-LED off - LightOnOff(false); // Flash-LED off + CaptureToBasisImageLed = false; + if (!CaptureToFileLed && !CaptureToHTTPLed && !CaptureToStreamLed) + { + set_blink_led_on_off(false); // Status-LED off + set_camera_deep_sleep(true); + if (flash_duration > 0) + { + set_flash_light_on_off(false); // Flash-LED off + } + } LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "is not working anymore (CaptureToBasisImage) - most probably caused " "by a hardware problem (instablility, ...). System will reboot."); - doReboot(); return ESP_FAIL; } - if (CCstatus.DemoMode) + if (Camera.DemoMode) { // Use images stored on SD-Card instead of camera image /* Replace Framebuffer with image from SD-Card */ - loadNextDemoImage(fb); + load_next_demo_image(fb); } - CImageBasis *_zwImage = new CImageBasis("zwImage"); - - if (_zwImage) + CImageBasis *_TempImage = new CImageBasis("TempImage"); + if (_TempImage) { - _zwImage->LoadFromMemory(fb->buf, fb->len); + _TempImage->LoadFromMemory(fb->buf, fb->len); } else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToBasisImage: Can't allocate _zwImage"); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToBasisImage: Can't allocate TempImage"); } esp_camera_fb_return(fb); -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CaptureToBasisImage - After fb_get"); -#endif - - LEDOnOff(false); // Status-LED off - - if (delay > 0) + CaptureToBasisImageLed = false; + if (!CaptureToFileLed && !CaptureToHTTPLed && !CaptureToStreamLed) { - LightOnOff(false); // Flash-LED off + set_blink_led_on_off(false); // Status-LED off + set_camera_deep_sleep(true); + if (flash_duration > 0) + { + set_flash_light_on_off(false); // Flash-LED off + } } - // TickType_t xDelay = 1000 / portTICK_PERIOD_MS; - // vTaskDelay( xDelay ); // wait for power to recover - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CaptureToBasisImage - After LoadFromMemory"); -#endif - - if (_zwImage == NULL) + if (_TempImage == NULL) { return ESP_OK; } - stbi_uc *p_target; - stbi_uc *p_source; int channels = 3; + int width = CCstatus.ImageWidth; int height = CCstatus.ImageHeight; - -#ifdef DEBUG_DETAIL_ON - std::string _zw = "Targetimage: " + std::to_string((int)_Image->rgb_image) + " Size: " + std::to_string(_Image->width) + ", " + std::to_string(_Image->height); - _zw = _zw + " _zwImage: " + std::to_string((int)_zwImage->rgb_image) + " Size: " + std::to_string(_zwImage->width) + ", " + std::to_string(_zwImage->height); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, _zw); -#endif + if (Camera.CamTempImage) + { + width = CFstatus.ImageWidth; + height = CFstatus.ImageHeight; + } for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) { - p_target = _Image->rgb_image + (channels * (y * width + x)); - p_source = _zwImage->rgb_image + (channels * (y * width + x)); + stbi_uc *p_target = _Image->rgb_image + (channels * (y * width + x)); + stbi_uc *p_source = _TempImage->rgb_image + (channels * (y * width + x)); for (int c = 0; c < channels; c++) { @@ -739,25 +831,28 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay) } } - delete _zwImage; - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CaptureToBasisImage - Done"); -#endif + delete _TempImage; return ESP_OK; } -esp_err_t CCamera::CaptureToFile(std::string nm, int delay) +esp_err_t CCamera::capture_to_file(std::string file_name, int flash_duration) { - string ftype; + Camera.check_camera_settings_changed(); - LEDOnOff(true); // Status-LED on - - if (delay > 0) + int _ImageQuality = CCstatus.ImageQuality; + if (Camera.CamTempImage) { - LightOnOff(true); // Flash-LED on - const TickType_t xDelay = delay / portTICK_PERIOD_MS; + _ImageQuality = CFstatus.ImageQuality; + } + + set_blink_led_on_off(true); // Status-LED on + + if (flash_duration > 0) + { + CaptureToFileLed = true; + set_flash_light_on_off(true); // Flash-LED on + const TickType_t xDelay = flash_duration / portTICK_PERIOD_MS; vTaskDelay(xDelay); } @@ -767,32 +862,26 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay) if (!fb) { - LEDOnOff(false); // Status-LED off - LightOnOff(false); // Flash-LED off + CaptureToFileLed = false; + if (!CaptureToBasisImageLed && !CaptureToHTTPLed && !CaptureToStreamLed) + { + set_blink_led_on_off(false); // Status-LED off + set_camera_deep_sleep(true); + if (flash_duration > 0) + { + set_flash_light_on_off(false); // Flash-LED off + } + } + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. " "Check camera module and/or proper electrical connection"); - // doReboot(); return ESP_FAIL; } - LEDOnOff(false); // Status-LED off + file_name = format_filename(file_name); -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "w %d, h %d, size %d", fb->width, fb->height, fb->len); -#endif - - nm = FormatFileName(nm); - -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Save Camera to: %s", nm.c_str()); -#endif - - ftype = toUpper(getFileType(nm)); - -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Filetype: %s", ftype.c_str()); -#endif + std::string ftype = to_upper(get_file_type(file_name)); uint8_t *buf = NULL; size_t buf_len = 0; @@ -808,7 +897,7 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay) { if (fb->format != PIXFORMAT_JPEG) { - bool jpeg_converted = frame2jpg(fb, CCstatus.ImageQuality, &buf, &buf_len); + bool jpeg_converted = frame2jpg(fb, (100 - _ImageQuality), &buf, &buf_len); converted = true; if (!jpeg_converted) @@ -823,12 +912,11 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay) } } - FILE *fp = fopen(nm.c_str(), "wb"); - + FILE *fp = fopen(file_name.c_str(), "wb"); if (fp == NULL) { // If an error occurs during the file creation - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Failed to open file " + nm); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Failed to open file " + file_name); } else { @@ -843,26 +931,35 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay) esp_camera_fb_return(fb); - if (delay > 0) + CaptureToFileLed = false; + if (!CaptureToBasisImageLed && !CaptureToHTTPLed && !CaptureToStreamLed) { - LightOnOff(false); // Flash-LED off + set_blink_led_on_off(false); // Status-LED off + set_camera_deep_sleep(true); + if (flash_duration > 0) + { + set_flash_light_on_off(false); // Flash-LED off + } } return ESP_OK; } -esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay) +esp_err_t CCamera::capture_to_http(httpd_req_t *req, int flash_duration) { esp_err_t res = ESP_OK; size_t fb_len = 0; int64_t fr_start = esp_timer_get_time(); - LEDOnOff(true); // Status-LED on + Camera.check_camera_settings_changed(); - if (delay > 0) + set_blink_led_on_off(true); // Status-LED on + + if (flash_duration > 0) { - LightOnOff(true); // Flash-LED on - const TickType_t xDelay = delay / portTICK_PERIOD_MS; + CaptureToHTTPLed = true; + set_flash_light_on_off(true); // Flash-LED on + const TickType_t xDelay = flash_duration / portTICK_PERIOD_MS; vTaskDelay(xDelay); } @@ -872,17 +969,24 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay) if (!fb) { - LEDOnOff(false); // Status-LED off - LightOnOff(false); // Flash-LED off + CaptureToHTTPLed = false; + if (!CaptureToBasisImageLed && !CaptureToFileLed && !CaptureToStreamLed) + { + set_blink_led_on_off(false); // Status-LED off + set_camera_deep_sleep(true); + if (flash_duration > 0) + { + set_flash_light_on_off(false); // Flash-LED off + } + } + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CaptureToFile: Capture Failed. " "Check camera module and/or proper electrical connection"); httpd_resp_send_500(req); - // doReboot(); return ESP_FAIL; } - LEDOnOff(false); // Status-LED off res = httpd_resp_set_type(req, "image/jpeg"); if (res == ESP_OK) @@ -892,12 +996,12 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay) if (res == ESP_OK) { - if (CCstatus.DemoMode) + if (Camera.DemoMode) { // Use images stored on SD-Card instead of camera image LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Using Demo image!"); /* Replace Framebuffer with image from SD-Card */ - loadNextDemoImage(fb); + load_next_demo_image(fb); res = httpd_resp_send(req, (const char *)fb->buf, fb->len); } @@ -910,8 +1014,14 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay) } else { + int _ImageQuality = CCstatus.ImageQuality; + if (Camera.CamTempImage) + { + _ImageQuality = CFstatus.ImageQuality; + } + jpg_chunking_t jchunk = {req, 0}; - res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk) ? ESP_OK : ESP_FAIL; + res = frame2jpg_cb(fb, (100 - _ImageQuality), jpg_encode_stream, &jchunk) ? ESP_OK : ESP_FAIL; httpd_resp_send_chunk(req, NULL, 0); fb_len = jchunk.len; } @@ -919,50 +1029,47 @@ esp_err_t CCamera::CaptureToHTTP(httpd_req_t *req, int delay) } esp_camera_fb_return(fb); - int64_t fr_end = esp_timer_get_time(); - ESP_LOGI(TAG, "JPG: %dKB %dms", (int)(fb_len / 1024), (int)((fr_end - fr_start) / 1000)); - - if (delay > 0) + CaptureToHTTPLed = false; + if (!CaptureToBasisImageLed && !CaptureToFileLed && !CaptureToStreamLed) { - LightOnOff(false); // Flash-LED off + set_blink_led_on_off(false); // Status-LED off + set_camera_deep_sleep(true); + if (flash_duration > 0) + { + set_flash_light_on_off(false); // Flash-LED off + } } + int64_t fr_end = esp_timer_get_time(); + ESP_LOGI(TAG, "JPG: %dKB %dms", (int)(fb_len / 1024), (int)((fr_end - fr_start) / 1000)); + return res; } -esp_err_t CCamera::CaptureToStream(httpd_req_t *req, bool FlashlightOn) +esp_err_t CCamera::capture_to_stream(httpd_req_t *req, bool FlashlightOn) { esp_err_t res = ESP_OK; - size_t fb_len = 0; - int64_t fr_start; - char *part_buf[64]; - // wenn die Kameraeinstellungen durch Erstellen eines neuen Referenzbildes verändert wurden, müssen sie neu gesetzt werden - if (CFstatus.changedCameraSettings) - { - Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera - Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip); - Camera.LedIntensity = CCstatus.ImageLedIntensity; - CFstatus.changedCameraSettings = false; - } + Camera.check_camera_settings_changed(); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Live stream started"); + set_blink_led_on_off(true); // Status-LED on + if (FlashlightOn) { - LEDOnOff(true); // Status-LED on - LightOnOff(true); // Flash-LED on + CaptureToStreamLed = true; + set_flash_light_on_off(true); // Flash-LED on } - // httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); //stream is blocking web interface, only serving to local - httpd_resp_set_type(req, _STREAM_CONTENT_TYPE); httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); while (1) { - fr_start = esp_timer_get_time(); + int64_t fr_start = esp_timer_get_time(); + camera_fb_t *fb = esp_camera_fb_get(); esp_camera_fb_return(fb); fb = esp_camera_fb_get(); @@ -973,10 +1080,11 @@ esp_err_t CCamera::CaptureToStream(httpd_req_t *req, bool FlashlightOn) break; } - fb_len = fb->len; + size_t fb_len = fb->len; if (res == ESP_OK) { + char *part_buf[64]; size_t hlen = snprintf((char *)part_buf, sizeof(part_buf), _STREAM_PART, fb_len); res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen); } @@ -1012,15 +1120,23 @@ esp_err_t CCamera::CaptureToStream(httpd_req_t *req, bool FlashlightOn) } } - LEDOnOff(false); // Status-LED off - LightOnOff(false); // Flash-LED off + CaptureToStreamLed = false; + if (!CaptureToBasisImageLed && !CaptureToFileLed && !CaptureToHTTPLed) + { + set_blink_led_on_off(false); // Status-LED off + set_camera_deep_sleep(true); + if (FlashlightOn) + { + set_flash_light_on_off(false); // Flash-LED off + } + } LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Live stream stopped"); return res; } -void CCamera::LightOnOff(bool status) +void CCamera::set_flash_light_on_off(bool status) { GpioHandler *gpioHandler = gpio_handler_get(); @@ -1031,40 +1147,43 @@ void CCamera::LightOnOff(bool status) } else { -#ifdef USE_PWM_LEDFLASH - if (status) + if (FLASH_MODE == GPIO_PIN_MODE_BUILTIN_FLASH_PWM) { - ESP_LOGD(TAG, "Internal Flash-LED turn on with PWM %d", Camera.LedIntensity); - ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, Camera.LedIntensity)); - // Update duty to apply the new value - ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL)); + if (status) + { + ESP_LOGD(TAG, "Internal Flash-LED turn on with PWM %d", Camera.LedIntensity); + ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, Camera.LedIntensity)); + // Update duty to apply the new value + ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL)); + } + else + { + ESP_LOGD(TAG, "Internal Flash-LED turn off PWM"); + ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0)); + ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL)); + } } else { - ESP_LOGD(TAG, "Internal Flash-LED turn off PWM"); - ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0)); - ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL)); - } -#else - // Init the GPIO - gpio_pad_select_gpio(FLASH_GPIO); + // Init the GPIO + gpio_pad_select_gpio(FLASH_GPIO); - // Set the GPIO as a push/pull output - gpio_set_direction(FLASH_GPIO, GPIO_MODE_OUTPUT); + // Set the GPIO as a push/pull output + gpio_set_direction(FLASH_GPIO, GPIO_MODE_OUTPUT); - if (status) - { - gpio_set_level(FLASH_GPIO, 1); + if (status) + { + gpio_set_level(FLASH_GPIO, 1); + } + else + { + gpio_set_level(FLASH_GPIO, 0); + } } - else - { - gpio_set_level(FLASH_GPIO, 0); - } -#endif } } -void CCamera::LEDOnOff(bool status) +void CCamera::set_blink_led_on_off(bool status) { if (xHandle_task_StatusLED == NULL) { @@ -1085,7 +1204,7 @@ void CCamera::LEDOnOff(bool status) } } -void CCamera::SetImageWidthHeightFromResolution(framesize_t resol) +void CCamera::set_image_width_height_from_resolution(framesize_t resol) { if (resol == FRAMESIZE_QVGA) { @@ -1144,7 +1263,7 @@ void CCamera::SetImageWidthHeightFromResolution(framesize_t resol) } } -framesize_t CCamera::TextToFramesize(const char *_size) +framesize_t CCamera::text_to_framesize(const char *_size) { if (strcmp(_size, "QVGA") == 0) { @@ -1190,12 +1309,9 @@ framesize_t CCamera::TextToFramesize(const char *_size) // return CCstatus.ImageFrameSize; } -std::vector demoFiles; - -void CCamera::useDemoMode(void) +void CCamera::use_demo_mode(void) { char line[50]; - FILE *fd = fopen("/sdcard/demo/files.txt", "r"); if (!fd) @@ -1228,30 +1344,27 @@ void CCamera::useDemoMode(void) LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, file); } - CCstatus.DemoMode = true; + Camera.DemoMode = true; } -bool CCamera::loadNextDemoImage(camera_fb_t *fb) +bool CCamera::load_next_demo_image(camera_fb_t *fb) { char filename[50]; int readBytes; long fileSize; snprintf(filename, sizeof(filename), "/sdcard/demo/%s", demoFiles[getCountFlowRounds() % demoFiles.size()].c_str()); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Using " + std::string(filename) + " as demo image"); /* Inject saved image */ - FILE *fp = fopen(filename, "rb"); - if (!fp) { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(filename) + "!"); return false; } - fileSize = GetFileSize(filename); + fileSize = get_file_size(filename); if (fileSize > DEMO_IMAGE_SIZE) { @@ -1272,7 +1385,7 @@ bool CCamera::loadNextDemoImage(camera_fb_t *fb) return true; } -long CCamera::GetFileSize(std::string filename) +long CCamera::get_file_size(std::string filename) { struct stat stat_buf; long rc = stat(filename.c_str(), &stat_buf); diff --git a/code/components/jomjol_controlcamera/ClassControllCamera.h b/code/components/jomjol_controlcamera/ClassControllCamera.h index 51e09191..ed3bd559 100644 --- a/code/components/jomjol_controlcamera/ClassControllCamera.h +++ b/code/components/jomjol_controlcamera/ClassControllCamera.h @@ -3,21 +3,21 @@ #ifndef CLASSCONTROLLCAMERA_H #define CLASSCONTROLLCAMERA_H -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "freertos/event_groups.h" - -#include "esp_camera.h" #include #include + +#include +#include +#include +#include + +#include "defines.h" +#include "esp_camera.h" #include "CImageBasis.h" -#include "../../include/defines.h" typedef struct { - uint16_t CamSensor_id; + int CamXclkFreqMhz; framesize_t ImageFrameSize = FRAMESIZE_VGA; // 0 - 10 gainceiling_t ImageGainceiling; // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128) @@ -59,59 +59,75 @@ typedef struct int ImageZoomSize; int WaitBeforePicture; - bool isImageSize; - - bool CameraInitSuccessful; - bool changedCameraSettings; - bool DemoMode; - bool SaveAllFiles; } camera_controll_config_temp_t; extern camera_controll_config_temp_t CCstatus; +extern camera_controll_config_temp_t CFstatus; class CCamera { protected: void ledc_init(void); - bool loadNextDemoImage(camera_fb_t *fb); - long GetFileSize(std::string filename); - void SetCamWindow(sensor_t *s, int frameSizeX, int frameSizeY, int xOffset, int yOffset, int xTotal, int yTotal, int xOutput, int yOutput, int imageVflip); - void SetImageWidthHeightFromResolution(framesize_t resol); - void SanitizeZoomParams(int imageSize, int frameSizeX, int frameSizeY, int &imageWidth, int &imageHeight, int &zoomOffsetX, int &zoomOffsetY); + bool load_next_demo_image(camera_fb_t *fb); + long get_file_size(std::string filename); + void set_camera_window(sensor_t *cam_sensor, int frameSizeX, int frameSizeY, int xOffset, int yOffset, int xTotal, int yTotal, int xOutput, int yOutput, int imageVflip); + void set_image_width_height_from_resolution(framesize_t resol); + void sanitize_zoom_params(camera_controll_config_temp_t *camConfig, int imageSize, int frameSizeX, int frameSizeY, int &imageWidth, int &imageHeight, int &zoomOffsetX, int &zoomOffsetY); public: + uint16_t CamSensorId = OV2640_PID; + int LedIntensity = 4096; + bool CaptureToBasisImageLed = false; + bool CaptureToFileLed = false; + bool CaptureToHTTPLed = false; + bool CaptureToStreamLed = false; + + bool DemoMode = false; + bool SaveAllFiles = false; + bool ImageAntialiasing = false; + float ImageInitialRotate = 0.0; + bool ImageInitialFlip = false; + + bool CamDeepSleepEnable = false; + bool CameraInitSuccessful = false; + bool changedCameraSettings = false; + bool CamTempImage = false; CCamera(void); - esp_err_t InitCam(void); + esp_err_t init_camera(void); + void power_reset_camera(void); - void LightOnOff(bool status); - void LEDOnOff(bool status); + void set_flash_light_on_off(bool status); + void set_blink_led_on_off(bool status); - esp_err_t setSensorDatenFromCCstatus(void); - esp_err_t getSensorDatenToCCstatus(void); + esp_err_t set_sensor_controll_config(camera_controll_config_temp_t *camConfig); + esp_err_t get_sensor_controll_config(camera_controll_config_temp_t *camConfig); + esp_err_t set_camera_config_from_to(camera_controll_config_temp_t *camConfigFrom, camera_controll_config_temp_t *camConfigTo); - int SetCamGainceiling(sensor_t *s, gainceiling_t gainceilingLevel); - void SetCamSharpness(bool autoSharpnessEnabled, int sharpnessLevel); - void SetCamSpecialEffect(sensor_t *s, int specialEffect); - void SetCamContrastBrightness(sensor_t *s, int _contrast, int _brightness); + int check_camera_settings_changed(void); + int set_camera_deep_sleep(bool enable); - esp_err_t CaptureToHTTP(httpd_req_t *req, int delay = 0); - esp_err_t CaptureToStream(httpd_req_t *req, bool FlashlightOn); + int set_camera_gainceiling(sensor_t *cam_sensor, gainceiling_t gainceilingLevel); + void set_camera_sharpness(bool autoSharpnessEnabled, int sharpnessLevel); + void set_camera_special_effect(sensor_t *cam_sensor, int specialEffect); + void set_camera_contrast_brightness(sensor_t *cam_sensor, int _contrast, int _brightness); - void SetQualityZoomSize(int qual, framesize_t resol, bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, int imageSize, int imageVflip); - void SetZoomSize(bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, int imageSize, int imageVflip); + esp_err_t capture_to_http(httpd_req_t *req, int flash_duration = 0); + esp_err_t capture_to_stream(httpd_req_t *req, bool FlashlightOn); - int SetLEDIntensity(int _intrel); - bool testCamera(void); - bool getCameraInitSuccessful(void); - void useDemoMode(void); + void set_quality_zoom_size(camera_controll_config_temp_t *camConfig); + void set_zoom_size(camera_controll_config_temp_t *camConfig); - framesize_t TextToFramesize(const char *text); + int set_led_intensity(int _intrel); + bool get_camera_init_successful(void); + void use_demo_mode(void); - esp_err_t CaptureToFile(std::string nm, int delay = 0); - esp_err_t CaptureToBasisImage(CImageBasis *_Image, int delay = 0); + framesize_t text_to_framesize(const char *text); + + esp_err_t capture_to_file(std::string file_name, int flash_duration = 0); + esp_err_t capture_to_basis_image(CImageBasis *_Image, int flash_duration = 0); }; extern CCamera Camera; -#endif \ No newline at end of file +#endif diff --git a/code/components/jomjol_controlcamera/server_camera.cpp b/code/components/jomjol_controlcamera/server_camera.cpp deleted file mode 100644 index d79fbcdb..00000000 --- a/code/components/jomjol_controlcamera/server_camera.cpp +++ /dev/null @@ -1,308 +0,0 @@ -#include "server_camera.h" - -#include -#include "string.h" - -#include "esp_camera.h" -#include "ClassControllCamera.h" -#include "MainFlowControl.h" - -#include "ClassLogFile.h" -#include "esp_log.h" - -#include "basic_auth.h" - -#include "../../include/defines.h" - -static const char *TAG = "server_cam"; - -void PowerResetCamera() -{ -#if CAM_PIN_PWDN == GPIO_NUM_NC // Use reset only if pin is available - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "No power down pin availbale to reset camera"); -#else - ESP_LOGD(TAG, "Resetting camera by power down line"); - gpio_config_t conf; - conf.intr_type = GPIO_INTR_DISABLE; - conf.pin_bit_mask = 1LL << CAM_PIN_PWDN; - conf.mode = GPIO_MODE_OUTPUT; - conf.pull_down_en = GPIO_PULLDOWN_DISABLE; - conf.pull_up_en = GPIO_PULLUP_DISABLE; - gpio_config(&conf); - - // carefull, logic is inverted compared to reset pin - gpio_set_level(CAM_PIN_PWDN, 1); - vTaskDelay(1000 / portTICK_PERIOD_MS); - gpio_set_level(CAM_PIN_PWDN, 0); - vTaskDelay(1000 / portTICK_PERIOD_MS); -#endif -} - -esp_err_t handler_lightOn(httpd_req_t *req) -{ -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_lightOn - Start"); - ESP_LOGD(TAG, "handler_lightOn uri: %s", req->uri); -#endif - - if (Camera.getCameraInitSuccessful()) - { - Camera.LightOnOff(true); - const char *resp_str = (const char *)req->user_ctx; - httpd_resp_send(req, resp_str, strlen(resp_str)); - } - else - { - httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lighton not available!"); - return ESP_ERR_NOT_FOUND; - } - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_lightOn - Done"); -#endif - - return ESP_OK; -} - -esp_err_t handler_lightOff(httpd_req_t *req) -{ -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_lightOff - Start"); - ESP_LOGD(TAG, "handler_lightOff uri: %s", req->uri); -#endif - - if (Camera.getCameraInitSuccessful()) - { - Camera.LightOnOff(false); - const char *resp_str = (const char *)req->user_ctx; - httpd_resp_send(req, resp_str, strlen(resp_str)); - } - else - { - httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lightoff not available!"); - return ESP_ERR_NOT_FOUND; - } - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_lightOff - Done"); -#endif - - return ESP_OK; -} - -esp_err_t handler_capture(httpd_req_t *req) -{ -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_capture - Start"); -#endif - - if (Camera.getCameraInitSuccessful()) - { - // If the camera settings were changed by creating a new reference image, they must be reset - if (CFstatus.changedCameraSettings) - { - Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera - Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip); - Camera.LedIntensity = CCstatus.ImageLedIntensity; - CFstatus.changedCameraSettings = false; - } - -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Size: %d, Quality: %d", CCstatus.ImageFrameSize, CCstatus.ImageQuality); -#endif - - esp_err_t result; - result = Camera.CaptureToHTTP(req); - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_capture - Done"); -#endif - - return result; - } - else - { - httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture not available!"); - return ESP_ERR_NOT_FOUND; - } -} - -esp_err_t handler_capture_with_light(httpd_req_t *req) -{ -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_capture_with_light - Start"); -#endif - - if (Camera.getCameraInitSuccessful()) - { - char _query[100]; - char _delay[10]; - int delay = 2500; - - if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) - { - ESP_LOGD(TAG, "Query: %s", _query); - - if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK) - { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Delay: %s", _delay); -#endif - delay = atoi(_delay); - - if (delay < 0) - { - delay = 0; - } - } - } - - // If the camera settings were changed by creating a new reference image, they must be reset - if (CFstatus.changedCameraSettings) - { - Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera - Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip); - Camera.LedIntensity = CCstatus.ImageLedIntensity; - CFstatus.changedCameraSettings = false; - } - -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Size: %d, Quality: %d", CCstatus.ImageFrameSize, CCstatus.ImageQuality); -#endif - - Camera.LightOnOff(true); - const TickType_t xDelay = delay / portTICK_PERIOD_MS; - vTaskDelay(xDelay); - - esp_err_t result; - result = Camera.CaptureToHTTP(req); - - Camera.LightOnOff(false); - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_capture_with_light - Done"); -#endif - - return result; - } - else - { - httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture_with_flashlight not available!"); - return ESP_ERR_NOT_FOUND; - } -} - -esp_err_t handler_capture_save_to_file(httpd_req_t *req) -{ -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_capture_save_to_file - Start"); -#endif - - if (Camera.getCameraInitSuccessful()) - { - char _query[100]; - char _delay[10]; - int delay = 0; - char filename[100]; - std::string fn = "/sdcard/"; - - if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) - { - ESP_LOGD(TAG, "Query: %s", _query); - - if (httpd_query_key_value(_query, "filename", filename, 100) == ESP_OK) - { - fn.append(filename); -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Filename: %s", fn.c_str()); -#endif - } - else - { - fn.append("noname.jpg"); - } - - if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK) - { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Delay: %s", _delay); -#endif - delay = atoi(_delay); - - if (delay < 0) - { - delay = 0; - } - } - } - else - { - fn.append("noname.jpg"); - } - - // If the camera settings were changed by creating a new reference image, they must be reset - if (CFstatus.changedCameraSettings) - { - Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera - Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip); - Camera.LedIntensity = CCstatus.ImageLedIntensity; - CFstatus.changedCameraSettings = false; - } - -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Size: %d, Quality: %d", CCstatus.ImageFrameSize, CCstatus.ImageQuality); -#endif - - esp_err_t result; - result = Camera.CaptureToFile(fn, delay); - - const char *resp_str = (const char *)fn.c_str(); - httpd_resp_send(req, resp_str, strlen(resp_str)); - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_capture_save_to_file - Done"); -#endif - - return result; - } - else - { - httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /save not available!"); - return ESP_ERR_NOT_FOUND; - } -} - -void register_server_camera_uri(httpd_handle_t server) -{ -#ifdef DEBUG_DETAIL_ON - ESP_LOGI(TAG, "server_part_camera - Registering URI handlers"); -#endif - - httpd_uri_t camuri = {}; - camuri.method = HTTP_GET; - - camuri.uri = "/lighton"; - camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_lightOn); - camuri.user_ctx = (void *)"Light On"; - httpd_register_uri_handler(server, &camuri); - - camuri.uri = "/lightoff"; - camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_lightOff); - camuri.user_ctx = (void *)"Light Off"; - httpd_register_uri_handler(server, &camuri); - - camuri.uri = "/capture"; - camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_capture); - camuri.user_ctx = NULL; - httpd_register_uri_handler(server, &camuri); - - camuri.uri = "/capture_with_flashlight"; - camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_capture_with_light); - camuri.user_ctx = NULL; - httpd_register_uri_handler(server, &camuri); - - camuri.uri = "/save"; - camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_capture_save_to_file); - camuri.user_ctx = NULL; - httpd_register_uri_handler(server, &camuri); -} diff --git a/code/components/jomjol_controlcamera/server_camera.h b/code/components/jomjol_controlcamera/server_camera.h deleted file mode 100644 index 656b99eb..00000000 --- a/code/components/jomjol_controlcamera/server_camera.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#ifndef JOMJOL_CONTROLCAMERA_H -#define JOMJOL_CONTROLCAMERA_H - -#include - -#include - -//#include "ClassControllCamera.h" - -void register_server_camera_uri(httpd_handle_t server); -void PowerResetCamera(); - -#endif \ No newline at end of file diff --git a/code/components/jomjol_fileserver_ota/CMakeLists.txt b/code/components/jomjol_fileserver_ota/CMakeLists.txt index d530eb41..6af4ab3b 100644 --- a/code/components/jomjol_fileserver_ota/CMakeLists.txt +++ b/code/components/jomjol_fileserver_ota/CMakeLists.txt @@ -1,7 +1,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." "../../include" "miniz" - REQUIRES vfs esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO) + INCLUDE_DIRS "." "../../include" + REQUIRES vfs esp_rom esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO) diff --git a/code/components/jomjol_fileserver_ota/miniz/miniz.c b/code/components/jomjol_fileserver_ota/miniz.c similarity index 96% rename from code/components/jomjol_fileserver_ota/miniz/miniz.c rename to code/components/jomjol_fileserver_ota/miniz.c index c197c181..87bdedb1 100644 --- a/code/components/jomjol_fileserver_ota/miniz/miniz.c +++ b/code/components/jomjol_fileserver_ota/miniz.c @@ -187,8 +187,6 @@ const char *mz_version(void) #ifndef MINIZ_NO_ZLIB_APIS -#ifndef MINIZ_NO_DEFLATE_APIS - int mz_deflateInit(mz_streamp pStream, int level) { return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); @@ -323,7 +321,7 @@ int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ - if ((mz_uint64)(source_len | *pDest_len) > 0xFFFFFFFFU) + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; @@ -356,10 +354,6 @@ mz_ulong mz_compressBound(mz_ulong source_len) return mz_deflateBound(NULL, source_len); } -#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ - -#ifndef MINIZ_NO_INFLATE_APIS - typedef struct { tinfl_decompressor m_decomp; @@ -566,7 +560,7 @@ int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned cha memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ - if ((mz_uint64)(*pSource_len | *pDest_len) > 0xFFFFFFFFU) + if ((*pSource_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; @@ -595,8 +589,6 @@ int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char return mz_uncompress2(pDest, pDest_len, pSource, &source_len); } -#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ - const char *mz_error(int err) { static struct @@ -674,8 +666,6 @@ const char *mz_error(int err) -#ifndef MINIZ_NO_DEFLATE_APIS - #ifdef __cplusplus extern "C" { #endif @@ -754,7 +744,7 @@ static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *p { mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; - MZ_CLEAR_ARR(hist); + MZ_CLEAR_OBJ(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; @@ -872,7 +862,7 @@ static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; - MZ_CLEAR_ARR(num_codes); + MZ_CLEAR_OBJ(num_codes); if (static_table) { for (i = 0; i < table_len; i++) @@ -898,8 +888,8 @@ static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); - MZ_CLEAR_ARR(d->m_huff_code_sizes[table_num]); - MZ_CLEAR_ARR(d->m_huff_codes[table_num]); + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); @@ -985,7 +975,7 @@ static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int } \ } -static const mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void tdefl_start_dynamic_block(tdefl_compressor *d) { @@ -1123,8 +1113,7 @@ static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; - mz_uint match_len = pLZ_codes[0]; - mz_uint match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); @@ -1169,7 +1158,7 @@ static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; - memcpy(pOutput_buf, &bit_buffer, sizeof(mz_uint64)); + *(mz_uint64 *)pOutput_buf = bit_buffer; pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; @@ -1251,8 +1240,6 @@ static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) return tdefl_compress_lz_codes(d); } -static const mz_uint s_tdefl_num_probes[11]; - static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; @@ -1273,27 +1260,8 @@ static int tdefl_flush_block(tdefl_compressor *d, int flush) if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { - const mz_uint8 cmf = 0x78; - mz_uint8 flg, flevel = 3; - mz_uint header, i, mz_un = sizeof(s_tdefl_num_probes) / sizeof(mz_uint); - - /* Determine compression level by reversing the process in tdefl_create_comp_flags_from_zip_params() */ - for (i = 0; i < mz_un; i++) - if (s_tdefl_num_probes[i] == (d->m_flags & 0xFFF)) break; - - if (i < 2) - flevel = 0; - else if (i < 6) - flevel = 1; - else if (i == 6) - flevel = 2; - - header = cmf << 8 | (flevel << 6); - header += 31 - (header % 31); - flg = header & 0xFF; - - TDEFL_PUT_BITS(cmf, 8); - TDEFL_PUT_BITS(flg, 8); + TDEFL_PUT_BITS(0x78, 8); + TDEFL_PUT_BITS(0x01, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); @@ -1764,7 +1732,7 @@ static mz_bool tdefl_compress_normal(tdefl_compressor *d) mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); - const mz_uint8 *pSrc_end = pSrc ? pSrc + num_bytes_to_process : NULL; + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) @@ -1974,8 +1942,8 @@ tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pI d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { - MZ_CLEAR_ARR(d->m_hash); - MZ_CLEAR_ARR(d->m_next); + MZ_CLEAR_OBJ(d->m_hash); + MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } } @@ -1998,7 +1966,7 @@ tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_fun d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) - MZ_CLEAR_ARR(d->m_hash); + MZ_CLEAR_OBJ(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; @@ -2019,7 +1987,7 @@ tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_fun d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) - MZ_CLEAR_ARR(d->m_dict); + MZ_CLEAR_OBJ(d->m_dict); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); return TDEFL_STATUS_OKAY; @@ -2229,7 +2197,7 @@ void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, /* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ /* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ -tdefl_compressor *tdefl_compressor_alloc(void) +tdefl_compressor *tdefl_compressor_alloc() { return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); } @@ -2247,8 +2215,6 @@ void tdefl_compressor_free(tdefl_compressor *pComp) #ifdef __cplusplus } #endif - -#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software @@ -2277,8 +2243,6 @@ void tdefl_compressor_free(tdefl_compressor *pComp) -#ifndef MINIZ_NO_INFLATE_APIS - #ifdef __cplusplus extern "C" { #endif @@ -2359,10 +2323,10 @@ extern "C" { /* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ /* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ /* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ -#define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree) \ +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ do \ { \ - temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) \ { \ code_len = temp >> 9; \ @@ -2374,7 +2338,7 @@ extern "C" { code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ - temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); \ if (temp >= 0) \ break; \ @@ -2390,7 +2354,7 @@ extern "C" { /* The slow path is only executed at the very end of the input buffer. */ /* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ /* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ -#define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree) \ +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ do \ { \ int temp; \ @@ -2399,7 +2363,7 @@ extern "C" { { \ if ((pIn_buf_end - pIn_buf_cur) < 2) \ { \ - TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree); \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ } \ else \ { \ @@ -2408,14 +2372,14 @@ extern "C" { num_bits += 16; \ } \ } \ - if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else \ { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ - temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while (temp < 0); \ } \ sym = temp; \ @@ -2424,33 +2388,20 @@ extern "C" { } \ MZ_MACRO_END -static void tinfl_clear_tree(tinfl_decompressor *r) -{ - if (r->m_type == 0) - MZ_CLEAR_ARR(r->m_tree_0); - else if (r->m_type == 1) - MZ_CLEAR_ARR(r->m_tree_1); - else - MZ_CLEAR_ARR(r->m_tree_2); -} - tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { - static const mz_uint16 s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; - static const mz_uint8 s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; - static const mz_uint16 s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; - static const mz_uint8 s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - static const mz_uint16 s_min_table_sizes[3] = { 257, 1, 4 }; - - mz_int16 *pTrees[3]; - mz_uint8 *pCode_sizes[3]; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; - mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ @@ -2460,13 +2411,6 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex return TINFL_STATUS_BAD_PARAM; } - pTrees[0] = r->m_tree_0; - pTrees[1] = r->m_tree_1; - pTrees[2] = r->m_tree_2; - pCode_sizes[0] = r->m_code_size_0; - pCode_sizes[1] = r->m_code_size_1; - pCode_sizes[2] = r->m_code_size_2; - num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; @@ -2483,7 +2427,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) - counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)((size_t)1 << (8U + (r->m_zhdr0 >> 4))))); + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); @@ -2544,11 +2488,11 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex { if (r->m_type == 1) { - mz_uint8 *p = r->m_code_size_0; + mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; - TINFL_MEMSET(r->m_code_size_1, 5, 32); + TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) @@ -2565,30 +2509,26 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } - MZ_CLEAR_ARR(r->m_code_size_2); + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); - r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s; + r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for (; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; - mz_int16 *pLookUp; - mz_int16 *pTree; - mz_uint8 *pCode_size; + tinfl_huff_table *pTable; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; - pLookUp = r->m_look_up[r->m_type]; - pTree = pTrees[r->m_type]; - pCode_size = pCode_sizes[r->m_type]; - MZ_CLEAR_ARR(total_syms); - TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0])); - tinfl_clear_tree(r); + pTable = &r->m_tables[r->m_type]; + MZ_CLEAR_OBJ(total_syms); + MZ_CLEAR_OBJ(pTable->m_look_up); + MZ_CLEAR_OBJ(pTable->m_tree); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) - total_syms[pCode_size[i]]++; + total_syms[pTable->m_code_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) @@ -2602,7 +2542,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { - mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index]; + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; @@ -2613,14 +2553,14 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { - pLookUp[rev_code] = k; + pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } - if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) + if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { - pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } @@ -2628,24 +2568,24 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); - if (!pTree[-tree_cur - 1]) + if (!pTable->m_tree[-tree_cur - 1]) { - pTree[-tree_cur - 1] = (mz_int16)tree_next; + pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else - tree_cur = pTree[-tree_cur - 1]; + tree_cur = pTable->m_tree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); - pTree[-tree_cur - 1] = (mz_int16)sym_index; + pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) { mz_uint s; - TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2); + TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; @@ -2665,8 +2605,8 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } - TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]); - TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for (;;) @@ -2676,7 +2616,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { - TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0); + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) @@ -2704,14 +2644,14 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex num_bits += 16; } #endif - if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { - sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; @@ -2728,14 +2668,14 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex num_bits += 16; } #endif - if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { - sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; @@ -2764,7 +2704,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex counter += extra_bits; } - TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1); + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) @@ -2849,7 +2789,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex --pIn_buf_cur; num_bits -= 8; } - bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits); + bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) @@ -2881,7 +2821,7 @@ common_exit: } } r->m_num_bits = num_bits; - r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits); + r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; @@ -2976,7 +2916,6 @@ int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; - memset(pDict,0,TINFL_LZ_DICT_SIZE); tinfl_init(&decomp); for (;;) { @@ -2999,7 +2938,7 @@ int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, } #ifndef MINIZ_NO_MALLOC -tinfl_decompressor *tinfl_decompressor_alloc(void) +tinfl_decompressor *tinfl_decompressor_alloc() { tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); if (pDecomp) @@ -3016,8 +2955,6 @@ void tinfl_decompressor_free(tinfl_decompressor *pDecomp) #ifdef __cplusplus } #endif - -#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software @@ -3060,48 +2997,19 @@ extern "C" { #include #if defined(_MSC_VER) || defined(__MINGW64__) - -#define WIN32_LEAN_AND_MEAN -#include - -static WCHAR* mz_utf8z_to_widechar(const char* str) -{ - int reqChars = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); - WCHAR* wStr = (WCHAR*)malloc(reqChars * sizeof(WCHAR)); - MultiByteToWideChar(CP_UTF8, 0, str, -1, wStr, sizeof(WCHAR) * reqChars); - return wStr; -} - static FILE *mz_fopen(const char *pFilename, const char *pMode) { - WCHAR* wFilename = mz_utf8z_to_widechar(pFilename); - WCHAR* wMode = mz_utf8z_to_widechar(pMode); - FILE* pFile = NULL; - errno_t err = _wfopen_s(&pFile, wFilename, wMode); - free(wFilename); - free(wMode); - return err ? NULL : pFile; + FILE *pFile = NULL; + fopen_s(&pFile, pFilename, pMode); + return pFile; } - static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { - WCHAR* wPath = mz_utf8z_to_widechar(pPath); - WCHAR* wMode = mz_utf8z_to_widechar(pMode); - FILE* pFile = NULL; - errno_t err = _wfreopen_s(&pFile, wPath, wMode, pStream); - free(wPath); - free(wMode); - return err ? NULL : pFile; + FILE *pFile = NULL; + if (freopen_s(&pFile, pPath, pMode, pStream)) + return NULL; + return pFile; } - -static int mz_stat64(const char *path, struct __stat64 *buffer) -{ - WCHAR* wPath = mz_utf8z_to_widechar(path); - int res = _wstat64(wPath, buffer); - free(wPath); - return res; -} - #ifndef MINIZ_NO_TIME #include #endif @@ -3112,12 +3020,11 @@ static int mz_stat64(const char *path, struct __stat64 *buffer) #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 #define MZ_FILE_STAT_STRUCT _stat64 -#define MZ_FILE_STAT mz_stat64 +#define MZ_FILE_STAT _stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN mz_freopen #define MZ_DELETE_FILE remove - -#elif defined(__MINGW32__) || defined(__WATCOMC__) +#elif defined(__MINGW32__) #ifndef MINIZ_NO_TIME #include #endif @@ -3125,14 +3032,13 @@ static int mz_stat64(const char *path, struct __stat64 *buffer) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite -#define MZ_FTELL64 _ftelli64 -#define MZ_FSEEK64 _fseeki64 -#define MZ_FILE_STAT_STRUCT stat -#define MZ_FILE_STAT stat +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove - #elif defined(__TINYC__) #ifndef MINIZ_NO_TIME #include @@ -3148,7 +3054,6 @@ static int mz_stat64(const char *path, struct __stat64 *buffer) #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove - #elif defined(__USE_LARGEFILE64) /* gcc, clang */ #ifndef MINIZ_NO_TIME #include @@ -3164,8 +3069,7 @@ static int mz_stat64(const char *path, struct __stat64 *buffer) #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) #define MZ_DELETE_FILE remove - -#elif defined(__APPLE__) || defined(__FreeBSD__) +#elif defined(__APPLE__) #ifndef MINIZ_NO_TIME #include #endif @@ -3311,7 +3215,7 @@ struct mz_zip_internal_state_tag mz_zip_array m_sorted_central_dir_offsets; /* The flags passed in when the archive is initially opened. */ - mz_uint32 m_init_flags; + uint32_t m_init_flags; /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ mz_bool m_zip64; @@ -3747,7 +3651,7 @@ static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flag if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); - if (cdir_size < (mz_uint64)pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) @@ -3898,7 +3802,7 @@ static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flag void mz_zip_zero_struct(mz_zip_archive *pZip) { if (pZip) - MZ_CLEAR_PTR(pZip); + MZ_CLEAR_OBJ(*pZip); } static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) @@ -4372,7 +4276,7 @@ static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); - const mz_uint32 size = pZip->m_total_files; + const uint32_t size = pZip->m_total_files; const mz_uint filename_len = (mz_uint)strlen(pFilename); if (pIndex) @@ -4387,7 +4291,7 @@ static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char while (l <= h) { mz_int64 m = l + ((h - l) >> 1); - mz_uint32 file_index = pIndices[(mz_uint32)m]; + uint32_t file_index = pIndices[(uint32_t)m]; int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); if (!comp) @@ -4480,8 +4384,7 @@ mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, co return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); } -static -mz_bool mz_zip_reader_extract_to_mem_no_alloc1(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size, const mz_zip_archive_file_stat *st) +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { int status = TINFL_STATUS_DONE; mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; @@ -4494,9 +4397,6 @@ mz_bool mz_zip_reader_extract_to_mem_no_alloc1(mz_zip_archive *pZip, mz_uint fil if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - if (st) { - file_stat = *st; - } else if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; @@ -4627,22 +4527,17 @@ mz_bool mz_zip_reader_extract_to_mem_no_alloc1(mz_zip_archive *pZip, mz_uint fil return status == TINFL_STATUS_DONE; } -mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) -{ - return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); -} - mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; - return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); } mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) { - return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, NULL, 0, NULL); + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); } mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) @@ -4652,17 +4547,23 @@ mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFil void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) { - mz_zip_archive_file_stat file_stat; - mz_uint64 alloc_size; + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); void *pBuf; if (pSize) *pSize = 0; - if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return NULL; + } - alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) { mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); @@ -4675,7 +4576,7 @@ void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, si return NULL; } - if (!mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, (size_t)alloc_size, flags, NULL, 0, &file_stat)) + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return NULL; @@ -5136,7 +5037,7 @@ size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); /* Copy data to caller's buffer */ - memcpy( (mz_uint8*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS /* Perform CRC */ @@ -5505,7 +5406,7 @@ handle_failure: mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) { mz_zip_internal_state *pState; - mz_uint32 i; + uint32_t i; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); @@ -5523,6 +5424,9 @@ mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) } else { + if (pZip->m_total_files >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } @@ -5884,7 +5788,7 @@ mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 cur_ofs = 0; char buf[4096]; - MZ_CLEAR_ARR(buf); + MZ_CLEAR_OBJ(buf); do { @@ -6247,7 +6151,7 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ } - if (((mz_uint64)buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ @@ -6340,7 +6244,7 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n } cur_archive_file_ofs += num_alignment_padding_bytes; - MZ_CLEAR_ARR(local_dir_header); + MZ_CLEAR_OBJ(local_dir_header); if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { @@ -6490,7 +6394,7 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { - mz_uint16 gen_flags; + mz_uint16 gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; @@ -6502,15 +6406,13 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA mz_zip_internal_state *pState; mz_uint64 file_ofs = 0, cur_archive_header_file_ofs; + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; - gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; - - if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) - gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; - /* Sanity checks */ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); @@ -6595,7 +6497,7 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA method = MZ_DEFLATED; } - MZ_CLEAR_ARR(local_dir_header); + MZ_CLEAR_OBJ(local_dir_header); if (pState->m_zip64) { if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) @@ -6899,7 +6801,7 @@ mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, } #endif /* #ifndef MINIZ_NO_STDIO */ -static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, mz_uint32 ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) +static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) { /* + 64 should be enough for any new zip64 data */ if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) @@ -7215,10 +7117,10 @@ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive * if (pZip->m_pState->m_zip64) { /* dest is zip64, so upgrade the data descriptor */ - const mz_uint8 *pSrc_descriptor = (const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0); - const mz_uint32 src_crc32 = MZ_READ_LE32(pSrc_descriptor); - const mz_uint64 src_comp_size = MZ_READ_LE32(pSrc_descriptor + sizeof(mz_uint32)); - const mz_uint64 src_uncomp_size = MZ_READ_LE32(pSrc_descriptor + 2*sizeof(mz_uint32)); + const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0)); + const mz_uint32 src_crc32 = pSrc_descriptor[0]; + const mz_uint64 src_comp_size = pSrc_descriptor[1]; + const mz_uint64 src_uncomp_size = pSrc_descriptor[2]; mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); @@ -7354,7 +7256,7 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) if (pState->m_zip64) { - if ((mz_uint64)pState->m_central_dir.m_size >= MZ_UINT32_MAX) + if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else @@ -7382,7 +7284,7 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) /* Write zip64 end of central directory header */ mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; - MZ_CLEAR_ARR(hdr); + MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ @@ -7397,7 +7299,7 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; /* Write zip64 end of central directory locator */ - MZ_CLEAR_ARR(hdr); + MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); @@ -7408,7 +7310,7 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) } /* Write end of central directory record */ - MZ_CLEAR_ARR(hdr); + MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); @@ -7724,9 +7626,7 @@ const char *mz_zip_get_error_string(mz_zip_error mz_err) case MZ_ZIP_VALIDATION_FAILED: return "validation failed"; case MZ_ZIP_WRITE_CALLBACK_FAILED: - return "write callback failed"; - case MZ_ZIP_TOTAL_ERRORS: - return "total errors"; + return "write calledback failed"; default: break; } diff --git a/code/components/jomjol_fileserver_ota/miniz/miniz.h b/code/components/jomjol_fileserver_ota/miniz.h similarity index 96% rename from code/components/jomjol_fileserver_ota/miniz/miniz.h rename to code/components/jomjol_fileserver_ota/miniz.h index 278898d1..6cc398c9 100644 --- a/code/components/jomjol_fileserver_ota/miniz/miniz.h +++ b/code/components/jomjol_fileserver_ota/miniz.h @@ -1,7 +1,5 @@ -#ifndef MINIZ_EXPORT #define MINIZ_EXPORT -#endif -/* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing +/* miniz.c 2.2.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt @@ -118,7 +116,7 @@ /* Defines to completely disable specific portions of miniz.c: - If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */ + If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ /* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ /*#define MINIZ_NO_STDIO */ @@ -128,12 +126,6 @@ /* The current downside is the times written to your archives will be from 1979. */ /*#define MINIZ_NO_TIME */ -/* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */ -/*#define MINIZ_NO_DEFLATE_APIS */ - -/* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */ -/*#define MINIZ_NO_INFLATE_APIS */ - /* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ /*#define MINIZ_NO_ARCHIVE_APIS */ @@ -152,14 +144,6 @@ functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ /*#define MINIZ_NO_MALLOC */ -#ifdef MINIZ_NO_INFLATE_APIS -//#define MINIZ_NO_ARCHIVE_APIS -#endif - -#ifdef MINIZ_NO_DEFLATE_APIS -#define MINIZ_NO_ARCHIVE_WRITING_APIS -#endif - #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) /* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ #define MINIZ_NO_TIME @@ -178,40 +162,18 @@ #define MINIZ_X86_OR_X64_CPU 0 #endif -/* Set MINIZ_LITTLE_ENDIAN only if not set */ -#if !defined(MINIZ_LITTLE_ENDIAN) -#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) - -#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU /* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif -#else - -#if MINIZ_X86_OR_X64_CPU -#define MINIZ_LITTLE_ENDIAN 1 -#else -#define MINIZ_LITTLE_ENDIAN 0 -#endif - -#endif -#endif - -/* Using unaligned loads and stores causes errors when using UBSan */ -#if defined(__has_feature) -#if __has_feature(undefined_behavior_sanitizer) -#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 -#endif -#endif - /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ #if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) #if MINIZ_X86_OR_X64_CPU /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ -#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_UNALIGNED_USE_MEMCPY #else #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 @@ -275,10 +237,10 @@ enum MZ_DEFAULT_COMPRESSION = -1 }; -#define MZ_VERSION "11.0.1" -#define MZ_VERNUM 0xB001 -#define MZ_VER_MAJOR 11 -#define MZ_VER_MINOR 1 +#define MZ_VERSION "10.2.0" +#define MZ_VERNUM 0xA100 +#define MZ_VER_MAJOR 10 +#define MZ_VER_MINOR 2 #define MZ_VER_REVISION 0 #define MZ_VER_SUBREVISION 0 @@ -343,8 +305,6 @@ typedef mz_stream *mz_streamp; /* Returns the version string of miniz.c. */ MINIZ_EXPORT const char *mz_version(void); -#ifndef MINIZ_NO_DEFLATE_APIS - /* mz_deflateInit() initializes a compressor with default options: */ /* Parameters: */ /* pStream must point to an initialized mz_stream struct. */ @@ -397,10 +357,6 @@ MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const u /* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); -#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ - -#ifndef MINIZ_NO_INFLATE_APIS - /* Initializes a decompressor. */ MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); @@ -434,7 +390,6 @@ MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); /* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); -#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ /* Returns a string description of the specified error code, or NULL if the error code is invalid. */ MINIZ_EXPORT const char *mz_error(int err); @@ -485,8 +440,6 @@ typedef void *const voidpc; #define free_func mz_free_func #define internal_state mz_internal_state #define z_stream mz_stream - -#ifndef MINIZ_NO_DEFLATE_APIS #define deflateInit mz_deflateInit #define deflateInit2 mz_deflateInit2 #define deflateReset mz_deflateReset @@ -496,9 +449,6 @@ typedef void *const voidpc; #define compress mz_compress #define compress2 mz_compress2 #define compressBound mz_compressBound -#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ - -#ifndef MINIZ_NO_INFLATE_APIS #define inflateInit mz_inflateInit #define inflateInit2 mz_inflateInit2 #define inflateReset mz_inflateReset @@ -506,8 +456,6 @@ typedef void *const voidpc; #define inflateEnd mz_inflateEnd #define uncompress mz_uncompress #define uncompress2 mz_uncompress2 -#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ - #define crc32 mz_crc32 #define adler32 mz_adler32 #define MAX_WBITS 15 @@ -571,8 +519,7 @@ typedef int mz_bool; #ifdef MINIZ_NO_TIME typedef struct mz_dummy_time_t_tag { - mz_uint32 m_dummy1; - mz_uint32 m_dummy2; + int m_dummy; } mz_dummy_time_t; #define MZ_TIME_T mz_dummy_time_t #else @@ -594,8 +541,6 @@ typedef struct mz_dummy_time_t_tag #define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) -#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) -#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) @@ -632,8 +577,6 @@ extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, si #pragma once -#ifndef MINIZ_NO_DEFLATE_APIS - #ifdef __cplusplus extern "C" { #endif @@ -821,14 +764,10 @@ MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); #ifdef __cplusplus } #endif - -#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #pragma once /* ------------------- Low-level Decompression API Definitions */ -#ifndef MINIZ_NO_INFLATE_APIS - #ifdef __cplusplus extern "C" { #endif @@ -937,6 +876,12 @@ enum TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #else @@ -956,21 +901,13 @@ struct tinfl_decompressor_tag mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; - mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE]; - mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; - mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2]; - mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2]; - mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0]; - mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1]; - mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2]; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; #ifdef __cplusplus } #endif - -#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ #pragma once @@ -1005,6 +942,10 @@ typedef struct mz_uint16 m_bit_flag; mz_uint16 m_method; +#ifndef MINIZ_NO_TIME + MZ_TIME_T m_time; +#endif + /* CRC-32 of uncompressed data. */ mz_uint32 m_crc32; @@ -1041,11 +982,6 @@ typedef struct /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; -#ifdef MINIZ_NO_TIME - MZ_TIME_T m_padding; -#else - MZ_TIME_T m_time; -#endif } mz_zip_archive_file_stat; typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); @@ -1157,7 +1093,9 @@ typedef struct mz_uint flags; int status; - +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32; +#endif mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf; @@ -1167,12 +1105,6 @@ typedef struct tinfl_decompressor inflator; -#ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS - mz_uint padding; -#else - mz_uint file_crc32; -#endif - } mz_zip_reader_extract_iter_state; /* -------- ZIP reading */ @@ -1296,9 +1228,9 @@ MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, c /* TODO */ typedef void *mz_zip_streaming_extract_state_ptr; mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); - mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); - mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); - mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs); + uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs); size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); #endif @@ -1312,9 +1244,7 @@ MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags /* Misc utils/helpers, valid for ZIP reading or writing */ MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); -#ifndef MINIZ_NO_STDIO MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); -#endif /* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); @@ -1388,7 +1318,7 @@ MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_ /* An archive must be manually finalized by calling this function for it to be valid. */ MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); -/* Finalizes a heap archive, returning a pointer to the heap block and its size. */ +/* Finalizes a heap archive, returning a poiner to the heap block and its size. */ /* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); @@ -1405,13 +1335,11 @@ MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); -#ifndef MINIZ_NO_STDIO /* Reads a single file from an archive into a heap block. */ /* If pComment is not NULL, only the file with the specified comment will be extracted. */ /* Returns NULL on failure. */ MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); -#endif #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ diff --git a/code/components/jomjol_fileserver_ota/miniz/ChangeLog.md b/code/components/jomjol_fileserver_ota/miniz/ChangeLog.md deleted file mode 100644 index 8ea8ad29..00000000 --- a/code/components/jomjol_fileserver_ota/miniz/ChangeLog.md +++ /dev/null @@ -1,235 +0,0 @@ -## Changelog - -### 3.0.1 - - - Fix compilation error with MINIZ_USE_UNALIGNED_LOADS_AND_STORES=1 - -### 3.0.0 - - - Reduce memory usage for inflate. This changes `struct tinfl_decompressor_tag` and therefore requires a major version bump (breaks ABI compatibility) - - Add padding to structures so it continues to work if features differ. This also changes some structures - - Use _ftelli64, _fseeki64 and stat with MinGW32 and OpenWatcom - - Fix varios warnings with OpenWatcom compiler - - Avoid using unaligned memory access in UBSan builds - - Set MINIZ_LITTLE_ENDIAN only if not set - - Add MINIZ_NO_DEFLATE_APIS and MINIZ_NO_INFLATE_APIS - - Fix use of uninitialized memory in tinfl_decompress_mem_to_callback() - - Use wfopen on windows - - Use _wstat64 instead _stat64 on windows - - Use level_and_flags after MZ_DEFAULT_COMPRESSION has been handled - - Improve endianess detection - - Don't use unaligned stores and loads per default - - Fix function declaration if MINIZ_NO_STDIO is used - - Fix MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 not being set - - Remove total files check (its 32-bit uint) - - tinfl_decompress: avoid NULL ptr arithmetic UB - - miniz_zip: fix mz_zip_reader_extract_to_heap to read correct sizes - - Eliminate 64-bit operations on 32-bit machines - - Disable treating warnings as error with MSVC - - Disable building shared lib via CMake by default - - Fixed alignment problems on MacOS - - Fixed get error string for MZ_ZIP_TOTAL_ERRORS - - Write correct FLEVEL 2-bit value in zlib header - - miniz.pc.in: fix include path not containing the "miniz" suffix - - Fix compatibility with FreeBSD - - pkg-config tweaks - - Fix integer overflow in header corruption check - - Fix some warnings - - tdefl_compress_normal: Avoid NULL ptr arithmetic UB - - replace use of stdint.h types with mz_ variants - - -### 2.2.0 - - - Fix examples with amalgamation - - Modified cmake script to support shared library mode and find_package - - Fix for misleading doc comment on `mz_zip_reader_init_cfile` function - - Add include location tolerance and stop forcing `_GNU_SOURCE` - - Fix: mz_zip_reader_locate_file_v2 returns an mz_bool - - Fix large file system checks - - Add #elif to enable an external mz_crc32() to be linked in - - Write with dynamic size (size of file/data to be added not known before adding) - - Added uncompress2 for zlib compatibility - - Add support for building as a Meson subproject - - Added OSSFuzz support; Integrate with CIFuzz - - Add pkg-config file - - Fixed use-of-uninitialized value msan error when copying dist bytes with no output bytes written. - - mz_zip_validate_file(): fix memory leak on errors - - Fixed MSAN use-of-uninitialized in tinfl_decompress when invalid dist is decoded. In this instance dist was 31 which s_dist_base translates as 0 - - Add flag to set (compressed) size in local file header - - avoid use of uninitialized value in tdefl_record_literal - -### 2.1.0 - - - More instances of memcpy instead of cast and use memcpy per default - - Remove inline for c90 support - - New function to read files via callback functions when adding them - - Fix out of bounds read while reading Zip64 extended information - - guard memcpy when n == 0 because buffer may be NULL - - Implement inflateReset() function - - Move comp/decomp alloc/free prototypes under guarding #ifndef MZ_NO_MALLOC - - Fix large file support under Windows - - Don't warn if _LARGEFILE64_SOURCE is not defined to 1 - - Fixes for MSVC warnings - - Remove check that path of file added to archive contains ':' or '\' - - Add !defined check on MINIZ_USE_ALIGNED_LOADS_AND_STORES - -### 2.0.8 - - - Remove unimplemented functions (mz_zip_locate_file and mz_zip_locate_file_v2) - - Add license, changelog, readme and example files to release zip - - Fix heap overflow to user buffer in tinfl_status tinfl_decompress - - Fix corrupt archive if uncompressed file smaller than 4 byte and the file is added by mz_zip_writer_add_mem* - -### 2.0.7 - - - Removed need in C++ compiler in cmake build - - Fixed a lot of uninitialized value errors found with Valgrind by memsetting m_dict to 0 in tdefl_init - - Fix resource leak in mz_zip_reader_init_file_v2 - - Fix assert with mz_zip_writer_add_mem* w/MZ_DEFAULT_COMPRESSION - - cmake build: install library and headers - - Remove _LARGEFILE64_SOURCE requirement from apple defines for large files - -### 2.0.6 - - - Improve MZ_ZIP_FLAG_WRITE_ZIP64 documentation - - Remove check for cur_archive_file_ofs > UINT_MAX because cur_archive_file_ofs is not used after this point - - Add cmake debug configuration - - Fix PNG height when creating png files - - Add "iterative" file extraction method based on mz_zip_reader_extract_to_callback. - - Option to use memcpy for unaligned data access - - Define processor/arch macros as zero if not set to one - -### 2.0.4/2.0.5 - - - Fix compilation with the various omission compile definitions - -### 2.0.3 - -- Fix GCC/clang compile warnings -- Added callback for periodic flushes (for ZIP file streaming) -- Use UTF-8 for file names in ZIP files per default - -### 2.0.2 - -- Fix source backwards compatibility with 1.x -- Fix a ZIP bit not being set correctly - -### 2.0.1 - -- Added some tests -- Added CI -- Make source code ANSI C compatible - -### 2.0.0 beta - -- Matthew Sitton merged miniz 1.x to Rich Geldreich's vogl ZIP64 changes. Miniz is now licensed as MIT since the vogl code base is MIT licensed -- Miniz is now split into several files -- Miniz does now not seek backwards when creating ZIP files. That is the ZIP files can be streamed -- Miniz automatically switches to the ZIP64 format when the created ZIP files goes over ZIP file limits -- Similar to [SQLite](https://www.sqlite.org/amalgamation.html) the Miniz source code is amalgamated into one miniz.c/miniz.h pair in a build step (amalgamate.sh). Please use miniz.c/miniz.h in your projects -- Miniz 2 is only source back-compatible with miniz 1.x. It breaks binary compatibility because structures changed - -### v1.16 BETA Oct 19, 2013 - -Still testing, this release is downloadable from [here](http://www.tenacioussoftware.com/miniz_v116_beta_r1.7z). Two key inflator-only robustness and streaming related changes. Also merged in tdefl_compressor_alloc(), tdefl_compressor_free() helpers to make script bindings easier for rustyzip. I would greatly appreciate any help with testing or any feedback. - -The inflator in raw (non-zlib) mode is now usable on gzip or similar streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520). This version should never read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer. - -The inflator now has a new failure status TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on. This is scary behavior if the caller didn't know when to stop accepting output (because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum). v1.16 will instead return TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft" failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios. - -- The inflator coroutine func. is subtle and complex so I'm being cautious about this release. I would greatly appreciate any help with testing or any feedback. - I feel good about these changes, and they've been through several hours of automated testing, but they will probably not fix anything for the majority of prev. users so I'm - going to mark this release as beta for a few weeks and continue testing it at work/home on various things. -- The inflator in raw (non-zlib) mode is now usable on gzip or similar data streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520). - This version should *never* read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer. This issue was caused by the various Huffman bitbuffer lookahead optimizations, and - would not be an issue if the caller knew and enforced the precise size of the raw compressed data *or* if the compressed data was in zlib format (i.e. always followed by the byte aligned zlib adler32). - So in other words, you can now call the inflator on deflate streams that are followed by arbitrary amounts of data and it's guaranteed that decompression will stop exactly on the last byte. -- The inflator now has a new failure status: TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the - caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on. - This is scary, because in the worst case, I believe it was possible for the prev. inflator to start outputting large amounts of literal data. If the caller didn't know when to stop accepting output - (because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum) it could continue forever. v1.16 cannot fall into this failure mode, instead it'll return - TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft" - failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios. -- Added documentation to all the tinfl return status codes, fixed miniz_tester so it accepts double minus params for Linux, tweaked example1.c, added a simple "follower bytes" test to miniz_tester.cpp. -### v1.15 r4 STABLE - Oct 13, 2013 - -Merged over a few very minor bug fixes that I fixed in the zip64 branch. This is downloadable from [here](http://code.google.com/p/miniz/downloads/list) and also in SVN head (as of 10/19/13). - - -### v1.15 - Oct. 13, 2013 - -Interim bugfix release while I work on the next major release with zip64 and streaming compression/decompression support. Fixed the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com), which could cause the locate files func to not find files when this flag was specified. Also fixed a bug in mz_zip_reader_extract_to_mem_no_alloc() with user provided read buffers (thanks kymoon). I also merged lots of compiler fixes from various github repo branches and Google Code issue reports. I finally added cmake support (only tested under for Linux so far), compiled and tested with clang v3.3 and gcc 4.6 (under Linux), added defl_write_image_to_png_file_in_memory_ex() (supports Y flipping for OpenGL use, real-time compression), added a new PNG example (example6.c - Mandelbrot), and I added 64-bit file I/O support (stat64(), etc.) for glibc. - -- Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug - would only have occurred in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() - (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). -- Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size -- Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. - Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). -- Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes -- mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed -- Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. -- Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti -- Merged MZ_FORCEINLINE fix from hdeanclark -- Fix include before config #ifdef, thanks emil.brink -- Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can - set it to 1 for real-time compression). -- Merged in some compiler fixes from paulharris's github repro. -- Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. -- Added example6.c, which dumps an image of the mandelbrot set to a PNG file. -- Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. -- In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled -- In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch - -### v1.14 - May 20, 2012 - -(SVN Only) Minor tweaks to get miniz.c compiling with the Tiny C Compiler, added #ifndef MINIZ_NO_TIME guards around utime.h includes. Adding mz_free() function, so the caller can free heap blocks returned by miniz using whatever heap functions it has been configured to use, MSVC specific fixes to use "safe" variants of several functions (localtime_s, fopen_s, freopen_s). - -MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). - -Compiler specific fixes, some from fermtect. I upgraded to TDM GCC 4.6.1 and now static __forceinline is giving it fits, so I'm changing all usage of __forceinline to MZ_FORCEINLINE and forcing gcc to use __attribute__((__always_inline__)) (and MSVC to use __forceinline). Also various fixes from fermtect for MinGW32: added #include , 64-bit ftell/fseek fixes. - -### v1.13 - May 19, 2012 - -From jason@cornsyrup.org and kelwert@mtu.edu - Most importantly, fixed mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bits. Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. Other stuff: - -Eliminated a bunch of warnings when compiling with GCC 32-bit/64. Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). - -Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) - -Fix ftell() usage in a few of the examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). Fix fail logic handling in mz_zip_add_mem_to_archive_file_in_place() so it always calls mz_zip_writer_finalize_archive() and mz_zip_writer_end(), even if the file add fails. - -- From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. -- Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. -- Eliminated a bunch of warnings when compiling with GCC 32-bit/64. -- Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly -"Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). -- Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. -- Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. -- Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. -- Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) -- Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). - -### v1.12 - 4/12/12 - -More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. -level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. - -### v1.11 - 5/28/11 - -Added statement from unlicense.org - -### v1.10 - 5/27/11 - -- Substantial compressor optimizations: -- Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). -- Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. -- Refactored the compression code for better readability and maintainability. -- Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large drop in throughput on some files). - -### v1.09 - 5/15/11 - -Initial stable release. - - diff --git a/code/components/jomjol_fileserver_ota/miniz/LICENSE b/code/components/jomjol_fileserver_ota/miniz/LICENSE deleted file mode 100644 index 1982f4bb..00000000 --- a/code/components/jomjol_fileserver_ota/miniz/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright 2013-2014 RAD Game Tools and Valve Software -Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC - -All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/code/components/jomjol_fileserver_ota/miniz/examples/example1.c b/code/components/jomjol_fileserver_ota/miniz/examples/example1.c deleted file mode 100644 index d6e33faa..00000000 --- a/code/components/jomjol_fileserver_ota/miniz/examples/example1.c +++ /dev/null @@ -1,105 +0,0 @@ -// example1.c - Demonstrates miniz.c's compress() and uncompress() functions (same as zlib's). -// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. -#include -#include "miniz.h" -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint; - -// The string to compress. -static const char *s_pStr = "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ - "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ - "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ - "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ - "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ - "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ - "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."; - -int main(int argc, char *argv[]) -{ - uint step = 0; - int cmp_status; - uLong src_len = (uLong)strlen(s_pStr); - uLong cmp_len = compressBound(src_len); - uLong uncomp_len = src_len; - uint8 *pCmp, *pUncomp; - uint total_succeeded = 0; - (void)argc, (void)argv; - - printf("miniz.c version: %s\n", MZ_VERSION); - - do - { - // Allocate buffers to hold compressed and uncompressed data. - pCmp = (mz_uint8 *)malloc((size_t)cmp_len); - pUncomp = (mz_uint8 *)malloc((size_t)src_len); - if ((!pCmp) || (!pUncomp)) - { - printf("Out of memory!\n"); - return EXIT_FAILURE; - } - - // Compress the string. - cmp_status = compress(pCmp, &cmp_len, (const unsigned char *)s_pStr, src_len); - if (cmp_status != Z_OK) - { - printf("compress() failed!\n"); - free(pCmp); - free(pUncomp); - return EXIT_FAILURE; - } - - printf("Compressed from %u to %u bytes\n", (mz_uint32)src_len, (mz_uint32)cmp_len); - - if (step) - { - // Purposely corrupt the compressed data if fuzzy testing (this is a very crude fuzzy test). - uint n = 1 + (rand() % 3); - while (n--) - { - uint i = rand() % cmp_len; - pCmp[i] ^= (rand() & 0xFF); - } - } - - // Decompress. - cmp_status = uncompress(pUncomp, &uncomp_len, pCmp, cmp_len); - total_succeeded += (cmp_status == Z_OK); - - if (step) - { - printf("Simple fuzzy test: step %u total_succeeded: %u\n", step, total_succeeded); - } - else - { - if (cmp_status != Z_OK) - { - printf("uncompress failed!\n"); - free(pCmp); - free(pUncomp); - return EXIT_FAILURE; - } - - printf("Decompressed from %u to %u bytes\n", (mz_uint32)cmp_len, (mz_uint32)uncomp_len); - - // Ensure uncompress() returned the expected data. - if ((uncomp_len != src_len) || (memcmp(pUncomp, s_pStr, (size_t)src_len))) - { - printf("Decompression failed!\n"); - free(pCmp); - free(pUncomp); - return EXIT_FAILURE; - } - } - - free(pCmp); - free(pUncomp); - - step++; - - // Keep on fuzzy testing if there's a non-empty command line. - } while (argc >= 2); - - printf("Success.\n"); - return EXIT_SUCCESS; -} diff --git a/code/components/jomjol_fileserver_ota/miniz/examples/example2.c b/code/components/jomjol_fileserver_ota/miniz/examples/example2.c deleted file mode 100644 index 03d24095..00000000 --- a/code/components/jomjol_fileserver_ota/miniz/examples/example2.c +++ /dev/null @@ -1,164 +0,0 @@ -// example2.c - Simple demonstration of miniz.c's ZIP archive API's. -// Note this test deletes the test archive file "__mz_example2_test__.zip" in the current directory, then creates a new one with test data. -// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. - -#if defined(__GNUC__) - // Ensure we get the 64-bit variants of the CRT's file I/O calls - #ifndef _FILE_OFFSET_BITS - #define _FILE_OFFSET_BITS 64 - #endif - #ifndef _LARGEFILE64_SOURCE - #define _LARGEFILE64_SOURCE 1 - #endif -#endif - -#include -#include "miniz.h" - -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint; - -// The string to compress. -static const char *s_pTest_str = - "MISSION CONTROL I wouldn't worry too much about the computer. First of all, there is still a chance that he is right, despite your tests, and" \ - "if it should happen again, we suggest eliminating this possibility by allowing the unit to remain in place and seeing whether or not it" \ - "actually fails. If the computer should turn out to be wrong, the situation is still not alarming. The type of obsessional error he may be" \ - "guilty of is not unknown among the latest generation of HAL 9000 computers. It has almost always revolved around a single detail, such as" \ - "the one you have described, and it has never interfered with the integrity or reliability of the computer's performance in other areas." \ - "No one is certain of the cause of this kind of malfunctioning. It may be over-programming, but it could also be any number of reasons. In any" \ - "event, it is somewhat analogous to human neurotic behavior. Does this answer your query? Zero-five-three-Zero, MC, transmission concluded."; - -static const char *s_pComment = "This is a comment"; - -int main(int argc, char *argv[]) -{ - int i, sort_iter; - mz_bool status; - size_t uncomp_size; - mz_zip_archive zip_archive; - void *p; - const int N = 50; - char data[2048]; - char archive_filename[64]; - static const char *s_Test_archive_filename = "__mz_example2_test__.zip"; - - assert((strlen(s_pTest_str) + 64) < sizeof(data)); - - printf("miniz.c version: %s\n", MZ_VERSION); - - (void)argc, (void)argv; - - // Delete the test archive, so it doesn't keep growing as we run this test - remove(s_Test_archive_filename); - - // Append a bunch of text files to the test archive - for (i = (N - 1); i >= 0; --i) - { - sprintf(archive_filename, "%u.txt", i); - sprintf(data, "%u %s %u", (N - 1) - i, s_pTest_str, i); - - // Add a new file to the archive. Note this is an IN-PLACE operation, so if it fails your archive is probably hosed (its central directory may not be complete) but it should be recoverable using zip -F or -FF. So use caution with this guy. - // A more robust way to add a file to an archive would be to read it into memory, perform the operation, then write a new archive out to a temp file and then delete/rename the files. - // Or, write a new archive to disk to a temp file, then delete/rename the files. For this test this API is fine. - status = mz_zip_add_mem_to_archive_file_in_place(s_Test_archive_filename, archive_filename, data, strlen(data) + 1, s_pComment, (uint16)strlen(s_pComment), MZ_BEST_COMPRESSION); - if (!status) - { - printf("mz_zip_add_mem_to_archive_file_in_place failed!\n"); - return EXIT_FAILURE; - } - } - - // Add a directory entry for testing - status = mz_zip_add_mem_to_archive_file_in_place(s_Test_archive_filename, "directory/", NULL, 0, "no comment", (uint16)strlen("no comment"), MZ_BEST_COMPRESSION); - if (!status) - { - printf("mz_zip_add_mem_to_archive_file_in_place failed!\n"); - return EXIT_FAILURE; - } - - // Now try to open the archive. - memset(&zip_archive, 0, sizeof(zip_archive)); - - status = mz_zip_reader_init_file(&zip_archive, s_Test_archive_filename, 0); - if (!status) - { - printf("mz_zip_reader_init_file() failed!\n"); - return EXIT_FAILURE; - } - - // Get and print information about each file in the archive. - for (i = 0; i < (int)mz_zip_reader_get_num_files(&zip_archive); i++) - { - mz_zip_archive_file_stat file_stat; - if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) - { - printf("mz_zip_reader_file_stat() failed!\n"); - mz_zip_reader_end(&zip_archive); - return EXIT_FAILURE; - } - - printf("Filename: \"%s\", Comment: \"%s\", Uncompressed size: %u, Compressed size: %u, Is Dir: %u\n", file_stat.m_filename, file_stat.m_comment, (uint)file_stat.m_uncomp_size, (uint)file_stat.m_comp_size, mz_zip_reader_is_file_a_directory(&zip_archive, i)); - - if (!strcmp(file_stat.m_filename, "directory/")) - { - if (!mz_zip_reader_is_file_a_directory(&zip_archive, i)) - { - printf("mz_zip_reader_is_file_a_directory() didn't return the expected results!\n"); - mz_zip_reader_end(&zip_archive); - return EXIT_FAILURE; - } - } - } - - // Close the archive, freeing any resources it was using - mz_zip_reader_end(&zip_archive); - - // Now verify the compressed data - for (sort_iter = 0; sort_iter < 2; sort_iter++) - { - memset(&zip_archive, 0, sizeof(zip_archive)); - status = mz_zip_reader_init_file(&zip_archive, s_Test_archive_filename, sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0); - if (!status) - { - printf("mz_zip_reader_init_file() failed!\n"); - return EXIT_FAILURE; - } - - for (i = 0; i < N; i++) - { - sprintf(archive_filename, "%u.txt", i); - sprintf(data, "%u %s %u", (N - 1) - i, s_pTest_str, i); - - // Try to extract all the files to the heap. - p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0); - if (!p) - { - printf("mz_zip_reader_extract_file_to_heap() failed!\n"); - mz_zip_reader_end(&zip_archive); - return EXIT_FAILURE; - } - - // Make sure the extraction really succeeded. - if ((uncomp_size != (strlen(data) + 1)) || (memcmp(p, data, strlen(data)))) - { - printf("mz_zip_reader_extract_file_to_heap() failed to extract the proper data\n"); - mz_free(p); - mz_zip_reader_end(&zip_archive); - return EXIT_FAILURE; - } - - printf("Successfully extracted file \"%s\", size %u\n", archive_filename, (uint)uncomp_size); - printf("File data: \"%s\"\n", (const char *)p); - - // We're done. - mz_free(p); - } - - // Close the archive, freeing any resources it was using - mz_zip_reader_end(&zip_archive); - } - - printf("Success.\n"); - return EXIT_SUCCESS; -} diff --git a/code/components/jomjol_fileserver_ota/miniz/examples/example3.c b/code/components/jomjol_fileserver_ota/miniz/examples/example3.c deleted file mode 100644 index a2c68465..00000000 --- a/code/components/jomjol_fileserver_ota/miniz/examples/example3.c +++ /dev/null @@ -1,269 +0,0 @@ -// example3.c - Demonstrates how to use miniz.c's deflate() and inflate() functions for simple file compression. -// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. -// For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c. -#include -#include -#include "miniz.h" - -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint; - -#define my_max(a,b) (((a) > (b)) ? (a) : (b)) -#define my_min(a,b) (((a) < (b)) ? (a) : (b)) - -#define BUF_SIZE (1024 * 1024) -static uint8 s_inbuf[BUF_SIZE]; -static uint8 s_outbuf[BUF_SIZE]; - -int main(int argc, char *argv[]) -{ - const char *pMode; - FILE *pInfile, *pOutfile; - uint infile_size; - int level = Z_BEST_COMPRESSION; - z_stream stream; - int p = 1; - const char *pSrc_filename; - const char *pDst_filename; - long file_loc; - - printf("miniz.c version: %s\n", MZ_VERSION); - - if (argc < 4) - { - printf("Usage: example3 [options] [mode:c or d] infile outfile\n"); - printf("\nModes:\n"); - printf("c - Compresses file infile to a zlib stream in file outfile\n"); - printf("d - Decompress zlib stream in file infile to file outfile\n"); - printf("\nOptions:\n"); - printf("-l[0-10] - Compression level, higher values are slower.\n"); - return EXIT_FAILURE; - } - - while ((p < argc) && (argv[p][0] == '-')) - { - switch (argv[p][1]) - { - case 'l': - { - level = atoi(&argv[1][2]); - if ((level < 0) || (level > 10)) - { - printf("Invalid level!\n"); - return EXIT_FAILURE; - } - break; - } - default: - { - printf("Invalid option: %s\n", argv[p]); - return EXIT_FAILURE; - } - } - p++; - } - - if ((argc - p) < 3) - { - printf("Must specify mode, input filename, and output filename after options!\n"); - return EXIT_FAILURE; - } - else if ((argc - p) > 3) - { - printf("Too many filenames!\n"); - return EXIT_FAILURE; - } - - pMode = argv[p++]; - if (!strchr("cCdD", pMode[0])) - { - printf("Invalid mode!\n"); - return EXIT_FAILURE; - } - - pSrc_filename = argv[p++]; - pDst_filename = argv[p++]; - - printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename); - - // Open input file. - pInfile = fopen(pSrc_filename, "rb"); - if (!pInfile) - { - printf("Failed opening input file!\n"); - return EXIT_FAILURE; - } - - // Determine input file's size. - fseek(pInfile, 0, SEEK_END); - file_loc = ftell(pInfile); - fseek(pInfile, 0, SEEK_SET); - - if ((file_loc < 0) || ((mz_uint64)file_loc > INT_MAX)) - { - // This is not a limitation of miniz or tinfl, but this example. - printf("File is too large to be processed by this example.\n"); - return EXIT_FAILURE; - } - - infile_size = (uint)file_loc; - - // Open output file. - pOutfile = fopen(pDst_filename, "wb"); - if (!pOutfile) - { - printf("Failed opening output file!\n"); - return EXIT_FAILURE; - } - - printf("Input file size: %u\n", infile_size); - - // Init the z_stream - memset(&stream, 0, sizeof(stream)); - stream.next_in = s_inbuf; - stream.avail_in = 0; - stream.next_out = s_outbuf; - stream.avail_out = BUF_SIZE; - - if ((pMode[0] == 'c') || (pMode[0] == 'C')) - { - // Compression. - uint infile_remaining = infile_size; - - if (deflateInit(&stream, level) != Z_OK) - { - printf("deflateInit() failed!\n"); - return EXIT_FAILURE; - } - - for ( ; ; ) - { - int status; - if (!stream.avail_in) - { - // Input buffer is empty, so read more bytes from input file. - uint n = my_min(BUF_SIZE, infile_remaining); - - if (fread(s_inbuf, 1, n, pInfile) != n) - { - printf("Failed reading from input file!\n"); - return EXIT_FAILURE; - } - - stream.next_in = s_inbuf; - stream.avail_in = n; - - infile_remaining -= n; - //printf("Input bytes remaining: %u\n", infile_remaining); - } - - status = deflate(&stream, infile_remaining ? Z_NO_FLUSH : Z_FINISH); - - if ((status == Z_STREAM_END) || (!stream.avail_out)) - { - // Output buffer is full, or compression is done, so write buffer to output file. - uint n = BUF_SIZE - stream.avail_out; - if (fwrite(s_outbuf, 1, n, pOutfile) != n) - { - printf("Failed writing to output file!\n"); - return EXIT_FAILURE; - } - stream.next_out = s_outbuf; - stream.avail_out = BUF_SIZE; - } - - if (status == Z_STREAM_END) - break; - else if (status != Z_OK) - { - printf("deflate() failed with status %i!\n", status); - return EXIT_FAILURE; - } - } - - if (deflateEnd(&stream) != Z_OK) - { - printf("deflateEnd() failed!\n"); - return EXIT_FAILURE; - } - } - else if ((pMode[0] == 'd') || (pMode[0] == 'D')) - { - // Decompression. - uint infile_remaining = infile_size; - - if (inflateInit(&stream)) - { - printf("inflateInit() failed!\n"); - return EXIT_FAILURE; - } - - for ( ; ; ) - { - int status; - if (!stream.avail_in) - { - // Input buffer is empty, so read more bytes from input file. - uint n = my_min(BUF_SIZE, infile_remaining); - - if (fread(s_inbuf, 1, n, pInfile) != n) - { - printf("Failed reading from input file!\n"); - return EXIT_FAILURE; - } - - stream.next_in = s_inbuf; - stream.avail_in = n; - - infile_remaining -= n; - } - - status = inflate(&stream, Z_SYNC_FLUSH); - - if ((status == Z_STREAM_END) || (!stream.avail_out)) - { - // Output buffer is full, or decompression is done, so write buffer to output file. - uint n = BUF_SIZE - stream.avail_out; - if (fwrite(s_outbuf, 1, n, pOutfile) != n) - { - printf("Failed writing to output file!\n"); - return EXIT_FAILURE; - } - stream.next_out = s_outbuf; - stream.avail_out = BUF_SIZE; - } - - if (status == Z_STREAM_END) - break; - else if (status != Z_OK) - { - printf("inflate() failed with status %i!\n", status); - return EXIT_FAILURE; - } - } - - if (inflateEnd(&stream) != Z_OK) - { - printf("inflateEnd() failed!\n"); - return EXIT_FAILURE; - } - } - else - { - printf("Invalid mode!\n"); - return EXIT_FAILURE; - } - - fclose(pInfile); - if (EOF == fclose(pOutfile)) - { - printf("Failed writing to output file!\n"); - return EXIT_FAILURE; - } - - printf("Total input bytes: %u\n", (mz_uint32)stream.total_in); - printf("Total output bytes: %u\n", (mz_uint32)stream.total_out); - printf("Success.\n"); - return EXIT_SUCCESS; -} diff --git a/code/components/jomjol_fileserver_ota/miniz/examples/example4.c b/code/components/jomjol_fileserver_ota/miniz/examples/example4.c deleted file mode 100644 index ac49e7f2..00000000 --- a/code/components/jomjol_fileserver_ota/miniz/examples/example4.c +++ /dev/null @@ -1,102 +0,0 @@ -// example4.c - Uses tinfl.c to decompress a zlib stream in memory to an output file -// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. -#include "miniz.h" -#include -#include - -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint; - -#define my_max(a,b) (((a) > (b)) ? (a) : (b)) -#define my_min(a,b) (((a) < (b)) ? (a) : (b)) - -static int tinfl_put_buf_func(const void* pBuf, int len, void *pUser) -{ - return len == (int)fwrite(pBuf, 1, len, (FILE*)pUser); -} - -int main(int argc, char *argv[]) -{ - int status; - FILE *pInfile, *pOutfile; - uint infile_size, outfile_size; - size_t in_buf_size; - uint8 *pCmp_data; - long file_loc; - - if (argc != 3) - { - printf("Usage: example4 infile outfile\n"); - printf("Decompresses zlib stream in file infile to file outfile.\n"); - printf("Input file must be able to fit entirely in memory.\n"); - printf("example3 can be used to create compressed zlib streams.\n"); - return EXIT_FAILURE; - } - - // Open input file. - pInfile = fopen(argv[1], "rb"); - if (!pInfile) - { - printf("Failed opening input file!\n"); - return EXIT_FAILURE; - } - - // Determine input file's size. - fseek(pInfile, 0, SEEK_END); - file_loc = ftell(pInfile); - fseek(pInfile, 0, SEEK_SET); - - if ((file_loc < 0) || ((mz_uint64)file_loc > INT_MAX)) - { - // This is not a limitation of miniz or tinfl, but this example. - printf("File is too large to be processed by this example.\n"); - return EXIT_FAILURE; - } - - infile_size = (uint)file_loc; - - pCmp_data = (uint8 *)malloc(infile_size); - if (!pCmp_data) - { - printf("Out of memory!\n"); - return EXIT_FAILURE; - } - if (fread(pCmp_data, 1, infile_size, pInfile) != infile_size) - { - printf("Failed reading input file!\n"); - return EXIT_FAILURE; - } - - // Open output file. - pOutfile = fopen(argv[2], "wb"); - if (!pOutfile) - { - printf("Failed opening output file!\n"); - return EXIT_FAILURE; - } - - printf("Input file size: %u\n", infile_size); - - in_buf_size = infile_size; - status = tinfl_decompress_mem_to_callback(pCmp_data, &in_buf_size, tinfl_put_buf_func, pOutfile, TINFL_FLAG_PARSE_ZLIB_HEADER); - if (!status) - { - printf("tinfl_decompress_mem_to_callback() failed with status %i!\n", status); - return EXIT_FAILURE; - } - - outfile_size = ftell(pOutfile); - - fclose(pInfile); - if (EOF == fclose(pOutfile)) - { - printf("Failed writing to output file!\n"); - return EXIT_FAILURE; - } - - printf("Total input bytes: %u\n", (uint)in_buf_size); - printf("Total output bytes: %u\n", outfile_size); - printf("Success.\n"); - return EXIT_SUCCESS; -} diff --git a/code/components/jomjol_fileserver_ota/miniz/examples/example5.c b/code/components/jomjol_fileserver_ota/miniz/examples/example5.c deleted file mode 100644 index 2e47199f..00000000 --- a/code/components/jomjol_fileserver_ota/miniz/examples/example5.c +++ /dev/null @@ -1,327 +0,0 @@ -// example5.c - Demonstrates how to use miniz.c's low-level tdefl_compress() and tinfl_inflate() API's for simple file to file compression/decompression. -// The low-level API's are the fastest, make no use of dynamic memory allocation, and are the most flexible functions exposed by miniz.c. -// Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. -// For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c. - -// Purposely disable a whole bunch of stuff this low-level example doesn't use. -#define MINIZ_NO_STDIO -#define MINIZ_NO_ARCHIVE_APIS -#define MINIZ_NO_TIME -#define MINIZ_NO_ZLIB_APIS -#define MINIZ_NO_MALLOC -#include "miniz.h" - -// Now include stdio.h because this test uses fopen(), etc. (but we still don't want miniz.c's stdio stuff, for testing). -#include -#include - -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint; - -#define my_max(a,b) (((a) > (b)) ? (a) : (b)) -#define my_min(a,b) (((a) < (b)) ? (a) : (b)) - -// IN_BUF_SIZE is the size of the file read buffer. -// IN_BUF_SIZE must be >= 1 -#define IN_BUF_SIZE (1024*512) -static uint8 s_inbuf[IN_BUF_SIZE]; - -// COMP_OUT_BUF_SIZE is the size of the output buffer used during compression. -// COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE -#define COMP_OUT_BUF_SIZE (1024*512) - -// OUT_BUF_SIZE is the size of the output buffer used during decompression. -// OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses) -//#define OUT_BUF_SIZE (TINFL_LZ_DICT_SIZE) -#define OUT_BUF_SIZE (1024*512) -static uint8 s_outbuf[OUT_BUF_SIZE]; - -// tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k). -// This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. -tdefl_compressor g_deflator; - -int main(int argc, char *argv[]) -{ - const char *pMode; - FILE *pInfile, *pOutfile; - uint infile_size; - int level = 9; - int p = 1; - const char *pSrc_filename; - const char *pDst_filename; - const void *next_in = s_inbuf; - size_t avail_in = 0; - void *next_out = s_outbuf; - size_t avail_out = OUT_BUF_SIZE; - size_t total_in = 0, total_out = 0; - long file_loc; - - assert(COMP_OUT_BUF_SIZE <= OUT_BUF_SIZE); - - printf("miniz.c example5 (demonstrates tinfl/tdefl)\n"); - - if (argc < 4) - { - printf("File to file compression/decompression using the low-level tinfl/tdefl API's.\n"); - printf("Usage: example5 [options] [mode:c or d] infile outfile\n"); - printf("\nModes:\n"); - printf("c - Compresses file infile to a zlib stream in file outfile\n"); - printf("d - Decompress zlib stream in file infile to file outfile\n"); - printf("\nOptions:\n"); - printf("-l[0-10] - Compression level, higher values are slower, 0 is none.\n"); - return EXIT_FAILURE; - } - - while ((p < argc) && (argv[p][0] == '-')) - { - switch (argv[p][1]) - { - case 'l': - { - level = atoi(&argv[1][2]); - if ((level < 0) || (level > 10)) - { - printf("Invalid level!\n"); - return EXIT_FAILURE; - } - break; - } - default: - { - printf("Invalid option: %s\n", argv[p]); - return EXIT_FAILURE; - } - } - p++; - } - - if ((argc - p) < 3) - { - printf("Must specify mode, input filename, and output filename after options!\n"); - return EXIT_FAILURE; - } - else if ((argc - p) > 3) - { - printf("Too many filenames!\n"); - return EXIT_FAILURE; - } - - pMode = argv[p++]; - if (!strchr("cCdD", pMode[0])) - { - printf("Invalid mode!\n"); - return EXIT_FAILURE; - } - - pSrc_filename = argv[p++]; - pDst_filename = argv[p++]; - - printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename); - - // Open input file. - pInfile = fopen(pSrc_filename, "rb"); - if (!pInfile) - { - printf("Failed opening input file!\n"); - return EXIT_FAILURE; - } - - // Determine input file's size. - fseek(pInfile, 0, SEEK_END); - file_loc = ftell(pInfile); - fseek(pInfile, 0, SEEK_SET); - - if ((file_loc < 0) || ((mz_uint64)file_loc > INT_MAX)) - { - // This is not a limitation of miniz or tinfl, but this example. - printf("File is too large to be processed by this example.\n"); - return EXIT_FAILURE; - } - - infile_size = (uint)file_loc; - - // Open output file. - pOutfile = fopen(pDst_filename, "wb"); - if (!pOutfile) - { - printf("Failed opening output file!\n"); - return EXIT_FAILURE; - } - - printf("Input file size: %u\n", infile_size); - - if ((pMode[0] == 'c') || (pMode[0] == 'C')) - { - // The number of dictionary probes to use at each compression level (0-10). 0=implies fastest/minimal possible probing. - static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; - - tdefl_status status; - uint infile_remaining = infile_size; - - // create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). - mz_uint comp_flags = TDEFL_WRITE_ZLIB_HEADER | s_tdefl_num_probes[MZ_MIN(10, level)] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); - if (!level) - comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; - - // Initialize the low-level compressor. - status = tdefl_init(&g_deflator, NULL, NULL, comp_flags); - if (status != TDEFL_STATUS_OKAY) - { - printf("tdefl_init() failed!\n"); - return EXIT_FAILURE; - } - - avail_out = COMP_OUT_BUF_SIZE; - - // Compression. - for ( ; ; ) - { - size_t in_bytes, out_bytes; - - if (!avail_in) - { - // Input buffer is empty, so read more bytes from input file. - uint n = my_min(IN_BUF_SIZE, infile_remaining); - - if (fread(s_inbuf, 1, n, pInfile) != n) - { - printf("Failed reading from input file!\n"); - return EXIT_FAILURE; - } - - next_in = s_inbuf; - avail_in = n; - - infile_remaining -= n; - //printf("Input bytes remaining: %u\n", infile_remaining); - } - - in_bytes = avail_in; - out_bytes = avail_out; - // Compress as much of the input as possible (or all of it) to the output buffer. - status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, infile_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); - - next_in = (const char *)next_in + in_bytes; - avail_in -= in_bytes; - total_in += in_bytes; - - next_out = (char *)next_out + out_bytes; - avail_out -= out_bytes; - total_out += out_bytes; - - if ((status != TDEFL_STATUS_OKAY) || (!avail_out)) - { - // Output buffer is full, or compression is done or failed, so write buffer to output file. - uint n = COMP_OUT_BUF_SIZE - (uint)avail_out; - if (fwrite(s_outbuf, 1, n, pOutfile) != n) - { - printf("Failed writing to output file!\n"); - return EXIT_FAILURE; - } - next_out = s_outbuf; - avail_out = COMP_OUT_BUF_SIZE; - } - - if (status == TDEFL_STATUS_DONE) - { - // Compression completed successfully. - break; - } - else if (status != TDEFL_STATUS_OKAY) - { - // Compression somehow failed. - printf("tdefl_compress() failed with status %i!\n", status); - return EXIT_FAILURE; - } - } - } - else if ((pMode[0] == 'd') || (pMode[0] == 'D')) - { - // Decompression. - uint infile_remaining = infile_size; - - tinfl_decompressor inflator; - tinfl_init(&inflator); - - for ( ; ; ) - { - size_t in_bytes, out_bytes; - tinfl_status status; - if (!avail_in) - { - // Input buffer is empty, so read more bytes from input file. - uint n = my_min(IN_BUF_SIZE, infile_remaining); - - if (fread(s_inbuf, 1, n, pInfile) != n) - { - printf("Failed reading from input file!\n"); - return EXIT_FAILURE; - } - - next_in = s_inbuf; - avail_in = n; - - infile_remaining -= n; - } - - in_bytes = avail_in; - out_bytes = avail_out; - status = tinfl_decompress(&inflator, (const mz_uint8 *)next_in, &in_bytes, s_outbuf, (mz_uint8 *)next_out, &out_bytes, (infile_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0) | TINFL_FLAG_PARSE_ZLIB_HEADER); - - avail_in -= in_bytes; - next_in = (const mz_uint8 *)next_in + in_bytes; - total_in += in_bytes; - - avail_out -= out_bytes; - next_out = (mz_uint8 *)next_out + out_bytes; - total_out += out_bytes; - - if ((status <= TINFL_STATUS_DONE) || (!avail_out)) - { - // Output buffer is full, or decompression is done, so write buffer to output file. - uint n = OUT_BUF_SIZE - (uint)avail_out; - if (fwrite(s_outbuf, 1, n, pOutfile) != n) - { - printf("Failed writing to output file!\n"); - return EXIT_FAILURE; - } - next_out = s_outbuf; - avail_out = OUT_BUF_SIZE; - } - - // If status is <= TINFL_STATUS_DONE then either decompression is done or something went wrong. - if (status <= TINFL_STATUS_DONE) - { - if (status == TINFL_STATUS_DONE) - { - // Decompression completed successfully. - break; - } - else - { - // Decompression failed. - printf("tinfl_decompress() failed with status %i!\n", status); - return EXIT_FAILURE; - } - } - } - } - else - { - printf("Invalid mode!\n"); - return EXIT_FAILURE; - } - - fclose(pInfile); - if (EOF == fclose(pOutfile)) - { - printf("Failed writing to output file!\n"); - return EXIT_FAILURE; - } - - printf("Total input bytes: %u\n", (mz_uint32)total_in); - printf("Total output bytes: %u\n", (mz_uint32)total_out); - printf("Success.\n"); - return EXIT_SUCCESS; -} diff --git a/code/components/jomjol_fileserver_ota/miniz/examples/example6.c b/code/components/jomjol_fileserver_ota/miniz/examples/example6.c deleted file mode 100644 index 5eeb9628..00000000 --- a/code/components/jomjol_fileserver_ota/miniz/examples/example6.c +++ /dev/null @@ -1,166 +0,0 @@ -// example6.c - Demonstrates how to miniz's PNG writer func -// Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. -// Mandlebrot set code from http://rosettacode.org/wiki/Mandelbrot_set#C -// Must link this example against libm on Linux. - -// Purposely disable a whole bunch of stuff this low-level example doesn't use. -#define MINIZ_NO_STDIO -#define MINIZ_NO_TIME -#define MINIZ_NO_ZLIB_APIS -#include "miniz.h" - -// Now include stdio.h because this test uses fopen(), etc. (but we still don't want miniz.c's stdio stuff, for testing). -#include -#include -#include - -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint; - -typedef struct -{ - uint8 r, g, b; -} rgb_t; - -static void hsv_to_rgb(int hue, int min, int max, rgb_t *p) -{ - const int invert = 0; - const int saturation = 1; - const int color_rotate = 0; - - if (min == max) max = min + 1; - if (invert) hue = max - (hue - min); - if (!saturation) { - p->r = p->g = p->b = 255 * (max - hue) / (max - min); - return; - } else { - const double h_dbl = fmod(color_rotate + 1e-4 + 4.0 * (hue - min) / (max - min), 6); - const double c_dbl = 255 * saturation; - const double X_dbl = c_dbl * (1 - fabs(fmod(h_dbl, 2) - 1)); - const int h = (int)h_dbl; - const int c = (int)c_dbl; - const int X = (int)X_dbl; - - p->r = p->g = p->b = 0; - - switch(h) { - case 0: p->r = c; p->g = X; return; - case 1: p->r = X; p->g = c; return; - case 2: p->g = c; p->b = X; return; - case 3: p->g = X; p->b = c; return; - case 4: p->r = X; p->b = c; return; - default:p->r = c; p->b = X; - } - } -} - -int main(int argc, char *argv[]) -{ - // Image resolution - const int iXmax = 4096; - const int iYmax = 4096; - - // Output filename - static const char *pFilename = "mandelbrot.png"; - - int iX, iY; - const double CxMin = -2.5; - const double CxMax = 1.5; - const double CyMin = -2.0; - const double CyMax = 2.0; - - double PixelWidth = (CxMax - CxMin) / iXmax; - double PixelHeight = (CyMax - CyMin) / iYmax; - - // Z=Zx+Zy*i ; Z0 = 0 - double Zx, Zy; - double Zx2, Zy2; // Zx2=Zx*Zx; Zy2=Zy*Zy - - int Iteration; - const int IterationMax = 200; - - // bail-out value , radius of circle - const double EscapeRadius = 2; - double ER2=EscapeRadius * EscapeRadius; - - uint8 *pImage = (uint8 *)malloc(iXmax * 3 * iYmax); - - // world ( double) coordinate = parameter plane - double Cx,Cy; - - int MinIter = 9999, MaxIter = 0; - - (void)argc, (void)argv; - - for(iY = 0; iY < iYmax; iY++) - { - Cy = CyMin + iY * PixelHeight; - if (fabs(Cy) < PixelHeight/2) - Cy = 0.0; // Main antenna - - for(iX = 0; iX < iXmax; iX++) - { - uint8 *color = pImage + (iX * 3) + (iY * iXmax * 3); - - Cx = CxMin + iX * PixelWidth; - - // initial value of orbit = critical point Z= 0 - Zx = 0.0; - Zy = 0.0; - Zx2 = Zx * Zx; - Zy2 = Zy * Zy; - - for (Iteration=0;Iteration> 8; - color[2] = 0; - - if (Iteration < MinIter) - MinIter = Iteration; - if (Iteration > MaxIter) - MaxIter = Iteration; - } - } - - for(iY = 0; iY < iYmax; iY++) - { - for(iX = 0; iX < iXmax; iX++) - { - uint8 *color = (uint8 *)(pImage + (iX * 3) + (iY * iXmax * 3)); - - uint Iterations = color[0] | (color[1] << 8U); - - hsv_to_rgb((int)Iterations, MinIter, MaxIter, (rgb_t *)color); - } - } - - // Now write the PNG image. - { - size_t png_data_size = 0; - void *pPNG_data = tdefl_write_image_to_png_file_in_memory_ex(pImage, iXmax, iYmax, 3, &png_data_size, 6, MZ_FALSE); - if (!pPNG_data) - fprintf(stderr, "tdefl_write_image_to_png_file_in_memory_ex() failed!\n"); - else - { - FILE *pFile = fopen(pFilename, "wb"); - fwrite(pPNG_data, 1, png_data_size, pFile); - fclose(pFile); - printf("Wrote %s\n", pFilename); - } - - // mz_free() is by default just an alias to free() internally, but if you've overridden miniz's allocation funcs you'll probably need to call mz_free(). - mz_free(pPNG_data); - } - - free(pImage); - - return EXIT_SUCCESS; -} diff --git a/code/components/jomjol_fileserver_ota/miniz/readme.md b/code/components/jomjol_fileserver_ota/miniz/readme.md deleted file mode 100644 index 9734435f..00000000 --- a/code/components/jomjol_fileserver_ota/miniz/readme.md +++ /dev/null @@ -1,46 +0,0 @@ -## Miniz - -Miniz is a lossless, high performance data compression library in a single source file that implements the zlib (RFC 1950) and Deflate (RFC 1951) compressed data format specification standards. It supports the most commonly used functions exported by the zlib library, but is a completely independent implementation so zlib's licensing requirements do not apply. Miniz also contains simple to use functions for writing .PNG format image files and reading/writing/appending .ZIP format archives. Miniz's compression speed has been tuned to be comparable to zlib's, and it also has a specialized real-time compressor function designed to compare well against fastlz/minilzo. - -## Usage - -Releases are available at the [releases page](https://github.com/richgel999/miniz/releases) as a pair of `miniz.c`/`miniz.h` files which can be simply added to a project. To create this file pair the different source and header files are [amalgamated](https://www.sqlite.org/amalgamation.html) during build. Alternatively use as cmake or meson module (or build system of your choice). - -## Features - -* MIT licensed -* A portable, single source and header file library written in plain C. Tested with GCC, clang and Visual Studio. -* Easily tuned and trimmed down by defines -* A drop-in replacement for zlib's most used API's (tested in several open source projects that use zlib, such as libpng and libzip). -* Fills a single threaded performance vs. compression ratio gap between several popular real-time compressors and zlib. For example, at level 1, miniz.c compresses around 5-9% better than minilzo, but is approx. 35% slower. At levels 2-9, miniz.c is designed to compare favorably against zlib's ratio and speed. See the miniz performance comparison page for example timings. -* Not a block based compressor: miniz.c fully supports stream based processing using a coroutine-style implementation. The zlib-style API functions can be called a single byte at a time if that's all you've got. -* Easy to use. The low-level compressor (tdefl) and decompressor (tinfl) have simple state structs which can be saved/restored as needed with simple memcpy's. The low-level codec API's don't use the heap in any way. -* Entire inflater (including optional zlib header parsing and Adler-32 checking) is implemented in a single function as a coroutine, which is separately available in a small (~550 line) source file: miniz_tinfl.c -* A fairly complete (but totally optional) set of .ZIP archive manipulation and extraction API's. The archive functionality is intended to solve common problems encountered in embedded, mobile, or game development situations. (The archive API's are purposely just powerful enough to write an entire archiver given a bit of additional higher-level logic.) - -## Building miniz - Using vcpkg - -You can download and install miniz using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: - - git clone https://github.com/Microsoft/vcpkg.git - cd vcpkg - ./bootstrap-vcpkg.sh - ./vcpkg integrate install - ./vcpkg install miniz - -The miniz port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. - -## Known Problems - -* No support for encrypted archives. Not sure how useful this stuff is in practice. -* Minimal documentation. The assumption is that the user is already familiar with the basic zlib API. I need to write an API wiki - for now I've tried to place key comments before each enum/API, and I've included 6 examples that demonstrate how to use the module's major features. - -## Special Thanks - -Thanks to Alex Evans for the PNG writer function. Also, thanks to Paul Holden and Thorsten Scheuermann for feedback and testing, Matt Pritchard for all his encouragement, and Sean Barrett's various public domain libraries for inspiration (and encouraging me to write miniz.c in C, which was much more enjoyable and less painful than I thought it would be considering I've been programming in C++ for so long). - -Thanks to Bruce Dawson for reporting a problem with the level_and_flags archive API parameter (which is fixed in v1.12) and general feedback, and Janez Zemva for indirectly encouraging me into writing more examples. - -## Patents - -I was recently asked if miniz avoids patent issues. miniz purposely uses the same core algorithms as the ones used by zlib. The compressor uses vanilla hash chaining as described [here](https://datatracker.ietf.org/doc/html/rfc1951#section-4). Also see the [gzip FAQ](https://web.archive.org/web/20160308045258/http://www.gzip.org/#faq11). In my opinion, if miniz falls prey to a patent attack then zlib/gzip are likely to be at serious risk too. diff --git a/code/components/jomjol_fileserver_ota/miniz/readme2.md b/code/components/jomjol_fileserver_ota/miniz/readme2.md deleted file mode 100644 index 64c69610..00000000 --- a/code/components/jomjol_fileserver_ota/miniz/readme2.md +++ /dev/null @@ -1,8 +0,0 @@ -The files in this folder are a direct extraction of the miniz release from https://github.com/richgel999/miniz/releases - -It should be possible to include the repo directly as a submodule, how ever it causes various issues. See also - - https://github.com/richgel999/miniz/issues/145 - - https://github.com/espressif/esptool/pull/500#issuecomment-574879468 - - For simplicity we therefore use the release files as suggested in the readme. - Additionally we added the CMakeLists.txt and this readme. diff --git a/code/components/jomjol_fileserver_ota/server_file.cpp b/code/components/jomjol_fileserver_ota/server_file.cpp index 253849d8..15b302d1 100644 --- a/code/components/jomjol_fileserver_ota/server_file.cpp +++ b/code/components/jomjol_fileserver_ota/server_file.cpp @@ -6,6 +6,8 @@ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +#include "defines.h" + #include "server_file.h" #include @@ -15,13 +17,9 @@ #include #include -#ifdef __cplusplus -extern "C" { -#endif +#include +#include #include -#ifdef __cplusplus -} -#endif #include "esp_err.h" #include "esp_log.h" @@ -30,16 +28,12 @@ extern "C" { #include #include "esp_http_server.h" -#include "../../include/defines.h" #include "ClassLogFile.h" - #include "MainFlowControl.h" #include "server_help.h" #include "md5.h" -#ifdef ENABLE_MQTT - #include "interface_mqtt.h" -#endif //ENABLE_MQTT +#include "interface_mqtt.h" #include "server_GPIO.h" #include "Helper.h" @@ -48,7 +42,8 @@ extern "C" { static const char *TAG = "OTA FILE"; -struct file_server_data { +struct file_server_data +{ /* Base path of file storage */ char base_path[ESP_VFS_PATH_MAX + 1]; @@ -56,10 +51,6 @@ struct file_server_data { char scratch[SERVER_FILER_SCRATCH_BUFSIZE]; }; -#include -#include -#include - using namespace std; string SUFFIX_ZW = "_tmp"; @@ -71,7 +62,7 @@ esp_err_t get_numbers_file_handler(httpd_req_t *req) { std::string ret = flowctrl.getNumbersName(); -// ESP_LOGI(TAG, "Result get_numbers_file_handler: %s", ret.c_str()); + // ESP_LOGI(TAG, "Result get_numbers_file_handler: %s", ret.c_str()); httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); httpd_resp_set_type(req, "text/plain"); @@ -88,7 +79,7 @@ esp_err_t get_data_file_handler(httpd_req_t *req) std::string _filename, _fileext; size_t pos = 0; - + const char verz_name[] = "/sdcard/log/data"; ESP_LOGD(TAG, "Suche data files in /sdcard/log/data"); @@ -96,13 +87,14 @@ esp_err_t get_data_file_handler(httpd_req_t *req) httpd_resp_set_type(req, "text/plain"); DIR *dir = opendir(verz_name); - while ((entry = readdir(dir)) != NULL) + while ((entry = readdir(dir)) != NULL) { _filename = std::string(entry->d_name); ESP_LOGD(TAG, "File: %s", _filename.c_str()); // ignore all files with starting dot (hidden files) - if (_filename.rfind(".", 0) == 0) { + if (_filename.rfind(".", 0) == 0) + { continue; } @@ -131,7 +123,7 @@ esp_err_t get_tflite_file_handler(httpd_req_t *req) std::string _filename, _fileext; size_t pos = 0; - + const char verz_name[] = "/sdcard/config"; ESP_LOGD(TAG, "Suche TFLITE in /sdcard/config/"); @@ -139,13 +131,14 @@ esp_err_t get_tflite_file_handler(httpd_req_t *req) httpd_resp_set_type(req, "text/plain"); DIR *dir = opendir(verz_name); - while ((entry = readdir(dir)) != NULL) + while ((entry = readdir(dir)) != NULL) { _filename = std::string(entry->d_name); ESP_LOGD(TAG, "File: %s", _filename.c_str()); // ignore all files with starting dot (hidden files) - if (_filename.rfind(".", 0) == 0) { + if (_filename.rfind(".", 0) == 0) + { continue; } @@ -186,7 +179,8 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const file_server_data *server_data = (file_server_data *)req->user_ctx; - if ((strlen(dirpath_corrected) - 1) > strlen(server_data->base_path)) { + if ((strlen(dirpath_corrected) - 1) > strlen(server_data->base_path)) + { // if dirpath is not mountpoint, the last "\" needs to be removed dirpath_corrected[strlen(dirpath_corrected) - 1] = '\0'; } @@ -200,7 +194,8 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const strlcpy(entrypath, dirpath, sizeof(entrypath)); ESP_LOGD(TAG, "entrypath: <%s>", entrypath); - if (!pdir) { + if (!pdir) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to stat dir: " + std::string(dirpath) + "!"); // Respond with 404 Not Found httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404()); @@ -211,6 +206,7 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const // Send HTML file header httpd_resp_sendstr_chunk(req, ""); + httpd_resp_sendstr_chunk(req, ""); httpd_resp_sendstr_chunk(req, ""); httpd_resp_sendstr_chunk(req, ""); httpd_resp_sendstr_chunk(req, ""); @@ -244,7 +240,8 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const "" "NameTypeSize"); - if (!readonly) { + if (!readonly) + { httpd_resp_sendstr_chunk(req, "
"); @@ -253,27 +250,34 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const httpd_resp_sendstr_chunk(req, "\n"); // Iterate over all files / folders and fetch their names and sizes - while ((entry = readdir(pdir)) != NULL) { + while ((entry = readdir(pdir)) != NULL) + { // wlan.ini soll nicht angezeigt werden! - if (strcmp("wlan.ini", entry->d_name) != 0) { + if (strcmp("wlan.ini", entry->d_name) != 0) + { entrytype = (entry->d_type == DT_DIR ? "directory" : "file"); strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len); ESP_LOGD(TAG, "Entrypath: %s", entrypath); - if (stat(entrypath, &entry_stat) == -1) { + if (stat(entrypath, &entry_stat) == -1) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to stat " + std::string(entrytype) + ": " + std::string(entry->d_name)); continue; } - if (entry->d_type == DT_DIR) { + if (entry->d_type == DT_DIR) + { strcpy(entrysize, "-\0"); } - else { - if (entry_stat.st_size >= 1024) { + else + { + if (entry_stat.st_size >= 1024) + { sprintf(entrysize, "%ld KiB", entry_stat.st_size / 1024); // kBytes } - else { + else + { sprintf(entrysize, "%ld B", entry_stat.st_size); // Bytes } } @@ -286,7 +290,8 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const httpd_resp_sendstr_chunk(req, uripath); httpd_resp_sendstr_chunk(req, entry->d_name); - if (entry->d_type == DT_DIR) { + if (entry->d_type == DT_DIR) + { httpd_resp_sendstr_chunk(req, "/"); } @@ -297,7 +302,8 @@ static esp_err_t http_resp_dir_html(httpd_req_t *req, const char *dirpath, const httpd_resp_sendstr_chunk(req, ""); httpd_resp_sendstr_chunk(req, entrysize); - if (!readonly) { + if (!readonly) + { httpd_resp_sendstr_chunk(req, ""); httpd_resp_sendstr_chunk(req, "
uri); std::string currentfilename = LogFile.GetCurrentFileNameData(); @@ -350,7 +360,8 @@ static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file) ESP_LOGD(TAG, "uri: %s, filename: %s, filepath: %s", req->uri, currentfilename.c_str(), currentfilename.c_str()); fd = fopen(currentfilename.c_str(), "r"); - if (!fd) { + if (!fd) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + currentfilename + "!"); /* Respond with 404 Error */ httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404()); @@ -359,30 +370,36 @@ static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file) httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); -// ESP_LOGI(TAG, "Sending file: %s (%ld bytes)...", &filename, file_stat.st_size); + // ESP_LOGI(TAG, "Sending file: %s (%ld bytes)...", &filename, file_stat.st_size); set_content_type_from_file(req, currentfilename.c_str()); - if (!send_full_file) { // Send only last part of file + if (!send_full_file) + { // Send only last part of file ESP_LOGD(TAG, "Sending last %d bytes of the actual datafile!", LOGFILE_LAST_PART_BYTES); /* Adapted from https://www.geeksforgeeks.org/implement-your-own-tail-read-last-n-lines-of-a-huge-file/ */ - if (fseek(fd, 0, SEEK_END)) { + if (fseek(fd, 0, SEEK_END)) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to get to end of file!"); return ESP_FAIL; } - else { + else + { long pos = ftell(fd); // Number of bytes in the file ESP_LOGI(TAG, "File contains %ld bytes", pos); - if (fseek(fd, pos - std::min((long)LOGFILE_LAST_PART_BYTES, pos), SEEK_SET)) { // Go LOGFILE_LAST_PART_BYTES bytes back from EOF + if (fseek(fd, pos - std::min((long)LOGFILE_LAST_PART_BYTES, pos), SEEK_SET)) + { // Go LOGFILE_LAST_PART_BYTES bytes back from EOF LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to go back " + to_string(std::min((long)LOGFILE_LAST_PART_BYTES, pos)) + " bytes within the file!"); return ESP_FAIL; } } /* Find end of line */ - while (1) { - if (fgetc(fd) == '\n') { + while (1) + { + if (fgetc(fd) == '\n') + { break; } } @@ -391,12 +408,14 @@ static esp_err_t send_datafile(httpd_req_t *req, bool send_full_file) /* Retrieve the pointer to scratch buffer for temporary storage */ char *chunk = ((struct file_server_data *)req->user_ctx)->scratch; size_t chunksize; - do { + do + { /* Read file in chunks into the scratch buffer */ chunksize = fread(chunk, 1, SERVER_FILER_SCRATCH_BUFSIZE, fd); /* Send the buffer contents as HTTP response chunk */ - if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) { + if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) + { fclose(fd); LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!"); /* Abort sending file */ @@ -422,10 +441,10 @@ static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file) { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "log_get_last_part_handler"); FILE *fd = NULL; - //struct stat file_stat; + // struct stat file_stat; ESP_LOGI(TAG, "uri: %s", req->uri); - const char* filename = ""; + const char *filename = ""; std::string currentfilename = LogFile.GetCurrentFileName(); @@ -435,7 +454,8 @@ static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file) LogFile.CloseLogFileAppendHandle(); fd = fopen(currentfilename.c_str(), "r"); - if (!fd) { + if (!fd) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + currentfilename + "!"); /* Respond with 404 Error */ httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404()); @@ -444,30 +464,36 @@ static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file) httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); -// ESP_LOGI(TAG, "Sending file: %s (%ld bytes)...", &filename, file_stat.st_size); + // ESP_LOGI(TAG, "Sending file: %s (%ld bytes)...", &filename, file_stat.st_size); set_content_type_from_file(req, filename); - if (!send_full_file) { // Send only last part of file + if (!send_full_file) + { // Send only last part of file ESP_LOGD(TAG, "Sending last %d bytes of the actual logfile!", LOGFILE_LAST_PART_BYTES); /* Adapted from https://www.geeksforgeeks.org/implement-your-own-tail-read-last-n-lines-of-a-huge-file/ */ - if (fseek(fd, 0, SEEK_END)) { + if (fseek(fd, 0, SEEK_END)) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to get to end of file!"); return ESP_FAIL; } - else { + else + { long pos = ftell(fd); // Number of bytes in the file ESP_LOGI(TAG, "File contains %ld bytes", pos); - if (fseek(fd, pos - std::min((long)LOGFILE_LAST_PART_BYTES, pos), SEEK_SET)) { // Go LOGFILE_LAST_PART_BYTES bytes back from EOF + if (fseek(fd, pos - std::min((long)LOGFILE_LAST_PART_BYTES, pos), SEEK_SET)) + { // Go LOGFILE_LAST_PART_BYTES bytes back from EOF LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to go back " + to_string(std::min((long)LOGFILE_LAST_PART_BYTES, pos)) + " bytes within the file!"); return ESP_FAIL; } } /* Find end of line */ - while (1) { - if (fgetc(fd) == '\n') { + while (1) + { + if (fgetc(fd) == '\n') + { break; } } @@ -476,12 +502,14 @@ static esp_err_t send_logfile(httpd_req_t *req, bool send_full_file) /* Retrieve the pointer to scratch buffer for temporary storage */ char *chunk = ((struct file_server_data *)req->user_ctx)->scratch; size_t chunksize; - do { + do + { /* Read file in chunks into the scratch buffer */ chunksize = fread(chunk, 1, SERVER_FILER_SCRATCH_BUFSIZE, fd); /* Send the buffer contents as HTTP response chunk */ - if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) { + if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) + { fclose(fd); LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!"); /* Abort sending file */ @@ -513,14 +541,15 @@ static esp_err_t download_get_handler(httpd_req_t *req) ESP_LOGD(TAG, "uri: %s", req->uri); const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, - req->uri + sizeof("/fileserver") - 1, sizeof(filepath)); + req->uri + sizeof("/fileserver") - 1, sizeof(filepath)); ESP_LOGD(TAG, "uri: %s, filename: %s, filepath: %s", req->uri, filename, filepath); -// filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, -// req->uri, sizeof(filepath)); + // filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, + // req->uri, sizeof(filepath)); - if (!filename) { + if (!filename) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Filename is too long"); /* Respond with 414 Error */ httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long"); @@ -528,18 +557,22 @@ static esp_err_t download_get_handler(httpd_req_t *req) } /* If name has trailing '/', respond with directory contents */ - if (filename[strlen(filename) - 1] == '/') { + if (filename[strlen(filename) - 1] == '/') + { bool readonly = false; size_t buf_len = httpd_req_get_url_query_len(req) + 1; - if (buf_len > 1) { + if (buf_len > 1) + { char buf[buf_len]; - if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) { + if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) + { ESP_LOGI(TAG, "Found URL query => %s", buf); char param[32]; /* Get value of expected key from query string */ - if (httpd_query_key_value(buf, "readonly", param, sizeof(param)) == ESP_OK) { + if (httpd_query_key_value(buf, "readonly", param, sizeof(param)) == ESP_OK) + { ESP_LOGI(TAG, "Found URL query parameter => readonly=%s", param); - readonly = (strcmp(param,"true") == 0); + readonly = (strcmp(param, "true") == 0); } } } @@ -548,9 +581,10 @@ static esp_err_t download_get_handler(httpd_req_t *req) return http_resp_dir_html(req, filepath, filename, readonly); } - std::string testwlan = toUpper(std::string(filename)); + std::string testwlan = to_upper(std::string(filename)); - if ((stat(filepath, &file_stat) == -1) || (testwlan.compare("/WLAN.INI") == 0 )) { // wlan.ini soll nicht angezeigt werden! + if ((stat(filepath, &file_stat) == -1) || (testwlan.compare("/WLAN.INI") == 0)) + { // wlan.ini soll nicht angezeigt werden! /* If file not present on SPIFFS check if URI * corresponds to one of the hardcoded paths */ @@ -561,7 +595,8 @@ static esp_err_t download_get_handler(httpd_req_t *req) } fd = fopen(filepath, "r"); - if (!fd) { + if (!fd) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read file: " + std::string(filepath) + "!"); /* Respond with 404 Error */ httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404()); @@ -576,14 +611,16 @@ static esp_err_t download_get_handler(httpd_req_t *req) /* Retrieve the pointer to scratch buffer for temporary storage */ char *chunk = ((struct file_server_data *)req->user_ctx)->scratch; size_t chunksize; - do { + do + { /* Read file in chunks into the scratch buffer */ chunksize = fread(chunk, 1, SERVER_FILER_SCRATCH_BUFSIZE, fd); /* Send buffer contents as HTTP chunk. If empty this functions as a * last-chunk message, signaling end-of-response, to the HTTP client. * See RFC 2616, section 3.6.1 for details on Chunked Transfer Encoding. */ - if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) { + if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) + { fclose(fd); LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File sending failed!"); /* Abort sending file */ @@ -619,21 +656,24 @@ static esp_err_t upload_post_handler(httpd_req_t *req) /* Note sizeof() counts NULL termination hence the -1 */ const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, req->uri + sizeof("/upload") - 1, sizeof(filepath)); - if (!filename) { + if (!filename) + { /* Respond with 413 Error */ httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long"); return ESP_FAIL; } /* Filename cannot have a trailing '/' */ - if (filename[strlen(filename) - 1] == '/') { + if (filename[strlen(filename) - 1] == '/') + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Invalid filename: " + string(filename)); /* Respond with 400 Bad Request */ httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename"); return ESP_FAIL; } - if (stat(filepath, &file_stat) == 0) { + if (stat(filepath, &file_stat) == 0) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File already exists: " + string(filepath)); /* Respond with 400 Bad Request */ httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "File already exists"); @@ -641,19 +681,20 @@ static esp_err_t upload_post_handler(httpd_req_t *req) } /* File cannot be larger than a limit */ - if (req->content_len > MAX_FILE_SIZE) { + if (req->content_len > MAX_FILE_SIZE) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File too large: " + to_string(req->content_len) + " bytes"); /* Respond with 400 Bad Request */ httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, - "File size must be less than " - MAX_FILE_SIZE_STR "!"); + "File size must be less than " MAX_FILE_SIZE_STR "!"); /* Return failure to close underlying connection else the * incoming file content will keep the socket busy */ return ESP_FAIL; } fd = fopen(filepath, "w"); - if (!fd) { + if (!fd) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create file: " + string(filepath)); /* Respond with 500 Internal Server Error */ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file"); @@ -670,12 +711,15 @@ static esp_err_t upload_post_handler(httpd_req_t *req) * the size of the file being uploaded */ int remaining = req->content_len; - while (remaining > 0) { + while (remaining > 0) + { ESP_LOGI(TAG, "Remaining size: %d", remaining); /* Receive the file part by part into a buffer */ - if ((received = httpd_req_recv(req, buf, MIN(remaining, SERVER_FILER_SCRATCH_BUFSIZE))) <= 0) { - if (received == HTTPD_SOCK_ERR_TIMEOUT) { + if ((received = httpd_req_recv(req, buf, MIN(remaining, SERVER_FILER_SCRATCH_BUFSIZE))) <= 0) + { + if (received == HTTPD_SOCK_ERR_TIMEOUT) + { /* Retry if timeout occurred */ continue; } @@ -692,7 +736,8 @@ static esp_err_t upload_post_handler(httpd_req_t *req) } /* Write buffer content to file on storage */ - if (received && (received != fwrite(buf, 1, received, fd))) { + if (received && (received != fwrite(buf, 1, received, fd))) + { /* Couldn't write everything to file! * Storage may be full? */ fclose(fd); @@ -715,11 +760,13 @@ static esp_err_t upload_post_handler(httpd_req_t *req) ESP_LOGI(TAG, "File reception completed"); string s = req->uri; - if (isInString(s, "?md5")) { + if (is_in_string(s, "?md5")) + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Calculate and return MD5 sum..."); - + fd = fopen(filepath, "r"); - if (!fd) { + if (!fd) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to open file for reading: " + string(filepath)); /* Respond with 500 Internal Server Error */ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to open file for reading"); @@ -734,7 +781,8 @@ static esp_err_t upload_post_handler(httpd_req_t *req) md5File(fd, result); fclose(fd); - for (int i = 0; i < sizeof(result); i++) { + for (int i = 0; i < sizeof(result); i++) + { snprintf(hex, sizeof(hex), "%02x", result[i]); md5hex.append(hex); } @@ -745,13 +793,14 @@ static esp_err_t upload_post_handler(httpd_req_t *req) httpd_resp_sendstr(req, response.c_str()); } - else { // Return file server page + else + { // Return file server page std::string directory = std::string(filepath); size_t zw = directory.find("/"); size_t found = zw; while (zw != std::string::npos) { - zw = directory.find("/", found+1); + zw = directory.find("/", found + 1); if (zw != std::string::npos) found = zw; } @@ -760,7 +809,7 @@ static esp_err_t upload_post_handler(httpd_req_t *req) ESP_LOGD(TAG, "Directory: %s, start_fn: %d, found: %d", directory.c_str(), start_fn, found); directory = directory.substr(start_fn, found - start_fn + 1); directory = "/fileserver" + directory; - // ESP_LOGD(TAG, "Directory danach 2: %s", directory.c_str()); + // ESP_LOGD(TAG, "Directory danach 2: %s", directory.c_str()); /* Redirect onto root to see the updated file list */ if (strcmp(filename, "/config/config.ini") == 0 || @@ -773,11 +822,12 @@ static esp_err_t upload_post_handler(httpd_req_t *req) strcmp(filename, "/img_tmp/ref0_org.jpg") == 0 || strcmp(filename, "/img_tmp/ref1.jpg") == 0 || strcmp(filename, "/img_tmp/ref1_org.jpg") == 0 || - strcmp(filename, "/img_tmp/reference.jpg") == 0 ) - { + strcmp(filename, "/img_tmp/reference.jpg") == 0) + { httpd_resp_set_status(req, HTTPD_200); // Avoid reloading of folder content } - else { + else + { httpd_resp_set_status(req, "303 See Other"); // Reload folder content after upload } @@ -795,20 +845,20 @@ static esp_err_t delete_post_handler(httpd_req_t *req) char filepath[FILE_PATH_MAX]; struct stat file_stat; -////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////// char _query[200]; - char _valuechar[30]; + char _valuechar[30]; std::string fn = "/sdcard/firmware/"; std::string _task; std::string directory; - std::string zw; + std::string zw; httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); if (httpd_req_get_url_query_str(req, _query, 200) == ESP_OK) { ESP_LOGD(TAG, "Query: %s", _query); - + if (httpd_query_key_value(_query, "task", _valuechar, 30) == ESP_OK) { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "task is found: " + string(_valuechar)); @@ -821,55 +871,60 @@ static esp_err_t delete_post_handler(httpd_req_t *req) /* Skip leading "/delete" from URI to get filename */ /* Note sizeof() counts NULL termination hence the -1 */ const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, - req->uri + sizeof("/delete") - 1, sizeof(filepath)); - if (!filename) { + req->uri + sizeof("/delete") - 1, sizeof(filepath)); + if (!filename) + { /* Respond with 414 Error */ httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long"); return ESP_FAIL; } zw = std::string(filename); - zw = zw.substr(0, zw.length()-1); + zw = zw.substr(0, zw.length() - 1); directory = "/fileserver" + zw + "/"; zw = "/sdcard" + zw; ESP_LOGD(TAG, "Directory to delete: %s", zw.c_str()); delete_all_in_directory(zw); -// directory = std::string(filepath); -// directory = "/fileserver" + directory; + // directory = std::string(filepath); + // directory = "/fileserver" + directory; ESP_LOGD(TAG, "Location after delete directory content: %s", directory.c_str()); /* Redirect onto root to see the updated file list */ -// httpd_resp_set_status(req, "303 See Other"); -// httpd_resp_set_hdr(req, "Location", directory.c_str()); -// httpd_resp_sendstr(req, "File deleted successfully"); -// return ESP_OK; + // httpd_resp_set_status(req, "303 See Other"); + // httpd_resp_set_hdr(req, "Location", directory.c_str()); + // httpd_resp_sendstr(req, "File deleted successfully"); + // return ESP_OK; } else { /* Skip leading "/delete" from URI to get filename */ /* Note sizeof() counts NULL termination hence the -1 */ const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, - req->uri + sizeof("/delete") - 1, sizeof(filepath)); - if (!filename) { + req->uri + sizeof("/delete") - 1, sizeof(filepath)); + if (!filename) + { /* Respond with 500 Internal Server Error */ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long"); return ESP_FAIL; } /* Filename cannot have a trailing '/' */ - if (filename[strlen(filename) - 1] == '/') { + if (filename[strlen(filename) - 1] == '/') + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Invalid filename: " + string(filename)); /* Respond with 400 Bad Request */ httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename"); return ESP_FAIL; } - if (strcmp(filename, "wlan.ini") == 0) { + if (strcmp(filename, "wlan.ini") == 0) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to delete protected file : " + string(filename)); httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Not allowed to delete wlan.ini"); return ESP_FAIL; } - if (stat(filepath, &file_stat) == -1) { // File does not exist + if (stat(filepath, &file_stat) == -1) + { // File does not exist /* This is ok, we would delete it anyway */ LogFile.WriteToFile(ESP_LOG_INFO, TAG, "File does not exist: " + string(filename)); } @@ -884,7 +939,7 @@ static esp_err_t delete_post_handler(httpd_req_t *req) size_t found = zw; while (zw != std::string::npos) { - zw = directory.find("/", found+1); + zw = directory.find("/", found + 1); if (zw != std::string::npos) found = zw; } @@ -894,7 +949,7 @@ static esp_err_t delete_post_handler(httpd_req_t *req) directory = directory.substr(start_fn, found - start_fn + 1); directory = "/fileserver" + directory; ESP_LOGD(TAG, "Directory danach 4: %s", directory.c_str()); - + ////////////////////////////////////////////////////////////// /* Redirect onto root to see the updated file list */ @@ -908,11 +963,12 @@ static esp_err_t delete_post_handler(httpd_req_t *req) strcmp(filename, "/img_tmp/ref0_org.jpg") == 0 || strcmp(filename, "/img_tmp/ref1.jpg") == 0 || strcmp(filename, "/img_tmp/ref1_org.jpg") == 0 || - strcmp(filename, "/img_tmp/reference.jpg") == 0 ) - { + strcmp(filename, "/img_tmp/reference.jpg") == 0) + { httpd_resp_set_status(req, HTTPD_200); // Avoid reloading of folder content } - else { + else + { httpd_resp_set_status(req, "303 See Other"); // Reload folder content after upload } } @@ -928,19 +984,23 @@ void delete_all_in_directory(std::string _directory) DIR *dir = opendir(_directory.c_str()); std::string filename; - if (!dir) { + if (!dir) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to stat dir: " + _directory); return; } /* Iterate over all files / folders and fetch their names and sizes */ - while ((entry = readdir(dir)) != NULL) { - if (!(entry->d_type == DT_DIR)){ - if (strcmp("wlan.ini", entry->d_name) != 0){ // auf wlan.ini soll nicht zugegriffen werden !!! + while ((entry = readdir(dir)) != NULL) + { + if (!(entry->d_type == DT_DIR)) + { + if (strcmp("wlan.ini", entry->d_name) != 0) + { // auf wlan.ini soll nicht zugegriffen werden !!! filename = _directory + "/" + std::string(entry->d_name); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Deleting file: " + filename); /* Delete file */ - unlink(filename.c_str()); + unlink(filename.c_str()); } }; } @@ -953,12 +1013,12 @@ std::string unzip_new(std::string _in_zip_file, std::string _html_tmp, std::stri mz_bool status; size_t uncomp_size; mz_zip_archive zip_archive; - void* p; + void *p; char archive_filename[64]; std::string zw, ret = ""; std::string directory = ""; - ESP_LOGD(TAG, "miniz.c version: %s", MZ_VERSION); + ESP_LOGD(TAG, "minizip.c version: %s", MZ_VERSION); ESP_LOGD(TAG, "Zipfile: %s", _in_zip_file.c_str()); // Now try to open the archive. @@ -966,7 +1026,7 @@ std::string unzip_new(std::string _in_zip_file, std::string _html_tmp, std::stri status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), 0); if (!status) { - ESP_LOGD(TAG, "mz_zip_reader_init_file() failed!"); + ESP_LOGD(TAG, "mzip_zip_reader_init_file() failed!"); return ret; } @@ -980,7 +1040,7 @@ std::string unzip_new(std::string _in_zip_file, std::string _html_tmp, std::stri status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0); if (!status) { - ESP_LOGD(TAG, "mz_zip_reader_init_file() failed!"); + ESP_LOGD(TAG, "mzip_zip_reader_init_file() failed!"); return ret; } @@ -989,29 +1049,30 @@ std::string unzip_new(std::string _in_zip_file, std::string _html_tmp, std::stri mz_zip_archive_file_stat file_stat; mz_zip_reader_file_stat(&zip_archive, i, &file_stat); sprintf(archive_filename, file_stat.m_filename); - - if (!file_stat.m_is_directory) { - // Try to extract all the files to the heap. - p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0); + + if (!file_stat.m_is_directory) + { + // Try to extract all the files to the heap. + p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0); if (!p) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_extract_file_to_heap() failed on file " + string(archive_filename)); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mzip_zip_reader_extract_file_to_heap() failed on file " + string(archive_filename)); mz_zip_reader_end(&zip_archive); return ret; } - + // Save to File. zw = std::string(archive_filename); ESP_LOGD(TAG, "Rohfilename: %s", zw.c_str()); - if (toUpper(zw) == "FIRMWARE.BIN") + if (to_upper(zw) == "FIRMWARE.BIN") { zw = _target_bin + zw; ret = zw; } else { - std::string _dir = getDirectory(zw); + std::string _dir = get_directory(zw); if ((_dir == "config-initial") && !_initial_setup) { continue; @@ -1020,7 +1081,7 @@ std::string unzip_new(std::string _in_zip_file, std::string _html_tmp, std::stri { _dir = "config"; std::string _s1 = "config-initial"; - FindReplace(zw, _s1, _dir); + find_replace(zw, _s1, _dir); } if (_dir.length() > 0) @@ -1031,28 +1092,28 @@ std::string unzip_new(std::string _in_zip_file, std::string _html_tmp, std::stri { zw = _html_tmp + zw; } - } // files in the html folder shall be redirected to the temporary html folder - if (zw.find(_html_final) == 0) { - FindReplace(zw, _html_final, _html_tmp); + if (zw.find(_html_final) == 0) + { + find_replace(zw, _html_final, _html_tmp); } - + string filename_zw = zw + SUFFIX_ZW; ESP_LOGI(TAG, "File to extract: %s, Temp. Filename: %s", zw.c_str(), filename_zw.c_str()); std::string folder = filename_zw.substr(0, filename_zw.find_last_of('/')); - MakeDir(folder); + make_dir(folder); // extrahieren in zwischendatei - DeleteFile(filename_zw); + delete_file(filename_zw); - FILE* fpTargetFile = fopen(filename_zw.c_str(), "wb"); + FILE *fpTargetFile = fopen(filename_zw.c_str(), "wb"); uint writtenbytes = fwrite(p, 1, (uint)uncomp_size, fpTargetFile); fclose(fpTargetFile); - + bool isokay = true; if (writtenbytes == (uint)uncomp_size) @@ -1062,14 +1123,13 @@ std::string unzip_new(std::string _in_zip_file, std::string _html_tmp, std::stri else { isokay = false; - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in writting extracted file (function fwrite) extracted file \"" + - string(archive_filename) + "\", size " + to_string(uncomp_size)); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in writting extracted file (function fwrite) extracted file \"" + string(archive_filename) + "\", size " + to_string(uncomp_size)); } - DeleteFile(zw); + delete_file(zw); if (!isokay) LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in fwrite \"" + string(archive_filename) + "\", size " + to_string(uncomp_size)); - isokay = isokay && RenameFile(filename_zw, zw); + isokay = isokay && rename_file(filename_zw, zw); if (!isokay) LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ERROR in Rename \"" + filename_zw + "\" to \"" + zw); @@ -1092,17 +1152,18 @@ std::string unzip_new(std::string _in_zip_file, std::string _html_tmp, std::stri return ret; } -void unzip(std::string _in_zip_file, std::string _target_directory){ +void unzip(std::string _in_zip_file, std::string _target_directory) +{ int i, sort_iter; mz_bool status; size_t uncomp_size; mz_zip_archive zip_archive; - void* p; + void *p; char archive_filename[64]; std::string zw; -// static const char* s_Test_archive_filename = "testhtml.zip"; + // static const char* s_Test_archive_filename = "testhtml.zip"; - ESP_LOGD(TAG, "miniz.c version: %s", MZ_VERSION); + ESP_LOGD(TAG, "minizip.c version: %s", MZ_VERSION); ESP_LOGD(TAG, "Zipfile: %s", _in_zip_file.c_str()); ESP_LOGD(TAG, "Target Dir: %s", _target_directory.c_str()); @@ -1111,7 +1172,7 @@ void unzip(std::string _in_zip_file, std::string _target_directory){ status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), 0); if (!status) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_init_file() failed!"); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mzip_zip_reader_init_file() failed!"); return; } @@ -1123,7 +1184,7 @@ void unzip(std::string _in_zip_file, std::string _target_directory){ status = mz_zip_reader_init_file(&zip_archive, _in_zip_file.c_str(), sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0); if (!status) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_init_file() failed!"); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mzip_zip_reader_init_file() failed!"); return; } @@ -1132,12 +1193,12 @@ void unzip(std::string _in_zip_file, std::string _target_directory){ mz_zip_archive_file_stat file_stat; mz_zip_reader_file_stat(&zip_archive, i, &file_stat); sprintf(archive_filename, file_stat.m_filename); - + // Try to extract all the files to the heap. p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0); if (!p) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mz_zip_reader_extract_file_to_heap() failed!"); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mzip_zip_reader_extract_file_to_heap() failed!"); mz_zip_reader_end(&zip_archive); return; } @@ -1146,12 +1207,12 @@ void unzip(std::string _in_zip_file, std::string _target_directory){ zw = std::string(archive_filename); zw = _target_directory + zw; ESP_LOGD(TAG, "File to extract: %s", zw.c_str()); - FILE* fpTargetFile = fopen(zw.c_str(), "wb"); + FILE *fpTargetFile = fopen(zw.c_str(), "wb"); fwrite(p, 1, (uint)uncomp_size, fpTargetFile); fclose(fpTargetFile); ESP_LOGD(TAG, "Successfully extracted file \"%s\", size %u", archive_filename, (uint)uncomp_size); - // ESP_LOGD(TAG, "File data: \"%s\"", (const char*)p); + // ESP_LOGD(TAG, "File data: \"%s\"", (const char*)p); // We're done. mz_free(p); @@ -1164,92 +1225,85 @@ void unzip(std::string _in_zip_file, std::string _target_directory){ ESP_LOGD(TAG, "Success."); } -void register_server_file_uri(httpd_handle_t server, const char *base_path) +void file_server_register_uri(httpd_handle_t server, const char *base_path) { static struct file_server_data *server_data = NULL; /* Validate file storage base path */ - if (!base_path) { -// if (!base_path || strcmp(base_path, "/spiffs") != 0) { + if (!base_path) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File server base_path not set"); -// return ESP_ERR_INVALID_ARG; } - if (server_data) { + if (server_data) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File server already started"); -// return ESP_ERR_INVALID_STATE; } /* Allocate memory for server data */ - server_data = (file_server_data *) calloc(1, sizeof(struct file_server_data)); - if (!server_data) { + server_data = (file_server_data *)calloc(1, sizeof(struct file_server_data)); + if (!server_data) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate memory for server data"); -// return ESP_ERR_NO_MEM; } - strlcpy(server_data->base_path, base_path, - sizeof(server_data->base_path)); + strlcpy(server_data->base_path, base_path, sizeof(server_data->base_path)); /* URI handler for getting uploaded files */ -// char zw[sizeof(serverprefix)+1]; -// strcpy(zw, serverprefix); -// zw[strlen(serverprefix)] = '*'; -// zw[strlen(serverprefix)+1] = '\0'; -// ESP_LOGD(TAG, "zw: %s", zw); httpd_uri_t file_download = { - .uri = "/fileserver*", // Match all URIs of type /path/to/file - .method = HTTP_GET, + .uri = "/fileserver*", // Match all URIs of type /path/to/file + .method = HTTP_GET, .handler = APPLY_BASIC_AUTH_FILTER(download_get_handler), - .user_ctx = server_data // Pass server data as context + .user_ctx = server_data // Pass server data as context }; httpd_register_uri_handler(server, &file_download); httpd_uri_t file_datafileact = { - .uri = "/datafileact", // Match all URIs of type /path/to/file - .method = HTTP_GET, + .uri = "/datafileact", // Match all URIs of type /path/to/file + .method = HTTP_GET, .handler = APPLY_BASIC_AUTH_FILTER(datafileact_get_full_handler), - .user_ctx = server_data // Pass server data as context + .user_ctx = server_data // Pass server data as context }; httpd_register_uri_handler(server, &file_datafileact); httpd_uri_t file_datafile_last_part_handle = { - .uri = "/data", // Match all URIs of type /path/to/file - .method = HTTP_GET, + .uri = "/data", // Match all URIs of type /path/to/file + .method = HTTP_GET, .handler = APPLY_BASIC_AUTH_FILTER(datafileact_get_last_part_handler), - .user_ctx = server_data // Pass server data as context + .user_ctx = server_data // Pass server data as context }; httpd_register_uri_handler(server, &file_datafile_last_part_handle); httpd_uri_t file_logfileact = { - .uri = "/logfileact", // Match all URIs of type /path/to/file - .method = HTTP_GET, + .uri = "/logfileact", // Match all URIs of type /path/to/file + .method = HTTP_GET, .handler = APPLY_BASIC_AUTH_FILTER(logfileact_get_full_handler), - .user_ctx = server_data // Pass server data as context + .user_ctx = server_data // Pass server data as context }; httpd_register_uri_handler(server, &file_logfileact); httpd_uri_t file_logfile_last_part_handle = { - .uri = "/log", // Match all URIs of type /path/to/file - .method = HTTP_GET, + .uri = "/log", // Match all URIs of type /path/to/file + .method = HTTP_GET, .handler = APPLY_BASIC_AUTH_FILTER(logfileact_get_last_part_handler), - .user_ctx = server_data // Pass server data as context + .user_ctx = server_data // Pass server data as context }; httpd_register_uri_handler(server, &file_logfile_last_part_handle); /* URI handler for uploading files to server */ httpd_uri_t file_upload = { - .uri = "/upload/*", // Match all URIs of type /upload/path/to/file - .method = HTTP_POST, + .uri = "/upload/*", // Match all URIs of type /upload/path/to/file + .method = HTTP_POST, .handler = APPLY_BASIC_AUTH_FILTER(upload_post_handler), - .user_ctx = server_data // Pass server data as context + .user_ctx = server_data // Pass server data as context }; httpd_register_uri_handler(server, &file_upload); /* URI handler for deleting files from server */ httpd_uri_t file_delete = { - .uri = "/delete/*", // Match all URIs of type /delete/path/to/file - .method = HTTP_POST, + .uri = "/delete/*", // Match all URIs of type /delete/path/to/file + .method = HTTP_POST, .handler = APPLY_BASIC_AUTH_FILTER(delete_post_handler), - .user_ctx = server_data // Pass server data as context + .user_ctx = server_data // Pass server data as context }; httpd_register_uri_handler(server, &file_delete); } diff --git a/code/components/jomjol_fileserver_ota/server_file.h b/code/components/jomjol_fileserver_ota/server_file.h index 3520ae5d..7e789591 100644 --- a/code/components/jomjol_fileserver_ota/server_file.h +++ b/code/components/jomjol_fileserver_ota/server_file.h @@ -3,19 +3,20 @@ #ifndef SERVERFILE_H #define SERVERFILE_H +#include "defines.h" + #include #include -void register_server_file_uri(httpd_handle_t server, const char *base_path); - void unzip(std::string _in_zip_file, std::string _target_directory); std::string unzip_new(std::string _in_zip_file, std::string _html_tmp, std::string _html_final, std::string _target_bin, std::string _main = "/sdcard/", bool _initial_setup = false); - void delete_all_in_directory(std::string _directory); esp_err_t get_tflite_file_handler(httpd_req_t *req); esp_err_t get_data_file_handler(httpd_req_t *req); esp_err_t get_numbers_file_handler(httpd_req_t *req); -#endif //SERVERFILE_H \ No newline at end of file +void file_server_register_uri(httpd_handle_t server, const char *base_path); + +#endif // SERVERFILE_H diff --git a/code/components/jomjol_fileserver_ota/server_help.cpp b/code/components/jomjol_fileserver_ota/server_help.cpp index ecf838e7..66f12fc6 100644 --- a/code/components/jomjol_fileserver_ota/server_help.cpp +++ b/code/components/jomjol_fileserver_ota/server_help.cpp @@ -1,3 +1,5 @@ +#include "defines.h" + #include "server_help.h" #include @@ -6,27 +8,19 @@ #include #include -#ifdef __cplusplus -extern "C" { -#endif -#include -#ifdef __cplusplus -} -#endif - #include "esp_err.h" #include "esp_log.h" #include "Helper.h" #include "esp_http_server.h" -#include "../../include/defines.h" static const char *TAG = "SERVER HELP"; char scratch[SERVER_HELPER_SCRATCH_BUFSIZE]; -bool endsWith(std::string const &str, std::string const &suffix) +bool endsWith(std::string const &str, std::string const &suffix) { - if (str.length() < suffix.length()) { + if (str.length() < suffix.length()) + { return false; } return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0; @@ -42,7 +36,8 @@ esp_err_t send_file(httpd_req_t *req, std::string filename) std::string _filename_temp = std::string(filename) + ".gz"; // Checks whether the file is available as .gz - if (stat(_filename_temp.c_str(), &file_stat) == 0) { + if (stat(_filename_temp.c_str(), &file_stat) == 0) + { filename = _filename_temp; ESP_LOGD(TAG, "new filename: %s", filename.c_str()); @@ -50,9 +45,10 @@ esp_err_t send_file(httpd_req_t *req, std::string filename) } FILE *fd = fopen(filename.c_str(), "r"); - if (!fd) { + if (!fd) + { ESP_LOGE(TAG, "Failed to read file: %s", filename.c_str()); - + /* Respond with 404 Error */ httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, get404()); return ESP_FAIL; @@ -73,44 +69,51 @@ esp_err_t send_file(httpd_req_t *req, std::string filename) endsWith(filename, ".png") || endsWith(filename, ".gif") || // endsWith(filename, ".zip") || - endsWith(filename, ".gz")) { - if (filename == "/sdcard/html/setup.html") { + endsWith(filename, ".gz")) + { + if (filename == "/sdcard/html/setup.html") + { httpd_resp_set_hdr(req, "Clear-Site-Data", "\"*\""); set_content_type_from_file(req, filename.c_str()); } - else if (_gz_file_exists) { + else if (_gz_file_exists) + { httpd_resp_set_hdr(req, "Cache-Control", "max-age=43200"); httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); set_content_type_from_file(req, _filename_old.c_str()); } - else { + else + { httpd_resp_set_hdr(req, "Cache-Control", "max-age=43200"); set_content_type_from_file(req, filename.c_str()); } } - else { + else + { set_content_type_from_file(req, filename.c_str()); } /* Retrieve the pointer to scratch buffer for temporary storage */ char *chunk = scratch; size_t chunksize; - - do { + + do + { /* Read file in chunks into the scratch buffer */ chunksize = fread(chunk, 1, SERVER_HELPER_SCRATCH_BUFSIZE, fd); /* Send the buffer contents as HTTP response chunk */ - if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) { + if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) + { fclose(fd); ESP_LOGE(TAG, "File sending failed!"); - + /* Abort sending file */ httpd_resp_sendstr_chunk(req, NULL); - + /* Respond with 500 Internal Server Error */ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file"); - + return ESP_FAIL; } @@ -120,27 +123,30 @@ esp_err_t send_file(httpd_req_t *req, std::string filename) /* Close file after sending complete */ fclose(fd); ESP_LOGD(TAG, "File sending complete"); - - return ESP_OK; + + return ESP_OK; } /* Copies the full path into destination buffer and returns * pointer to path (skipping the preceding base path) */ -const char* get_path_from_uri(char *dest, const char *base_path, const char *uri, size_t destsize) +const char *get_path_from_uri(char *dest, const char *base_path, const char *uri, size_t destsize) { const size_t base_pathlen = strlen(base_path); size_t pathlen = strlen(uri); const char *quest = strchr(uri, '?'); - if (quest) { + if (quest) + { pathlen = MIN(pathlen, quest - uri); } const char *hash = strchr(uri, '#'); - if (hash) { + if (hash) + { pathlen = MIN(pathlen, hash - uri); } - if (base_pathlen + pathlen + 1 > destsize) { + if (base_pathlen + pathlen + 1 > destsize) + { /* Full path string won't fit into destination buffer */ return NULL; } @@ -156,43 +162,56 @@ const char* get_path_from_uri(char *dest, const char *base_path, const char *uri /* Set HTTP response content type according to file extension */ esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename) { - if (IS_FILE_EXT(filename, ".pdf")) { + if (IS_FILE_EXT(filename, ".pdf")) + { return httpd_resp_set_type(req, "application/x-pdf"); } - else if (IS_FILE_EXT(filename, ".htm")) { + else if (IS_FILE_EXT(filename, ".htm")) + { return httpd_resp_set_type(req, "text/html"); } - else if (IS_FILE_EXT(filename, ".html")) { + else if (IS_FILE_EXT(filename, ".html")) + { return httpd_resp_set_type(req, "text/html"); } - else if (IS_FILE_EXT(filename, ".jpeg")) { + else if (IS_FILE_EXT(filename, ".jpeg")) + { return httpd_resp_set_type(req, "image/jpeg"); } - else if (IS_FILE_EXT(filename, ".jpg")) { + else if (IS_FILE_EXT(filename, ".jpg")) + { return httpd_resp_set_type(req, "image/jpeg"); } - else if (IS_FILE_EXT(filename, ".gif")) { + else if (IS_FILE_EXT(filename, ".gif")) + { return httpd_resp_set_type(req, "image/gif"); } - else if (IS_FILE_EXT(filename, ".png")) { + else if (IS_FILE_EXT(filename, ".png")) + { return httpd_resp_set_type(req, "image/png"); } - else if (IS_FILE_EXT(filename, ".ico")) { + else if (IS_FILE_EXT(filename, ".ico")) + { return httpd_resp_set_type(req, "image/x-icon"); } - else if (IS_FILE_EXT(filename, ".js")) { + else if (IS_FILE_EXT(filename, ".js")) + { return httpd_resp_set_type(req, "application/javascript"); } - else if (IS_FILE_EXT(filename, ".css")) { + else if (IS_FILE_EXT(filename, ".css")) + { return httpd_resp_set_type(req, "text/css"); } - else if (IS_FILE_EXT(filename, ".xml")) { + else if (IS_FILE_EXT(filename, ".xml")) + { return httpd_resp_set_type(req, "text/xml"); } - else if (IS_FILE_EXT(filename, ".zip")) { + else if (IS_FILE_EXT(filename, ".zip")) + { return httpd_resp_set_type(req, "application/x-zip"); } - else if (IS_FILE_EXT(filename, ".gz")) { + else if (IS_FILE_EXT(filename, ".gz")) + { return httpd_resp_set_type(req, "application/x-gzip"); } diff --git a/code/components/jomjol_fileserver_ota/server_help.h b/code/components/jomjol_fileserver_ota/server_help.h index e9e2c19a..9ce7ff92 100644 --- a/code/components/jomjol_fileserver_ota/server_help.h +++ b/code/components/jomjol_fileserver_ota/server_help.h @@ -3,15 +3,13 @@ #ifndef SERVERHELP_H #define SERVERHELP_H +#include "defines.h" + #include -//#include #include "esp_http_server.h" - const char* get_path_from_uri(char *dest, const char *base_path, const char *uri, size_t destsize); - esp_err_t send_file(httpd_req_t *req, std::string filename); - esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename); -#endif //SERVERHELP_H \ No newline at end of file +#endif //SERVERHELP_H diff --git a/code/components/jomjol_fileserver_ota/server_ota.cpp b/code/components/jomjol_fileserver_ota/server_ota.cpp index 1ec21936..03751bdb 100644 --- a/code/components/jomjol_fileserver_ota/server_ota.cpp +++ b/code/components/jomjol_fileserver_ota/server_ota.cpp @@ -1,80 +1,74 @@ +#include "defines.h" + #include "server_ota.h" #include -#include "string.h" +#include /* TODO Rethink the usage of the int watchdog. It is no longer to be used, see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/release-5.x/5.0/system.html?highlight=esp_int_wdt */ -#include "esp_private/esp_int_wdt.h" +#include #include - -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_system.h" -#include "esp_log.h" +#include +#include +#include +#include #include -#include "esp_http_client.h" -#include "esp_flash_partitions.h" -#include "esp_partition.h" +#include +#include +#include #include -#include "esp_app_format.h" -#include "nvs_flash.h" -#include "driver/gpio.h" -// #include "protocol_examples_common.h" -#include "errno.h" +#include +#include +#include +#include #include #include "MainFlowControl.h" #include "server_file.h" #include "server_GPIO.h" -#ifdef ENABLE_MQTT - #include "interface_mqtt.h" -#endif //ENABLE_MQTT +#include "interface_mqtt.h" #include "ClassControllCamera.h" -#include "connect_wlan.h" - +#include "connect_wifi_sta.h" #include "ClassLogFile.h" #include "Helper.h" #include "statusled.h" #include "basic_auth.h" -#include "../../include/defines.h" /*an ota data write buffer ready to write to the flash*/ -static char ota_write_data[SERVER_OTA_SCRATCH_BUFSIZE + 1] = { 0 }; +static char ota_write_data[SERVER_OTA_SCRATCH_BUFSIZE + 1] = {0}; static const char *TAG = "OTA"; esp_err_t handler_reboot(httpd_req_t *req); static bool ota_update_task(std::string fn); -std::string _file_name_update; +std::string file_name_update; bool initial_setup = false; - static void infinite_loop(void) { int i = 0; LogFile.WriteToFile(ESP_LOG_INFO, TAG, "When a new firmware is available on the server, press the reset button to download it"); - while(1) { + while (1) + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Waiting for a new firmware... (" + to_string(++i) + ")"); vTaskDelay(1000 / portTICK_PERIOD_MS); } } - void task_do_Update_ZIP(void *pvParameter) { - StatusLED(AP_OR_OTA, 1, true); // Signaling an OTA update - - std::string filetype = toUpper(getFileType(_file_name_update)); + set_status_led(AP_OR_OTA, 1, true); // Signaling an OTA update - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "File: " + _file_name_update + " Filetype: " + filetype); + std::string filetype = to_upper(get_file_type(file_name_update)); + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "File: " + file_name_update + " Filetype: " + filetype); if (filetype == "ZIP") { @@ -86,21 +80,23 @@ void task_do_Update_ZIP(void *pvParameter) outbin = "/sdcard/firmware"; /* Remove the old and tmp html folder in case they still exist */ - removeFolder(outHtmlTmp.c_str(), TAG); - removeFolder(outHtmlOld.c_str(), TAG); + remove_folder(outHtmlTmp.c_str(), TAG); + remove_folder(outHtmlOld.c_str(), TAG); /* Extract the ZIP file. The content of the html folder gets extracted to the temporar folder html-temp. */ - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Extracting ZIP file " + _file_name_update + "..."); - retfirmware = unzip_new(_file_name_update, outHtmlTmp+"/", outHtml+"/", outbin+"/", "/sdcard/", initial_setup); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Files unzipped."); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Extracting ZIP file " + file_name_update + "..."); + retfirmware = unzip_new(file_name_update, outHtmlTmp + "/", outHtml + "/", outbin + "/", "/sdcard/", initial_setup); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Files unzipped."); /* ZIP file got extracted, replace the old html folder with the new one */ LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Renaming folder " + outHtml + " to " + outHtmlOld + "..."); - RenameFolder(outHtml, outHtmlOld); + rename_folder(outHtml, outHtmlOld); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Renaming folder " + outHtmlTmp + " to " + outHtml + "..."); - RenameFolder(outHtmlTmp, outHtml); + rename_folder(outHtmlTmp, outHtml); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Deleting folder " + outHtmlOld + "..."); - removeFolder(outHtmlOld.c_str(), TAG); + remove_folder(outHtmlOld.c_str(), TAG); if (retfirmware.length() > 0) { @@ -110,58 +106,61 @@ void task_do_Update_ZIP(void *pvParameter) LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update"); doRebootOTA(); - } else if (filetype == "BIN") + } + else if (filetype == "BIN") { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Do firmware update - file: " + _file_name_update); - ota_update_task(_file_name_update); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Do firmware update - file: " + file_name_update); + ota_update_task(file_name_update); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update"); doRebootOTA(); } else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Only ZIP-Files support for update during startup!"); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Only ZIP-Files support for update during startup!"); } } - -void CheckUpdate() +bool CheckOTAUpdateAvailability(void) { - FILE *pfile; - if ((pfile = fopen("/sdcard/update.txt", "r")) == NULL) + FILE *pFile = fopen("/sdcard/update.txt", "r"); + if (pFile == NULL) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "No pending update"); - return; - } + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CheckOTAUpdateAvailability() - No pending update"); + return false; + } - char zw[1024] = ""; - fgets(zw, 1024, pfile); - _file_name_update = std::string(zw); - if (fgets(zw, 1024, pfile)) - { - std::string _szw = std::string(zw); - if (_szw == "init") + char data_temp[1024] = ""; + fgets(data_temp, 1024, pFile); + file_name_update = std::string(data_temp); + + if (fgets(data_temp, 1024, pFile)) + { + std::string _data_temp = std::string(data_temp); + if (_data_temp == "init") { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Inital Setup triggered"); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "CheckOTAUpdateAvailability() - Inital Setup triggered"); } - } + } - fclose(pfile); - DeleteFile("/sdcard/update.txt"); // Prevent Boot Loop!!! - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Start update process (" + _file_name_update + ")"); + fclose(pFile); + delete_file("/sdcard/update.txt"); // Prevent Boot Loop!!! + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "CheckOTAUpdateAvailability() - Start update process (" + file_name_update + ")"); - - xTaskCreate(&task_do_Update_ZIP, "task_do_Update_ZIP", configMINIMAL_STACK_SIZE * 35, NULL, tskIDLE_PRIORITY+1, NULL); - while(1) { // wait until reboot within task_do_Update_ZIP + xTaskCreate(&task_do_Update_ZIP, "task_do_Update_ZIP", configMINIMAL_STACK_SIZE * 35, NULL, tskIDLE_PRIORITY + 1, NULL); + while (1) + { + // wait until reboot within task_do_Update_ZIP vTaskDelay(1000 / portTICK_PERIOD_MS); } -} + return true; +} static bool ota_update_task(std::string fn) { esp_err_t err; /* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */ - esp_ota_handle_t update_handle = 0 ; + esp_ota_handle_t update_handle = 0; const esp_partition_t *update_partition = NULL; ESP_LOGI(TAG, "Starting OTA update"); @@ -169,113 +168,122 @@ static bool ota_update_task(std::string fn) const esp_partition_t *configured = esp_ota_get_boot_partition(); const esp_partition_t *running = esp_ota_get_running_partition(); - if (configured != running) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Configured OTA boot partition at offset " + to_string(configured->address) + - ", but running from offset " + to_string(running->address)); + if (configured != running) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Configured OTA boot partition at offset " + to_string(configured->address) + ", but running from offset " + to_string(running->address)); LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "(This can happen if either the OTA boot data or preferred boot image become somehow corrupted.)"); } ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)", running->type, running->subtype, (unsigned int)running->address); - update_partition = esp_ota_get_next_update_partition(NULL); - ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x", - update_partition->subtype, (unsigned int)update_partition->address); -// assert(update_partition != NULL); + ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x", update_partition->subtype, (unsigned int)update_partition->address); int binary_file_length = 0; - // deal with all receive packet + // deal with all receive packet bool image_header_was_checked = false; - int data_read; + int data_read; - FILE* f = fopen(fn.c_str(), "rb"); // previously only "r + FILE *f = fopen(fn.c_str(), "rb"); // previously only "r - if (f == NULL) { // File does not exist + if (f == NULL) + { + // File does not exist return false; } data_read = fread(ota_write_data, 1, SERVER_OTA_SCRATCH_BUFSIZE, f); - while (data_read > 0) { - if (data_read < 0) { + while (data_read > 0) + { + if (data_read < 0) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Error: SSL data read error"); return false; - } else if (data_read > 0) { - if (image_header_was_checked == false) { + } + else if (data_read > 0) + { + if (image_header_was_checked == false) + { esp_app_desc_t new_app_info; - if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) { + if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) + { // check current version with downloading memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t)); ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version); esp_app_desc_t running_app_info; - if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) { + if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) + { ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version); } - const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition(); + const esp_partition_t *last_invalid_app = esp_ota_get_last_invalid_partition(); esp_app_desc_t invalid_app_info; - if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) { + if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) + { ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version); } // check current version with last invalid partition - if (last_invalid_app != NULL) { - if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) { + if (last_invalid_app != NULL) + { + if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "New version is the same as invalid version"); - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Previously, there was an attempt to launch the firmware with " + - string(invalid_app_info.version) + " version, but it failed"); + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Previously, there was an attempt to launch the firmware with " + string(invalid_app_info.version) + " version, but it failed"); LogFile.WriteToFile(ESP_LOG_WARN, TAG, "The firmware has been rolled back to the previous version"); infinite_loop(); } } -/* - if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Current running version is the same as a new. We will not continue the update"); - infinite_loop(); - } -*/ image_header_was_checked = true; err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle); - if (err != ESP_OK) { + if (err != ESP_OK) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_begin failed (" + string(esp_err_to_name(err)) + ")"); return false; } ESP_LOGI(TAG, "esp_ota_begin succeeded"); - } else { + } + else + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "received package is not fit len"); return false; } - } - err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read); - if (err != ESP_OK) { + } + err = esp_ota_write(update_handle, (const void *)ota_write_data, data_read); + if (err != ESP_OK) + { return false; } binary_file_length += data_read; ESP_LOGD(TAG, "Written image length %d", binary_file_length); - } else if (data_read == 0) { - // - // * As esp_http_client_read never returns negative error code, we rely on - // * `errno` to check for underlying transport connectivity closure if any - // - if (errno == ECONNRESET || errno == ENOTCONN) { + } + else if (data_read == 0) + { + // * As esp_http_client_read never returns negative error code, we rely on + // * `errno` to check for underlying transport connectivity closure if any + if (errno == ECONNRESET || errno == ENOTCONN) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection closed, errno = " + to_string(errno)); break; } } data_read = fread(ota_write_data, 1, SERVER_OTA_SCRATCH_BUFSIZE, f); } - fclose(f); + fclose(f); ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length); err = esp_ota_end(update_handle); - if (err != ESP_OK) { - if (err == ESP_ERR_OTA_VALIDATE_FAILED) { + if (err != ESP_OK) + { + if (err == ESP_ERR_OTA_VALIDATE_FAILED) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Image validation failed, image is corrupted"); } LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_end failed (" + string(esp_err_to_name(err)) + ")!"); @@ -283,52 +291,48 @@ static bool ota_update_task(std::string fn) } err = esp_ota_set_boot_partition(update_partition); - if (err != ESP_OK) { + if (err != ESP_OK) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_set_boot_partition failed (" + string(esp_err_to_name(err)) + ")!"); - } -// ESP_LOGI(TAG, "Prepare to restart system!"); -// esp_restart(); - return true ; + return true; } - -static void print_sha256 (const uint8_t *image_hash, const char *label) +static void print_sha256(const uint8_t *image_hash, const char *label) { char hash_print[HASH_LEN * 2 + 1]; hash_print[HASH_LEN * 2] = 0; - for (int i = 0; i < HASH_LEN; ++i) { + for (int i = 0; i < HASH_LEN; ++i) + { sprintf(&hash_print[i * 2], "%02x", image_hash[i]); } ESP_LOGI(TAG, "%s: %s", label, hash_print); } - static bool diagnostic(void) { return true; } - -void CheckOTAUpdate(void) +void CheckOTAUpdateStatus(void) { - ESP_LOGI(TAG, "Start CheckOTAUpdateCheck..."); + ESP_LOGI(TAG, "Start CheckOTAUpdateStatus..."); - uint8_t sha_256[HASH_LEN] = { 0 }; + uint8_t sha_256[HASH_LEN] = {0}; esp_partition_t partition; // get sha256 digest for the partition table - partition.address = ESP_PARTITION_TABLE_OFFSET; - partition.size = ESP_PARTITION_TABLE_MAX_LEN; - partition.type = ESP_PARTITION_TYPE_DATA; + partition.address = ESP_PARTITION_TABLE_OFFSET; + partition.size = ESP_PARTITION_TABLE_MAX_LEN; + partition.type = ESP_PARTITION_TYPE_DATA; esp_partition_get_sha256(&partition, sha_256); print_sha256(sha_256, "SHA-256 for the partition table: "); // get sha256 digest for bootloader - partition.address = ESP_BOOTLOADER_OFFSET; - partition.size = ESP_PARTITION_TABLE_OFFSET; - partition.type = ESP_PARTITION_TYPE_APP; + partition.address = ESP_BOOTLOADER_OFFSET; + partition.size = ESP_PARTITION_TABLE_OFFSET; + partition.type = ESP_PARTITION_TYPE_APP; esp_partition_get_sha256(&partition, sha_256); print_sha256(sha_256, "SHA-256 for bootloader: "); @@ -341,40 +345,50 @@ void CheckOTAUpdate(void) esp_err_t res_stat_partition = esp_ota_get_state_partition(running, &ota_state); switch (res_stat_partition) { - case ESP_OK: - ESP_LOGD(TAG, "CheckOTAUpdate Partition: ESP_OK"); - if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) { - if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) { - // run diagnostic function ... - bool diagnostic_is_ok = diagnostic(); - if (diagnostic_is_ok) { - ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution..."); - esp_ota_mark_app_valid_cancel_rollback(); - } else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Diagnostics failed! Start rollback to the previous version..."); - esp_ota_mark_app_invalid_rollback_and_reboot(); - } + case ESP_OK: + ESP_LOGD(TAG, "CheckOTAUpdateStatus Partition: ESP_OK"); + if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) + { + if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) + { + // run diagnostic function ... + bool diagnostic_is_ok = diagnostic(); + if (diagnostic_is_ok) + { + ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution..."); + esp_ota_mark_app_valid_cancel_rollback(); } - } - break; - case ESP_ERR_INVALID_ARG: - ESP_LOGD(TAG, "CheckOTAUpdate Partition: ESP_ERR_INVALID_ARG"); - break; - case ESP_ERR_NOT_SUPPORTED: - ESP_LOGD(TAG, "CheckOTAUpdate Partition: ESP_ERR_NOT_SUPPORTED"); - break; - case ESP_ERR_NOT_FOUND: - ESP_LOGD(TAG, "CheckOTAUpdate Partition: ESP_ERR_NOT_FOUND"); - break; + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Diagnostics failed! Start rollback to the previous version..."); + esp_ota_mark_app_invalid_rollback_and_reboot(); + } + } + } + break; + case ESP_ERR_INVALID_ARG: + ESP_LOGD(TAG, "CheckOTAUpdateStatus Partition: ESP_ERR_INVALID_ARG"); + break; + case ESP_ERR_NOT_SUPPORTED: + ESP_LOGD(TAG, "CheckOTAUpdateStatus Partition: ESP_ERR_NOT_SUPPORTED"); + break; + case ESP_ERR_NOT_FOUND: + ESP_LOGD(TAG, "CheckOTAUpdateStatus Partition: ESP_ERR_NOT_FOUND"); + break; } - if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) { - if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) { + if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) + { + if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) + { // run diagnostic function ... bool diagnostic_is_ok = diagnostic(); - if (diagnostic_is_ok) { + if (diagnostic_is_ok) + { ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution..."); esp_ota_mark_app_valid_cancel_rollback(); - } else { + } + else + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Diagnostics failed! Start rollback to the previous version..."); esp_ota_mark_app_invalid_rollback_and_reboot(); } @@ -382,17 +396,12 @@ void CheckOTAUpdate(void) } } - esp_err_t handler_ota_update(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_ota_update - Start"); -#endif - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_ota_update"); char _query[200]; char _filename[100]; - char _valuechar[30]; + char _valuechar[30]; std::string fn = "/sdcard/firmware/"; bool _file_del = false; std::string _task = ""; @@ -400,7 +409,7 @@ esp_err_t handler_ota_update(httpd_req_t *req) if (httpd_req_get_url_query_str(req, _query, 200) == ESP_OK) { ESP_LOGD(TAG, "Query: %s", _query); - + if (httpd_query_key_value(_query, "task", _valuechar, 30) == ESP_OK) { ESP_LOGD(TAG, "task is found: %s", _valuechar); @@ -418,7 +427,6 @@ esp_err_t handler_ota_update(httpd_req_t *req) _file_del = true; ESP_LOGD(TAG, "Delete Default File: %s", fn.c_str()); } - } if (_task.compare("emptyfirmwaredir") == 0) @@ -428,11 +436,11 @@ esp_err_t handler_ota_update(httpd_req_t *req) std::string zw = "firmware directory deleted - v2\n"; ESP_LOGD(TAG, "%s", zw.c_str()); printf("Ausgabe: %s\n", zw.c_str()); - + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - httpd_resp_send(req, zw.c_str(), strlen(zw.c_str())); + httpd_resp_send(req, zw.c_str(), strlen(zw.c_str())); /* Respond with an empty chunk to signal HTTP response completion */ - httpd_resp_send_chunk(req, NULL, 0); + httpd_resp_send_chunk(req, NULL, 0); ESP_LOGD(TAG, "Done empty directory /firmware"); return ESP_OK; @@ -440,32 +448,31 @@ esp_err_t handler_ota_update(httpd_req_t *req) if (_task.compare("update") == 0) { - std::string filetype = toUpper(getFileType(fn)); + std::string filetype = to_upper(get_file_type(fn)); if (filetype.length() == 0) { std::string zw = "Update failed - no file specified (zip, bin, tfl, tlite)"; httpd_resp_sendstr_chunk(req, zw.c_str()); - httpd_resp_sendstr_chunk(req, NULL); - return ESP_OK; + httpd_resp_sendstr_chunk(req, NULL); + return ESP_OK; } if ((filetype == "TFLITE") || (filetype == "TFL")) { - std::string out = "/sdcard/config/" + getFileFullFileName(fn); - DeleteFile(out); - CopyFile(fn, out); - DeleteFile(fn); + std::string out = "/sdcard/config/" + get_file_full_filename(fn); + delete_file(out); + copy_file(fn, out); + delete_file(fn); - const char* resp_str = "Neural Network File copied."; + const char *resp_str = "Neural Network File copied."; httpd_resp_sendstr_chunk(req, resp_str); - httpd_resp_sendstr_chunk(req, NULL); + httpd_resp_sendstr_chunk(req, NULL); return ESP_OK; } - if ((filetype == "ZIP") || (filetype == "BIN")) { - FILE *pfile; + FILE *pfile; LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Update for reboot"); pfile = fopen("/sdcard/update.txt", "w"); fwrite(fn.c_str(), fn.length(), 1, pfile); @@ -473,46 +480,17 @@ esp_err_t handler_ota_update(httpd_req_t *req) std::string zw = "reboot\n"; httpd_resp_sendstr_chunk(req, zw.c_str()); - httpd_resp_sendstr_chunk(req, NULL); + httpd_resp_sendstr_chunk(req, NULL); ESP_LOGD(TAG, "Send reboot"); - return ESP_OK; - - } - -/* - if (filetype == "BIN") - { - const char* resp_str; - - DeleteMainFlowTask(); - gpio_handler_deinit(); - if (ota_update_task(fn)) - { - std::string zw = "reboot\n"; - httpd_resp_sendstr_chunk(req, zw.c_str()); - httpd_resp_sendstr_chunk(req, NULL); - ESP_LOGD(TAG, "Send reboot"); - return ESP_OK; - } - - resp_str = "Error during Firmware Update!!!\nPlease check output of console."; - httpd_resp_send(req, resp_str, strlen(resp_str)); - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_ota_update - Done"); - #endif - return ESP_OK; } -*/ std::string zw = "Update failed - no valid file specified (zip, bin, tfl, tlite)!"; httpd_resp_sendstr_chunk(req, zw.c_str()); - httpd_resp_sendstr_chunk(req, NULL); - return ESP_OK; + httpd_resp_sendstr_chunk(req, NULL); + return ESP_OK; } - if (_task.compare("unziphtml") == 0) { ESP_LOGD(TAG, "Task unziphtml"); @@ -523,11 +501,11 @@ esp_err_t handler_ota_update(httpd_req_t *req) delete_all_in_directory(out); - unzip(in, out+"/"); + unzip(in, out + "/"); zw = "Web Interface Update Successfull!\nNo reboot necessary"; httpd_resp_send(req, zw.c_str(), strlen(zw.c_str())); - httpd_resp_sendstr_chunk(req, NULL); - return ESP_OK; + httpd_resp_sendstr_chunk(req, NULL); + return ESP_OK; } if (_file_del) @@ -536,7 +514,8 @@ esp_err_t handler_ota_update(httpd_req_t *req) struct stat file_stat; int _result = stat(fn.c_str(), &file_stat); ESP_LOGD(TAG, "Ergebnis %d\n", _result); - if (_result == 0) { + if (_result == 0) + { ESP_LOGD(TAG, "Deleting file: %s", fn.c_str()); /* Delete file */ unlink(fn.c_str()); @@ -555,164 +534,149 @@ esp_err_t handler_ota_update(httpd_req_t *req) string zw = "ota without parameter - should not be the case!"; httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - httpd_resp_send(req, zw.c_str(), strlen(zw.c_str())); - httpd_resp_send_chunk(req, NULL, 0); + httpd_resp_send(req, zw.c_str(), strlen(zw.c_str())); + httpd_resp_send_chunk(req, NULL, 0); LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ota without parameter - should not be the case!"); -/* - const char* resp_str; - - DeleteMainFlowTask(); - gpio_handler_deinit(); - if (ota_update_task(fn)) - { - resp_str = "Firmware Update Successfull! You can restart now."; - } - else - { - resp_str = "Error during Firmware Update!!! Please check console output."; - } - - httpd_resp_send(req, resp_str, strlen(resp_str)); -*/ - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_ota_update - Done"); - #endif - return ESP_OK; } - -void hard_restart() +void hard_restart(void) { - esp_task_wdt_config_t twdt_config = { - .timeout_ms = 1, - .idle_core_mask = (1 << portNUM_PROCESSORS) - 1, // Bitmask of all cores - .trigger_panic = true, - }; - ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config)); + esp_task_wdt_config_t twdt_config = { + .timeout_ms = 1, + .idle_core_mask = (1 << portNUM_PROCESSORS) - 1, // Bitmask of all cores + .trigger_panic = true, + }; + ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config)); - esp_task_wdt_add(NULL); - while(true); + esp_task_wdt_add(NULL); + while (true) + ; } - void task_reboot(void *DeleteMainFlow) { // write a reboot, to identify a reboot by purpouse - FILE* pfile = fopen("/sdcard/reboot.txt", "w"); - std::string _s_zw= "reboot"; - fwrite(_s_zw.c_str(), strlen(_s_zw.c_str()), 1, pfile); + FILE *pfile = fopen("/sdcard/reboot.txt", "w"); + std::string data_temp = "reboot"; + fwrite(data_temp.c_str(), strlen(data_temp.c_str()), 1, pfile); fclose(pfile); vTaskDelay(3000 / portTICK_PERIOD_MS); - if ((bool)DeleteMainFlow) { - DeleteMainFlowTask(); // Kill autoflow task if executed in extra task, if not don't kill parent task + if ((bool)DeleteMainFlow) + { + DeleteMainFlowTask(); // Kill autoflow task if executed in extra task, if not don't kill parent task } - Camera.LightOnOff(false); - StatusLEDOff(); + Camera.set_camera_deep_sleep(false); + esp_camera_return_all(); + Camera.set_flash_light_on_off(false); + set_status_led_off(); /* Stop service tasks */ - #ifdef ENABLE_MQTT - MQTTdestroy_client(true); - #endif //ENABLE_MQTT + MQTTdestroy_client(true); + gpio_handler_destroy(); - esp_camera_deinit(); - WIFIDestroy(); + wifi_deinit_sta(); vTaskDelay(3000 / portTICK_PERIOD_MS); - esp_restart(); // Reset type: CPU reset (Reset both CPUs) + esp_restart(); // Reset type: CPU reset (Reset both CPUs) vTaskDelay(5000 / portTICK_PERIOD_MS); - hard_restart(); // Reset type: System reset (Triggered by watchdog), if esp_restart stalls (WDT needs to be activated) + hard_restart(); // Reset type: System reset (Triggered by watchdog), if esp_restart stalls (WDT needs to be activated) LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Reboot failed!"); - vTaskDelete(NULL); //Delete this task if it comes to this point + vTaskDelete(NULL); // Delete this task if it comes to this point } - -void doReboot() +void doReboot(void) { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Reboot triggered by Software (5s)"); - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reboot in 5sec"); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "doReboot() - Reboot triggered by Software (5s)"); + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "doReboot() - Reboot in 5sec"); - BaseType_t xReturned = xTaskCreate(&task_reboot, "task_reboot", configMINIMAL_STACK_SIZE * 4, (void*) true, 10, NULL); - if( xReturned != pdPASS ) + while (*flowctrl.getActStatus() == std::string("Take Image")) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + + BaseType_t xReturned = xTaskCreate(&task_reboot, "task_reboot", configMINIMAL_STACK_SIZE * 4, (void *)true, 10, NULL); + if (xReturned != pdPASS) { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "task_reboot not created -> force reboot without killing flow"); - task_reboot((void*) false); + task_reboot((void *)false); } vTaskDelay(10000 / portTICK_PERIOD_MS); // Prevent serving web client fetch response until system is shuting down } - -void doRebootOTA() +void doRebootOTA(void) { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reboot in 5sec"); + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "doRebootOTA() - Reboot in 5sec"); - Camera.LightOnOff(false); - StatusLEDOff(); - esp_camera_deinit(); + while (*flowctrl.getActStatus() == std::string("Take Image")) + { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + + DeleteMainFlowTask(); // Kill autoflow task if executed in extra task, if not don't kill parent task + + Camera.set_camera_deep_sleep(false); + esp_camera_return_all(); + Camera.set_flash_light_on_off(false); + set_status_led_off(); + + /* Stop service tasks */ + MQTTdestroy_client(true); + + gpio_handler_destroy(); + wifi_deinit_sta(); vTaskDelay(5000 / portTICK_PERIOD_MS); - esp_restart(); // Reset type: CPU reset (Reset both CPUs) + esp_restart(); // Reset type: CPU reset (Reset both CPUs) vTaskDelay(5000 / portTICK_PERIOD_MS); - hard_restart(); // Reset type: System reset (Triggered by watchdog), if esp_restart stalls (WDT needs to be activated) + hard_restart(); // Reset type: System reset (Triggered by watchdog), if esp_restart stalls (WDT needs to be activated) } - esp_err_t handler_reboot(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_reboot - Start"); - #endif - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_reboot"); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "!!! System will restart within 5 sec!!!"); - std::string response = + std::string response = "

" - "" - ""; + "function m(h) {" + "document.getElementById('t').innerHTML=h;" + "setInterval(function (){h +='.'; document.getElementById('t').innerHTML=h;" + "fetch('reboot_page.html',{mode: 'no-cors'}).then(r=>{parent.location.href=('index.html');})}, 1000);" + "}

" + "" + ""; httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); httpd_resp_send(req, response.c_str(), strlen(response.c_str())); - - doReboot(); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_reboot - Done"); - #endif + doReboot(); return ESP_OK; } - -void register_server_ota_sdcard_uri(httpd_handle_t server) +void ota_register_uri(httpd_handle_t server) { ESP_LOGI(TAG, "Registering URI handlers"); - - httpd_uri_t camuri = { }; - camuri.method = HTTP_GET; - camuri.uri = "/ota"; + + httpd_uri_t camuri = {}; + camuri.method = HTTP_GET; + camuri.uri = "/ota"; camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_ota_update); - camuri.user_ctx = (void*) "Do OTA"; + camuri.user_ctx = (void *)"Do OTA"; httpd_register_uri_handler(server, &camuri); - camuri.method = HTTP_GET; - camuri.uri = "/reboot"; + camuri.method = HTTP_GET; + camuri.uri = "/reboot"; camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_reboot); - camuri.user_ctx = (void*) "Reboot"; + camuri.user_ctx = (void *)"Reboot"; httpd_register_uri_handler(server, &camuri); - } diff --git a/code/components/jomjol_fileserver_ota/server_ota.h b/code/components/jomjol_fileserver_ota/server_ota.h index 5a503321..a229e716 100644 --- a/code/components/jomjol_fileserver_ota/server_ota.h +++ b/code/components/jomjol_fileserver_ota/server_ota.h @@ -1,20 +1,21 @@ #pragma once -#ifndef SERVEROTA_H -#define SERVEROTA_H +#ifndef SERVER_OTA_H +#define SERVER_OTA_H -#include - -#include +#include "defines.h" #include +#include +#include +void CheckOTAUpdateStatus(void); +bool CheckOTAUpdateAvailability(void); -void register_server_ota_sdcard_uri(httpd_handle_t server); -void CheckOTAUpdate(); -void doReboot(); -void doRebootOTA(); -void hard_restart(); -void CheckUpdate(); +void doReboot(void); +void doRebootOTA(void); +void hard_restart(void); -#endif //SERVEROTA_H \ No newline at end of file +void ota_register_uri(httpd_handle_t server); + +#endif // SERVEROTA_H diff --git a/code/components/jomjol_flowcontroll/CMakeLists.txt b/code/components/jomjol_flowcontroll/CMakeLists.txt index 2df515fc..9cae28e8 100644 --- a/code/components/jomjol_flowcontroll/CMakeLists.txt +++ b/code/components/jomjol_flowcontroll/CMakeLists.txt @@ -1,7 +1,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." - REQUIRES esp_timer esp_wifi jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_webhook jomjol_fileserver_ota jomjol_image_proc jomjol_wlan openmetrics) + INCLUDE_DIRS "." "../../include" + REQUIRES esp_timer esp_wifi jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_webhook jomjol_fileserver_ota jomjol_image_proc jomjol_network openmetrics) diff --git a/code/components/jomjol_flowcontroll/ClassFlow.cpp b/code/components/jomjol_flowcontroll/ClassFlow.cpp index 858c9524..2e697b9e 100644 --- a/code/components/jomjol_flowcontroll/ClassFlow.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlow.cpp @@ -1,114 +1,192 @@ +#include "defines.h" + #include "ClassFlow.h" #include #include #include #include #include "esp_log.h" -#include "../../include/defines.h" static const char *TAG = "CLASS"; - void ClassFlow::SetInitialParameter(void) { ListFlowControll = NULL; - previousElement = NULL; + previousElement = NULL; + disabled = false; } -bool ClassFlow::isNewParagraph(string input) -{ - if ((input[0] == '[') || ((input[0] == ';') && (input[1] == '['))) - { - return true; - } - return false; -} - -bool ClassFlow::GetNextParagraph(FILE* pfile, string& aktparamgraph) -{ - while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph)); - - if (isNewParagraph(aktparamgraph)) - return true; - return false; -} - - ClassFlow::ClassFlow(void) { SetInitialParameter(); } -ClassFlow::ClassFlow(std::vector * lfc) +ClassFlow::ClassFlow(std::vector *lfc) { - SetInitialParameter(); + SetInitialParameter(); ListFlowControll = lfc; } -ClassFlow::ClassFlow(std::vector * lfc, ClassFlow *_prev) +ClassFlow::ClassFlow(std::vector *lfc, ClassFlow *_prev) { - SetInitialParameter(); + SetInitialParameter(); ListFlowControll = lfc; previousElement = _prev; -} +} -bool ClassFlow::ReadParameter(FILE* pfile, string &aktparamgraph) +bool ClassFlow::ReadParameter(FILE *pFile, std::string &aktparamgraph) { return false; } -bool ClassFlow::doFlow(string time) +bool ClassFlow::doFlow(std::string time) { return false; } -string ClassFlow::getHTMLSingleStep(string host){ +std::string ClassFlow::getHTMLSingleStep(std::string host) +{ return ""; } std::string ClassFlow::GetParameterName(std::string _input) { - string _param; - int _pospunkt = _input.find_first_of("."); - if (_pospunkt > -1) - { - _param = _input.substr(_pospunkt+1, _input.length() - _pospunkt - 1); - } - else - { - _param = _input; - } -// ESP_LOGD(TAG, "Parameter: %s, Pospunkt: %d", _param.c_str(), _pospunkt); + std::string _param; + int _pospunkt = _input.find_first_of("."); + if (_pospunkt > -1) + { + _param = _input.substr(_pospunkt + 1, _input.length() - _pospunkt - 1); + } + else + { + _param = _input; + } + return _param; } - -bool ClassFlow::getNextLine(FILE* pfile, string *rt) +bool ClassFlow::isNewParagraph(std::string input) { - char zw[1024]; - if (pfile == NULL) + if ((input[0] == '[') || ((input[0] == ';') && (input[1] == '['))) + { + return true; + } + + return false; +} + +bool ClassFlow::GetNextParagraph(FILE *pFile, std::string &aktparamgraph) +{ + while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph)); + + if (isNewParagraph(aktparamgraph)) + { + return true; + } + + return false; +} + +/* +bool ClassFlow::GetNextParagraph(FILE *pFile, std::string &aktparamgraph, bool &disabled, bool &eof) +{ + while (getNextLine_new(pFile, &aktparamgraph, disabled, eof) && !isNewParagraph(aktparamgraph)); + + if (isNewParagraph(aktparamgraph)) + { + return true; + } + + return false; +} +*/ + +bool ClassFlow::getNextLine(FILE *pFile, std::string *rt) +{ + char temp_char[1024]; + if (pFile == NULL) { *rt = ""; return false; } - if (!fgets(zw, 1024, pfile)) + + if (!fgets(temp_char, 1024, pFile)) { *rt = ""; ESP_LOGD(TAG, "END OF FILE"); return false; } - ESP_LOGD(TAG, "%s", zw); - *rt = zw; - *rt = trim(*rt); - while ((zw[0] == ';' || zw[0] == '#' || (rt->size() == 0)) && !(zw[1] == '[')) + + ESP_LOGD(TAG, "%s", temp_char); + *rt = temp_char; + *rt = trim_string_left_right(*rt); + + while ((temp_char[0] == ';' || temp_char[0] == '#' || (rt->size() == 0)) && !(temp_char[1] == '[')) { *rt = ""; - if (!fgets(zw, 1024, pfile)) + if (!fgets(temp_char, 1024, pFile)) + { return false; - ESP_LOGD(TAG, "%s", zw); - *rt = zw; - *rt = trim(*rt); + } + ESP_LOGD(TAG, "%s", temp_char); + *rt = temp_char; + *rt = trim_string_left_right(*rt); } + return true; } + +/* +bool ClassFlow::getNextLine(FILE *pFile, std::string *rt, bool &disabled, bool &eof) +{ + eof = false; + char zw[1024] = ""; + + if (pFile == NULL) + { + *rt = ""; + return false; + } + + if (fgets(zw, 1024, pFile)) + { + ESP_LOGD(TAG, "%s", zw); + + if ((strlen(zw) == 0) && feof(pFile)) + { + *rt = ""; + eof = true; + return false; + } + } + else + { + *rt = ""; + eof = true; + return false; + } + + *rt = zw; + *rt = trim_string_left_right(*rt); + + while ((zw[0] == ';' || zw[0] == '#' || (rt->size() == 0)) && !(zw[1] == '[')) + { + fgets(zw, 1024, pFile); + ESP_LOGD(TAG, "%s", zw); + + if (feof(pFile)) + { + *rt = ""; + eof = true; + return false; + } + + *rt = zw; + *rt = trim_string_left_right(*rt); + } + + disabled = ((*rt)[0] == ';'); + return true; +} +*/ diff --git a/code/components/jomjol_flowcontroll/ClassFlow.h b/code/components/jomjol_flowcontroll/ClassFlow.h index 9c826c11..778bc49a 100644 --- a/code/components/jomjol_flowcontroll/ClassFlow.h +++ b/code/components/jomjol_flowcontroll/ClassFlow.h @@ -18,36 +18,35 @@ struct HTMLInfo CImageBasis *image = NULL; CImageBasis *image_org = NULL; std::string filename; - std::string filename_org; + std::string filename_org; }; - class ClassFlow { protected: - bool isNewParagraph(string input); - bool GetNextParagraph(FILE* pfile, string& aktparamgraph); - bool getNextLine(FILE* pfile, string* rt); - - std::vector* ListFlowControll; + std::vector *ListFlowControll; ClassFlow *previousElement; virtual void SetInitialParameter(void); - std::string GetParameterName(std::string _input); bool disabled; public: ClassFlow(void); - ClassFlow(std::vector * lfc); - ClassFlow(std::vector * lfc, ClassFlow *_prev); - - virtual bool ReadParameter(FILE* pfile, string &aktparamgraph); - virtual bool doFlow(string time); - virtual string getHTMLSingleStep(string host); - virtual string name(){return "ClassFlow";}; + ClassFlow(std::vector *lfc); + ClassFlow(std::vector *lfc, ClassFlow *_prev); + bool isNewParagraph(std::string input); + bool GetNextParagraph(FILE *pFile, std::string &aktparamgraph); + // bool GetNextParagraph(FILE *pFile, std::string &aktparamgraph, bool &disabled, bool &eof); + bool getNextLine(FILE *pFile, std::string *rt); + // bool getNextLine(FILE *pFile, std::string *rt, bool &disabled, bool &eof); + + virtual bool ReadParameter(FILE *pFile, std::string &aktparamgraph); + virtual bool doFlow(std::string time); + virtual std::string getHTMLSingleStep(std::string host); + virtual std::string name() { return "ClassFlow"; }; }; -#endif //CLASSFLOW_H \ No newline at end of file +#endif // CLASSFLOW_H diff --git a/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp b/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp index 2324fd3c..9f077620 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp @@ -1,4 +1,7 @@ +#include "defines.h" + #include "ClassFlowAlignment.h" +#include "ClassControllCamera.h" #include "ClassFlowTakeImage.h" #include "ClassFlow.h" #include "MainFlowControl.h" @@ -8,19 +11,15 @@ #include "ClassLogFile.h" #include "psram.h" -#include "../../include/defines.h" static const char *TAG = "ALIGN"; -// #define DEBUG_DETAIL_ON - void ClassFlowAlignment::SetInitialParameter(void) { - initialrotate = 0; anz_ref = 0; - use_antialiasing = false; - initialflip = false; - SaveAllFiles = false; + Camera.ImageInitialRotate = 0; + Camera.ImageAntialiasing = false; + Camera.ImageInitialFlip = false; namerawimage = "/sdcard/img_tmp/raw.jpg"; FileStoreRefAlignment = "/sdcard/config/align.txt"; ListFlowControll = NULL; @@ -40,126 +39,147 @@ ClassFlowAlignment::ClassFlowAlignment(std::vector *lfc) SetInitialParameter(); ListFlowControll = lfc; - for (int i = 0; i < ListFlowControll->size(); ++i) { - if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0) { + for (int i = 0; i < ListFlowControll->size(); ++i) + { + if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0) + { ImageBasis = ((ClassFlowTakeImage *)(*ListFlowControll)[i])->rawImage; } } // the function take pictures does not exist --> must be created first ONLY FOR TEST PURPOSES - if (!ImageBasis) { + if (!ImageBasis) + { ESP_LOGD(TAG, "CImageBasis had to be created"); ImageBasis = new CImageBasis("ImageBasis", namerawimage); } } -bool ClassFlowAlignment::ReadParameter(FILE *pfile, string &aktparamgraph) +bool ClassFlowAlignment::ReadParameter(FILE *pfile, std::string &aktparamgraph) { - std::vector splitted; - int suchex = 40; - int suchey = 40; - int alg_algo = 0; // default=0; 1 =HIGHACCURACY; 2= FAST; 3= OFF //add disable aligment algo |01.2023 - - aktparamgraph = trim(aktparamgraph); - + aktparamgraph = trim_string_left_right(aktparamgraph); if (aktparamgraph.size() == 0) { - if (!this->GetNextParagraph(pfile, aktparamgraph)) { + if (!GetNextParagraph(pfile, aktparamgraph)) + { return false; } } - if (aktparamgraph.compare("[Alignment]") != 0) + if ((to_upper(aktparamgraph).compare("[ALIGNMENT]") != 0) && (to_upper(aktparamgraph).compare(";[ALIGNMENT]") != 0)) { // Paragraph does not fit Alignment return false; } - while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) + int suchex = 20; + int suchey = 20; + int maxangle = 45; + int alg_algo = 0; // default=0; 1 =HIGHACCURACY; 2= FAST; 3= OFF //add disable aligment algo |01.2023 + + std::vector splitted; + + while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph)) { - splitted = ZerlegeZeile(aktparamgraph); + splitted = split_line(aktparamgraph); - if ((toUpper(splitted[0]) == "FLIPIMAGESIZE") && (splitted.size() > 1)) { - initialflip = alphanumericToBoolean(splitted[1]); - } - else if (((toUpper(splitted[0]) == "initialrotate") || (toUpper(splitted[0]) == "INITIALROTATE")) && (splitted.size() > 1)) { - if (isStringNumeric(splitted[1])) { - this->initialrotate = std::stod(splitted[1]); - } - } - else if ((toUpper(splitted[0]) == "SEARCHFIELDX") && (splitted.size() > 1)) { - if (isStringNumeric(splitted[1])) { - suchex = std::stod(splitted[1]); - } - } - else if ((toUpper(splitted[0]) == "SEARCHFIELDY") && (splitted.size() > 1)) { - if (isStringNumeric(splitted[1])) { - suchey = std::stod(splitted[1]); - } - } - else if ((toUpper(splitted[0]) == "ANTIALIASING") && (splitted.size() > 1)) { - use_antialiasing = alphanumericToBoolean(splitted[1]); - } - else if ((splitted.size() == 3) && (anz_ref < 2)) { - if ((isStringNumeric(splitted[1])) && (isStringNumeric(splitted[2]))) - { - References[anz_ref].image_file = FormatFileName("/sdcard" + splitted[0]); - References[anz_ref].target_x = std::stod(splitted[1]); - References[anz_ref].target_y = std::stod(splitted[2]); - anz_ref++; - } - else - { - References[anz_ref].image_file = FormatFileName("/sdcard" + splitted[0]); - References[anz_ref].target_x = 10; - References[anz_ref].target_y = 10; - anz_ref++; - } - } + if (splitted.size() > 1) + { + std::string _param = to_upper(splitted[0]); - else if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1)) { - SaveAllFiles = alphanumericToBoolean(splitted[1]); - } - else if ((toUpper(splitted[0]) == "ALIGNMENTALGO") && (splitted.size() > 1)) { -#ifdef DEBUG_DETAIL_ON - std::string zw2 = "Alignment mode selected: " + splitted[1]; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2); -#endif - if (toUpper(splitted[1]) == "HIGHACCURACY") { - alg_algo = 1; + if (_param == "FLIPIMAGESIZE") + { + Camera.ImageInitialFlip = alphanumeric_to_boolean(splitted[1]); } - if (toUpper(splitted[1]) == "FAST") { - alg_algo = 2; + else if (_param == "INITIALROTATE") + { + if (is_string_numeric(splitted[1])) + { + Camera.ImageInitialRotate = std::stod(splitted[1]); + } } - if (toUpper(splitted[1]) == "OFF") { - // no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023 - alg_algo = 3; + else if (_param == "SEARCHFIELDX") + { + if (is_string_numeric(splitted[1])) + { + suchex = clip_int(std::stoi(splitted[1]), 320, 0); + } + } + else if (_param == "SEARCHFIELDY") + { + if (is_string_numeric(splitted[1])) + { + suchey = clip_int(std::stoi(splitted[1]), 240, 0); + } + } + else if (_param == "SEARCHMAXANGLE") + { + if (is_string_numeric(splitted[1])) + { + maxangle = clip_int(std::stoi(splitted[1]), 180, 0); + } + } + else if (_param == "ANTIALIASING") + { + Camera.ImageAntialiasing = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "ALIGNMENTALGO") + { + if (to_upper(splitted[1]) == "HIGHACCURACY") + { + alg_algo = 1; + } + else if (to_upper(splitted[1]) == "FAST") + { + alg_algo = 2; + } + else if (to_upper(splitted[1]) == "OFF") + { + // no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023 + alg_algo = 3; + } + } + else if ((splitted.size() == 3) && (anz_ref < 2)) + { + if (is_string_numeric(splitted[1]) && is_string_numeric(splitted[2])) + { + References[anz_ref].image_file = format_filename("/sdcard" + splitted[0]); + References[anz_ref].target_x = std::stod(splitted[1]); + References[anz_ref].target_y = std::stod(splitted[2]); + anz_ref++; + } + else + { + References[anz_ref].image_file = format_filename("/sdcard" + splitted[0]); + References[anz_ref].target_x = 10; + References[anz_ref].target_y = 10; + anz_ref++; + } } } } - for (int i = 0; i < anz_ref; ++i) { + for (int i = 0; i < anz_ref; ++i) + { References[i].search_x = suchex; References[i].search_y = suchey; + References[i].search_max_angle = (float)maxangle; References[i].fastalg_SAD_criteria = SAD_criteria; References[i].alignment_algo = alg_algo; -#ifdef DEBUG_DETAIL_ON - std::string zw2 = "Alignment mode written: " + std::to_string(alg_algo); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2); -#endif } // no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023 - if (References[0].alignment_algo != 3) { + if (References[0].alignment_algo != 3) + { return LoadReferenceAlignmentValues(); } return true; } -string ClassFlowAlignment::getHTMLSingleStep(string host) +std::string ClassFlowAlignment::getHTMLSingleStep(std::string host) { - string result; + std::string result; result = "

Rotated Image:

\n"; result = result + "

Found Alignment:

\n"; @@ -167,29 +187,34 @@ string ClassFlowAlignment::getHTMLSingleStep(string host) return result; } -bool ClassFlowAlignment::doFlow(string time) +bool ClassFlowAlignment::doFlow(std::string time) { #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG // AlgROI needs to be allocated before ImageTMP to avoid heap fragmentation - if (!AlgROI) { + if (!AlgROI) + { AlgROI = (ImageData *)heap_caps_realloc(AlgROI, sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - if (!AlgROI) { + if (!AlgROI) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlgROI"); LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow"); } } - if (AlgROI) { + if (AlgROI) + { ImageBasis->writeToMemoryAsJPG((ImageData *)AlgROI, 90); } #endif - if (!ImageTMP) { - ImageTMP = new CImageBasis("tmpImage", ImageBasis); // Make sure the name does not get change, it is relevant for the PSRAM allocation! + if (!ImageTMP) + { + ImageTMP = new CImageBasis("TempImage", ImageBasis); // Make sure the name does not get change, it is relevant for the PSRAM allocation! - if (!ImageTMP) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tmpImage -> Exec this round aborted!"); + if (!ImageTMP) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate TempImage -> Exec this round aborted!"); LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow"); return false; } @@ -198,48 +223,74 @@ bool ClassFlowAlignment::doFlow(string time) delete AlignAndCutImage; AlignAndCutImage = new CAlignAndCutImage("AlignAndCutImage", ImageBasis, ImageTMP); - if (!AlignAndCutImage) { + if (!AlignAndCutImage) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlignAndCutImage -> Exec this round aborted!"); LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow"); return false; } - CRotateImage rt("rawImage", AlignAndCutImage, ImageTMP, initialflip); + CRotateImage rt("rawImage", AlignAndCutImage, ImageTMP, Camera.ImageInitialFlip); - if (initialflip) { - int _zw = ImageBasis->height; + if (Camera.ImageInitialFlip) + { + int temp_value = ImageBasis->height; ImageBasis->height = ImageBasis->width; - ImageBasis->width = _zw; + ImageBasis->width = temp_value; - _zw = ImageTMP->width; + temp_value = ImageTMP->width; ImageTMP->width = ImageTMP->height; - ImageTMP->height = _zw; + ImageTMP->height = temp_value; } - if ((initialrotate != 0) || initialflip) { - if (use_antialiasing) { - rt.RotateAntiAliasing(initialrotate); + if (((Camera.ImageInitialRotate > 0) && (Camera.ImageInitialRotate < 360)) || Camera.ImageInitialFlip) + { + if (Camera.ImageAntialiasing) + { + rt.RotateAntiAliasing(Camera.ImageInitialRotate); } - else { - rt.Rotate(initialrotate); + else + { + rt.Rotate(Camera.ImageInitialRotate); } - if (SaveAllFiles) { - AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg")); + if (Camera.SaveAllFiles) + { + AlignAndCutImage->SaveToFile(format_filename("/sdcard/img_tmp/rot.jpg")); } } // no align algo if set to 3 = off //add disable aligment algo |01.2023 - if (References[0].alignment_algo != 3) { - if (!AlignAndCutImage->Align(&References[0], &References[1])) { - SaveReferenceAlignmentValues(); + if (References[0].alignment_algo != 3) + { + int res = AlignAndCutImage->Align(&References[0], &References[1]); + if (res >= Alignment_OK) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Alignment OK"); + if (res == Fast_Alignment_OK) + { + SaveReferenceAlignmentValues(); + } + flowctrl.AlignmentOk = true; } - } // no align + else + { + // Alignment failed + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Alignment failed"); + flowctrl.AlignmentOk = false; + } + } + else + { + flowctrl.AlignmentOk = true; + } #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG - if (AlgROI) { + if (AlgROI) + { // no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023 - if (References[0].alignment_algo != 3) { + if (References[0].alignment_algo != 3) + { DrawRef(ImageTMP); } @@ -249,31 +300,26 @@ bool ClassFlowAlignment::doFlow(string time) } #endif - if (SaveAllFiles) { - AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/alg.jpg")); - ImageTMP->SaveToFile(FormatFileName("/sdcard/img_tmp/alg_roi.jpg")); + if (Camera.SaveAllFiles) + { + AlignAndCutImage->SaveToFile(format_filename("/sdcard/img_tmp/alg.jpg")); + ImageTMP->SaveToFile(format_filename("/sdcard/img_tmp/alg_roi.jpg")); } // must be deleted to have memory space for loading tflite delete ImageTMP; ImageTMP = NULL; - // no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023 - if (References[0].alignment_algo != 3) { - return LoadReferenceAlignmentValues(); - } - return true; } -void ClassFlowAlignment::SaveReferenceAlignmentValues() +void ClassFlowAlignment::SaveReferenceAlignmentValues(void) { - FILE *pFile; - std::string zwtime, zwvalue; + FILE *pFile = fopen(FileStoreRefAlignment.c_str(), "w"); - pFile = fopen(FileStoreRefAlignment.c_str(), "w"); - - if (strlen(zwtime.c_str()) == 0) { + std::string temp_time; + if (strlen(temp_time.c_str()) == 0) + { time_t rawtime; struct tm *timeinfo; char buffer[80]; @@ -282,22 +328,22 @@ void ClassFlowAlignment::SaveReferenceAlignmentValues() timeinfo = localtime(&rawtime); strftime(buffer, 80, "%Y-%m-%dT%H:%M:%S", timeinfo); - zwtime = std::string(buffer); + temp_time = std::string(buffer); } - fputs(zwtime.c_str(), pFile); + fputs(temp_time.c_str(), pFile); fputs("\n", pFile); - zwvalue = std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_y); - zwvalue = zwvalue + "\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min); - zwvalue = zwvalue + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg); - fputs(zwvalue.c_str(), pFile); + std::string temp_value = std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_y); + temp_value = temp_value + "\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min); + temp_value = temp_value + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg); + fputs(temp_value.c_str(), pFile); fputs("\n", pFile); - zwvalue = std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_y); - zwvalue = zwvalue + "\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min); - zwvalue = zwvalue + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg); - fputs(zwvalue.c_str(), pFile); + temp_value = std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_y); + temp_value = temp_value + "\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min); + temp_value = temp_value + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg); + fputs(temp_value.c_str(), pFile); fputs("\n", pFile); fclose(pFile); @@ -305,70 +351,100 @@ void ClassFlowAlignment::SaveReferenceAlignmentValues() bool ClassFlowAlignment::LoadReferenceAlignmentValues(void) { - FILE *pFile; - char zw[1024]; - string zwvalue; - std::vector splitted; - - pFile = fopen(FileStoreRefAlignment.c_str(), "r"); - - if (pFile == NULL) { + FILE *pFile = fopen(FileStoreRefAlignment.c_str(), "r"); + if (pFile == NULL) + { return false; } - fgets(zw, 1024, pFile); - ESP_LOGD(TAG, "%s", zw); - - fgets(zw, 1024, pFile); - splitted = ZerlegeZeile(std::string(zw), " \t"); - - if (splitted.size() < 6) { + char temp_bufer[1024]; + // erste Zeile: 2025-03-16T18:50:22 + if (!fgets(temp_bufer, 1024, pFile)) + { fclose(pFile); + ESP_LOGE(TAG, "/sdcard/config/align.txt empty!"); return false; } - References[0].fastalg_x = stoi(splitted[0]); - References[0].fastalg_y = stoi(splitted[1]); - References[0].fastalg_SAD = stof(splitted[2]); - References[0].fastalg_min = stoi(splitted[3]); - References[0].fastalg_max = stoi(splitted[4]); - References[0].fastalg_avg = stof(splitted[5]); - - fgets(zw, 1024, pFile); - splitted = ZerlegeZeile(std::string(zw)); - - if (splitted.size() < 6) { + // zweite Zeile: 177 342 -0.000000 6144 1611659784 0.000000 + if (!fgets(temp_bufer, 1024, pFile)) + { fclose(pFile); + ESP_LOGE(TAG, "/sdcard/config/align.txt empty!"); return false; } - References[1].fastalg_x = stoi(splitted[0]); - References[1].fastalg_y = stoi(splitted[1]); - References[1].fastalg_SAD = stof(splitted[2]); - References[1].fastalg_min = stoi(splitted[3]); - References[1].fastalg_max = stoi(splitted[4]); - References[1].fastalg_avg = stof(splitted[5]); + std::vector splitted; + splitted = split_line(temp_bufer, "\t"); + + if (splitted.size() < 6) + { + fclose(pFile); + ESP_LOGE(TAG, "/sdcard/config/align.txt wrong format!"); + return false; + } + + if (is_string_numeric(splitted[0]) && is_string_numeric(splitted[1]) && is_string_numeric(splitted[2]) && + is_string_numeric(splitted[3]) && is_string_numeric(splitted[4]) && is_string_numeric(splitted[5])) + { + References[0].fastalg_x = stoi(splitted[0]); + References[0].fastalg_y = stoi(splitted[1]); + References[0].fastalg_SAD = stof(splitted[2]); + References[0].fastalg_min = stoi(splitted[3]); + References[0].fastalg_max = stoi(splitted[4]); + References[0].fastalg_avg = stof(splitted[5]); + } + else + { + fclose(pFile); + ESP_LOGE(TAG, "/sdcard/config/align.txt wrong format!"); + return false; + } + + // dritte Zeile: 398 145 -0.000000 6144 1611659784 0.000000 + if (!fgets(temp_bufer, 1024, pFile)) + { + fclose(pFile); + ESP_LOGE(TAG, "/sdcard/config/align.txt empty!"); + return false; + } + + splitted = split_line(temp_bufer, "\t"); + + if (splitted.size() < 6) + { + fclose(pFile); + ESP_LOGE(TAG, "/sdcard/config/align.txt wrong format!"); + return false; + } + + if (is_string_numeric(splitted[0]) && is_string_numeric(splitted[1]) && is_string_numeric(splitted[2]) && + is_string_numeric(splitted[3]) && is_string_numeric(splitted[4]) && is_string_numeric(splitted[5])) + { + References[1].fastalg_x = stoi(splitted[0]); + References[1].fastalg_y = stoi(splitted[1]); + References[1].fastalg_SAD = stof(splitted[2]); + References[1].fastalg_min = stoi(splitted[3]); + References[1].fastalg_max = stoi(splitted[4]); + References[1].fastalg_avg = stof(splitted[5]); + } + else + { + fclose(pFile); + ESP_LOGE(TAG, "/sdcard/config/align.txt wrong format!"); + return false; + } fclose(pFile); - /*#ifdef DEBUG_DETAIL_ON - std::string _zw = "\tLoadReferences[0]\tx,y:\t" + std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_x); - _zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min); - _zw = _zw + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg); - LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw); - _zw = "\tLoadReferences[1]\tx,y:\t" + std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_x); - _zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min); - _zw = _zw + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg); - LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw); - #endif*/ - return true; } -void ClassFlowAlignment::DrawRef(CImageBasis *_zw) +void ClassFlowAlignment::DrawRef(CImageBasis *Image) { - if (_zw->ImageOkay()) { - _zw->drawRect(References[0].target_x, References[0].target_y, References[0].width, References[0].height, 255, 0, 0, 2); - _zw->drawRect(References[1].target_x, References[1].target_y, References[1].width, References[1].height, 255, 0, 0, 2); + if (Image->ImageOkay()) + { + Image->drawRect(References[0].target_x, References[0].target_y, References[0].width, References[0].height, 255, 0, 0, 2); + Image->drawRect(References[1].target_x, References[1].target_y, References[1].width, References[1].height, 255, 0, 0, 2); } } diff --git a/code/components/jomjol_flowcontroll/ClassFlowAlignment.h b/code/components/jomjol_flowcontroll/ClassFlowAlignment.h index 2d0944cb..1d99bae9 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowAlignment.h +++ b/code/components/jomjol_flowcontroll/ClassFlowAlignment.h @@ -3,25 +3,21 @@ #ifndef CLASSFLOWALIGNMENT_H #define CLASSFLOWALIGNMENT_H +#include + #include "ClassFlow.h" #include "Helper.h" #include "CAlignAndCutImage.h" #include "CFindTemplate.h" -#include - using namespace std; class ClassFlowAlignment : public ClassFlow { protected: - float initialrotate; - bool initialflip; - bool use_antialiasing; RefInfo References[2]; int anz_ref; - string namerawimage; - bool SaveAllFiles; + std::string namerawimage; CAlignAndCutImage *AlignAndCutImage; std::string FileStoreRefAlignment; float SAD_criteria; @@ -40,12 +36,12 @@ public: CAlignAndCutImage *GetAlignAndCutImage() { return AlignAndCutImage; }; - void DrawRef(CImageBasis *_zw); + void DrawRef(CImageBasis *Image); - bool ReadParameter(FILE *pfile, string &aktparamgraph); - bool doFlow(string time); - string getHTMLSingleStep(string host); - string name() { return "ClassFlowAlignment"; }; + bool ReadParameter(FILE *pfile, std::string &aktparamgraph); + bool doFlow(std::string time); + std::string getHTMLSingleStep(std::string host); + std::string name() { return "ClassFlowAlignment"; }; }; #endif // CLASSFLOWALIGNMENT_H diff --git a/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp b/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp index 5b3c89c2..b2fdfd82 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp @@ -1,129 +1,172 @@ -#include "ClassFlowCNNGeneral.h" +#include "defines.h" #include -#include +#include #include -#include // std::stringstream +#include // std::stringstream +#include "esp_log.h" + +#include "ClassFlowCNNGeneral.h" +#include "MainFlowControl.h" +#include "ClassControllCamera.h" #include "CTfLiteClass.h" #include "ClassLogFile.h" -#include "esp_log.h" -#include "../../include/defines.h" -static const char* TAG = "CNN"; +static const char *TAG = "CNN"; -//#ifdef CONFIG_HEAP_TRACING_STANDALONE -#ifdef HEAP_TRACING_CLASS_FLOW_CNN_GENERAL_DO_ALING_AND_CUT - #include - #define NUM_RECORDS 300 - static heap_trace_record_t trace_record[NUM_RECORDS]; // This buffer must be in internal RAM -#endif +ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype) : ClassFlowImage(NULL, TAG) +{ + cnn_model_file = ""; + model_x_size = 1; + model_y_size = 1; -ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype) : ClassFlowImage(NULL, TAG) { - string cnnmodelfile = ""; - modelxsize = 1; - modelysize = 1; CNNGoodThreshold = 0.0; ListFlowControll = NULL; - previousElement = NULL; - SaveAllFiles = false; - disabled = false; + previousElement = NULL; + isLogImageSelect = false; - CNNType = AutoDetect; CNNType = _cnntype; flowpostalignment = _flowalign; imagesRetention = 5; + + disabled = false; } -string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution, int prev, float _before_narrow_Analog, float AnalogToDigitTransitionStart) { - string result = ""; +std::vector ClassFlowCNNGeneral::getMeterValues(int _number = 0) +{ + std::vector meterValues; - if (GENERAL[_analog]->ROI.size() == 0) { + if (GENERAL[_number]->ROI.size() == 0) + { + return meterValues; + } + + for (int i = 0; i < GENERAL[_number]->ROI.size(); ++i) + { + if (CNNType == Digit) + { + meterValues.push_back(GENERAL[_number]->ROI[i]->result_klasse); + } + else + { + meterValues.push_back(GENERAL[_number]->ROI[i]->result_float); + } + } + + return meterValues; +} + +std::string ClassFlowCNNGeneral::getReadout(int _number = 0, bool _extendedResolution, int prev, float _before_narrow_Analog, float AnalogToDigitTransitionStart) +{ + std::string result = ""; + + if (GENERAL[_number]->ROI.size() == 0) + { return result; } - - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout _analog=" + std::to_string(_analog) + ", _extendedResolution=" + std::to_string(_extendedResolution) + ", prev=" + std::to_string(prev)); - - if (CNNType == Analogue || CNNType == Analogue100) { - float number = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float; - int result_after_decimal_point = ((int) floor(number * 10) + 10) % 10; - - prev = PointerEvalAnalogNew(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev); -// LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(analog) number=" + std::to_string(number) + ", result_after_decimal_point=" + std::to_string(result_after_decimal_point) + ", prev=" + std::to_string(prev)); + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout _number=" + std::to_string(_number) + ", _extendedResolution=" + std::to_string(_extendedResolution) + ", prev=" + std::to_string(prev)); + + if (CNNType == Analogue || CNNType == Analogue100) + { + float number = GENERAL[_number]->ROI[GENERAL[_number]->ROI.size() - 1]->result_float; + int result_after_decimal_point = ((int)floor(number * 10) + 10) % 10; + + prev = PointerEvalAnalog(GENERAL[_number]->ROI[GENERAL[_number]->ROI.size() - 1]->result_float, prev); result = std::to_string(prev); - if (_extendedResolution) { + if (_extendedResolution) + { result = result + std::to_string(result_after_decimal_point); } - for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i) { - prev = PointerEvalAnalogNew(GENERAL[_analog]->ROI[i]->result_float, prev); + for (int i = GENERAL[_number]->ROI.size() - 2; i >= 0; --i) + { + prev = PointerEvalAnalog(GENERAL[_number]->ROI[i]->result_float, prev); result = std::to_string(prev) + result; } return result; } - if (CNNType == Digit) { - for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i) { - if ((GENERAL[_analog]->ROI[i]->result_klasse >= 0) && (GENERAL[_analog]->ROI[i]->result_klasse < 10)) { - result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse); + if (CNNType == Digit) + { + for (int i = 0; i < GENERAL[_number]->ROI.size(); ++i) + { + if ((GENERAL[_number]->ROI[i]->result_klasse >= 0) && (GENERAL[_number]->ROI[i]->result_klasse < 10)) + { + result = result + std::to_string(GENERAL[_number]->ROI[i]->result_klasse); } - else { + else + { result = result + "N"; } } return result; } - if ((CNNType == DoubleHyprid10) || (CNNType == Digit100)) { - float number = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float; + if ((CNNType == DoubleHyprid10) || (CNNType == Digit100)) + { + float number = GENERAL[_number]->ROI[GENERAL[_number]->ROI.size() - 1]->result_float; // NaN? - if ((number >= 0) && (number < 10)) { + if ((number >= 0.0f) && (number < 10.0f)) + { // is only set if it is the first digit (no analogue before!) - if (_extendedResolution) { - int result_after_decimal_point = ((int) floor(number * 10)) % 10; - int result_before_decimal_point = ((int) floor(number)) % 10; + if (_extendedResolution) + { + int result_after_decimal_point = ((int)floor(number * 10)) % 10; + int result_before_decimal_point = ((int)floor(number)) % 10; result = std::to_string(result_before_decimal_point) + std::to_string(result_after_decimal_point); prev = result_before_decimal_point; LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(dig100-ext) result_before_decimal_point=" + std::to_string(result_before_decimal_point) + ", result_after_decimal_point=" + std::to_string(result_after_decimal_point) + ", prev=" + std::to_string(prev)); } - else { - if (_before_narrow_Analog >= 0) { - prev = PointerEvalHybridNew(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, _before_narrow_Analog, prev, true, AnalogToDigitTransitionStart); + else + { + if (_before_narrow_Analog >= 0.0f) + { + prev = PointerEvalHybrid(GENERAL[_number]->ROI[GENERAL[_number]->ROI.size() - 1]->result_float, _before_narrow_Analog, prev, true, AnalogToDigitTransitionStart); } - else { - prev = PointerEvalHybridNew(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev, prev); + else + { + prev = PointerEvalHybrid(GENERAL[_number]->ROI[GENERAL[_number]->ROI.size() - 1]->result_float, prev, prev); } - // is necessary because a number greater than 9.994999 returns a 10! (for further details see check in PointerEvalHybridNew) - if ((prev >= 0) && (prev < 10)) { + // is necessary because a number greater than 9.994999 returns a 10! (for further details see check in PointerEvalHybrid) + if ((prev >= 0) && (prev < 10)) + { result = std::to_string(prev); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(dig100) prev=" + std::to_string(prev)); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(dig100) prev=" + std::to_string(prev)); } - else { + else + { result = "N"; } } } - else { + else + { result = "N"; - if (_extendedResolution && (CNNType != Digit)) { + if (_extendedResolution && (CNNType != Digit)) + { result = "NN"; } } - for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i) { - if ((GENERAL[_analog]->ROI[i]->result_float >= 0) && (GENERAL[_analog]->ROI[i]->result_float < 10)) { - prev = PointerEvalHybridNew(GENERAL[_analog]->ROI[i]->result_float, GENERAL[_analog]->ROI[i+1]->result_float, prev); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout#PointerEvalHybridNew()= " + std::to_string(prev)); + for (int i = GENERAL[_number]->ROI.size() - 2; i >= 0; --i) + { + if ((GENERAL[_number]->ROI[i]->result_float >= 0.0f) && (GENERAL[_number]->ROI[i]->result_float < 10.0f)) + { + prev = PointerEvalHybrid(GENERAL[_number]->ROI[i]->result_float, GENERAL[_number]->ROI[i + 1]->result_float, prev); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(DoubleHyprid10) - roi_" + std::to_string(i) + "prev= " + std::to_string(prev)); result = std::to_string(prev) + result; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout#result= " + result); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(DoubleHyprid10) - roi_" + std::to_string(i) + "result= " + result); } - else { + else + { prev = -1; result = "N" + result; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(result_float<0 /'N') result_float=" + std::to_string(GENERAL[_analog]->ROI[i]->result_float)); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "getReadout(result_float < 0 /'N') - roi_" + std::to_string(i) + "result_float=" + std::to_string(GENERAL[_number]->ROI[i]->result_float)); } } return result; @@ -133,499 +176,556 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution /** * @brief Determines the number of an ROI in connection with previous ROI results - * + * * @param number: is the current ROI as float value from recognition * @param number_of_predecessors: is the last (lower) ROI as float from recognition * @param eval_predecessors: is the evaluated number. Sometimes a much lower value can change higer values * example: 9.8, 9.9, 0.1 * 0.1 => 0 (eval_predecessors) * The 0 makes a 9.9 to 0 (eval_predecessors) - * The 0 makes a 9.8 to 0 + * The 0 makes a 9.8 to 0 * @param Analog_Predecessors false/true if the last ROI is an analog or digit ROI (default=false) * runs in special handling because analog is much less precise * @param digitAnalogTransitionStart start of the transitionlogic begins on number_of_predecessor (default=9.2) * * @return int the determined number of the current ROI */ -int ClassFlowCNNGeneral::PointerEvalHybridNew(float number, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors, float digitAnalogTransitionStart) { - int result; - int result_after_decimal_point = ((int) floor(number * 10)) % 10; - int result_before_decimal_point = ((int) floor(number) + 10) % 10; +int ClassFlowCNNGeneral::PointerEvalHybrid(float number, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors, float digitAnalogTransitionStart) +{ + int result = -1; + int result_after_decimal_point = ((int)floor(number * 10)) % 10; + int result_before_decimal_point = ((int)floor(number) + 10) % 10; - if (eval_predecessors < 0) { + if (eval_predecessors < 0) + { // on first digit is no spezial logic for transition needed // we use the recognition as given. The result is the int value of the recognition // add precisition of 2 digits and round before trunc // a number greater than 9.994999 is returned as 10, this leads to an error during the decimal shift because the NUMBERS[j]->ReturnRawValue is one digit longer. // To avoid this, an additional test must be carried out, see "if ((CNNType == DoubleHyprid10) || (CNNType == Digit100))" check in getReadout() // Another alternative would be "result = (int) ((int) trunc(round((number+10 % 10)*1000))) / 1000;", which could, however, lead to other errors? - result = (int) ((int) trunc(round((number+10 % 10)*100)) ) / 100; + result = (int)((int)trunc(round((number + 10 % 10) * 100))) / 100; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - No predecessor - Result = " + std::to_string(result) + - " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digit_Uncertainty = " + std::to_string(Digit_Uncertainty)); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybrid - No predecessor - Result = " + std::to_string(result) + " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors) + " eval_predecessors = " + std::to_string(eval_predecessors) + " Digit_Uncertainty = " + std::to_string(Digit_Uncertainty)); return result; } - if (Analog_Predecessors) { - result = PointerEvalAnalogToDigitNew(number, number_of_predecessors, eval_predecessors, digitAnalogTransitionStart); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - Analog predecessor, evaluation over PointerEvalAnalogNew = " + std::to_string(result) + - " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digit_Uncertainty = " + std::to_string(Digit_Uncertainty)); + if (Analog_Predecessors) + { + result = PointerEvalAnalogToDigit(number, number_of_predecessors, eval_predecessors, digitAnalogTransitionStart); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybrid - Analog predecessor, evaluation over PointerEvalAnalog = " + std::to_string(result) + " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors) + " eval_predecessors = " + std::to_string(eval_predecessors) + " Digit_Uncertainty = " + std::to_string(Digit_Uncertainty)); return result; } - if ((number_of_predecessors >= Digit_Transition_Area_Predecessor ) && (number_of_predecessors <= (10.0 - Digit_Transition_Area_Predecessor))) { + if ((number_of_predecessors >= Digit_Transition_Area_Predecessor) && (number_of_predecessors <= (10.0 - Digit_Transition_Area_Predecessor))) + { // no digit change, because predecessor is far enough away (0+/-DigitTransitionRangePredecessor) --> number is rounded // Band around the digit --> Round off, as digit reaches inaccuracy in the frame - if ((result_after_decimal_point <= DigitBand) || (result_after_decimal_point >= (10-DigitBand))) { - result = ((int) round(number) + 10) % 10; + if ((result_after_decimal_point <= DigitBand) || (result_after_decimal_point >= (10 - DigitBand))) + { + result = ((int)round(number) + 10) % 10; } - else { - result = ((int) trunc(number) + 10) % 10; + else + { + result = ((int)trunc(number) + 10) % 10; } - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - NO analogue predecessor, no change of digits, as pre-decimal point far enough away = " + std::to_string(result) + - " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digit_Uncertainty = " + std::to_string(Digit_Uncertainty)); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybrid - NO analogue predecessor, no change of digits, as pre-decimal point far enough away = " + std::to_string(result) + " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors) + " eval_predecessors = " + std::to_string(eval_predecessors) + " Digit_Uncertainty = " + std::to_string(Digit_Uncertainty)); return result; - } + } // Zero crossing at the predecessor has taken place (! evaluation via Prev_value and not number!) --> round up here (2.8 --> 3, but also 3.1 --> 3) - if (eval_predecessors <= 1) { + if (eval_predecessors <= 1) + { // We simply assume that the current digit after the zero crossing of the predecessor // has passed through at least half (x.5) - if (result_after_decimal_point > 5) { + if (result_after_decimal_point > 5) + { // The current digit does not yet have a zero crossing, but the predecessor does.. - result = (result_before_decimal_point + 1) % 10; + result = (result_before_decimal_point + 1) % 10; } - else { + else + { // Act. digit and predecessor have zero crossing - result = result_before_decimal_point % 10; + result = result_before_decimal_point % 10; } - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - NO analogue predecessor, zero crossing has taken placen = " + std::to_string(result) + - " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digit_Uncertainty = " + std::to_string(Digit_Uncertainty)); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybrid - NO analogue predecessor, zero crossing has taken placen = " + std::to_string(result) + " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors) + " eval_predecessors = " + std::to_string(eval_predecessors) + " Digit_Uncertainty = " + std::to_string(Digit_Uncertainty)); return result; } - // remains only >= 9.x --> no zero crossing yet --> 2.8 --> 2, + // remains only >= 9.x --> no zero crossing yet --> 2.8 --> 2, // and from 9.7(DigitTransitionRangeLead) 3.1 --> 2 // everything >=x.4 can be considered as current number in transition. With 9.x predecessor the current - // number can still be x.6 - x.7. + // number can still be x.6 - x.7. // Preceding (else - branch) does not already happen from 9. - if (Digit_Transition_Area_Forward>=number_of_predecessors || result_after_decimal_point >= 4) { - // The current digit, like the previous digit, does not yet have a zero crossing. - result = result_before_decimal_point % 10; + if (Digit_Transition_Area_Forward >= number_of_predecessors || result_after_decimal_point >= 4) + { + // The current digit, like the previous digit, does not yet have a zero crossing. + result = result_before_decimal_point % 10; } - else { + else + { // current digit precedes the smaller digit (9.x). So already >=x.0 while the previous digit has not yet // has no zero crossing. Therefore, it is reduced by 1. - result = (result_before_decimal_point - 1 + 10) % 10; + result = (result_before_decimal_point + 9) % 10; } - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybridNew - O analogue predecessor, >= 9.5 --> no zero crossing yet = " + std::to_string(result) + - " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors)+ " eval_predecessors = " + std::to_string(eval_predecessors) + " Digit_Uncertainty = " + std::to_string(Digit_Uncertainty) + " result_after_decimal_point = " + std::to_string(result_after_decimal_point)); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalHybrid - O analogue predecessor, >= 9.5 --> no zero crossing yet = " + std::to_string(result) + " number: " + std::to_string(number) + " number_of_predecessors = " + std::to_string(number_of_predecessors) + " eval_predecessors = " + std::to_string(eval_predecessors) + " Digit_Uncertainty = " + std::to_string(Digit_Uncertainty) + " result_after_decimal_point = " + std::to_string(result_after_decimal_point)); return result; } -int ClassFlowCNNGeneral::PointerEvalAnalogToDigitNew(float number, float numeral_preceder, int eval_predecessors, float AnalogToDigitTransitionStart) { - int result; - int result_after_decimal_point = ((int) floor(number * 10)) % 10; - int result_before_decimal_point = ((int) floor(number) + 10) % 10; +int ClassFlowCNNGeneral::PointerEvalAnalogToDigit(float number, float numeral_preceder, int eval_predecessors, float AnalogToDigitTransitionStart) +{ + int result = -1; + int result_after_decimal_point = ((int)floor(number * 10)) % 10; + int result_before_decimal_point = ((int)floor(number) + 10) % 10; bool roundedUp = false; - // Within the digit inequalities - if ((result_after_decimal_point >= (10-Digit_Uncertainty * 10)) // Band around the digit --> Round off, as digit reaches inaccuracy in the frame - || (eval_predecessors <= 4 && result_after_decimal_point>=6)) { // or digit runs after (analogue =0..4, digit >=6) - result = (int) (round(number) + 10) % 10; + // Within the digit inequalities + // Band around the digit --> Round off, as digit reaches inaccuracy in the frame + if ((result_after_decimal_point >= (10 - Digit_Uncertainty * 10)) || (eval_predecessors <= 4 && result_after_decimal_point >= 6)) + { + // or digit runs after (analogue =0..4, digit >=6) + result = (int)(round(number) + 10) % 10; roundedUp = true; // before/ after decimal point, because we adjust the number based on the uncertainty. - result_after_decimal_point = ((int) floor(result * 10)) % 10; - result_before_decimal_point = ((int) floor(result) + 10) % 10; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogToDigitNew - Digit Uncertainty - Result = " + std::to_string(result) + - " number: " + std::to_string(number) + " numeral_preceder: " + std::to_string(numeral_preceder) + - " erg before comma: " + std::to_string(result_before_decimal_point) + - " erg after comma: " + std::to_string(result_after_decimal_point)); - } - else { - result = (int) ((int) trunc(number) + 10) % 10; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogToDigitNew - NO digit Uncertainty - Result = " + std::to_string(result) + - " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder)); + result_after_decimal_point = ((int)floor(result * 10)) % 10; + result_before_decimal_point = ((int)floor(result) + 10) % 10; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogToDigit - Digit Uncertainty - Result = " + std::to_string(result) + " number: " + std::to_string(number) + " numeral_preceder: " + std::to_string(numeral_preceder) + " erg before comma: " + std::to_string(result_before_decimal_point) + " erg after comma: " + std::to_string(result_after_decimal_point)); + } + else + { + result = (int)((int)trunc(number) + 10) % 10; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogToDigit - NO digit Uncertainty - Result = " + std::to_string(result) + " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder)); } // No zero crossing has taken place. // Only eval_predecessors used because numeral_preceder could be wrong here. // numeral_preceder<=0.1 & eval_predecessors=9 corresponds to analogue was reset because of previous analogue that are not yet at 0. - if ((eval_predecessors>=6 && (numeral_preceder>AnalogToDigitTransitionStart || numeral_preceder<=0.2) && roundedUp)) { - result = ((result_before_decimal_point+10) - 1) % 10; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogToDigitNew - Nulldurchgang noch nicht stattgefunden = " + std::to_string(result) + - " number: " + std::to_string(number) + - " numeral_preceder = " + std::to_string(numeral_preceder) + - " eerg after comma = " + std::to_string(result_after_decimal_point)); + if ((eval_predecessors >= 6 && (numeral_preceder > AnalogToDigitTransitionStart || numeral_preceder <= 0.2) && roundedUp)) + { + result = ((result_before_decimal_point + 10) - 1) % 10; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogToDigit - Nulldurchgang noch nicht stattgefunden = " + std::to_string(result) + " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " eerg after comma = " + std::to_string(result_after_decimal_point)); } return result; } -int ClassFlowCNNGeneral::PointerEvalAnalogNew(float number, int numeral_preceder) { - float number_min, number_max; - int result; +int ClassFlowCNNGeneral::PointerEvalAnalog(float number, int numeral_preceder) +{ + int result = -1; - if (numeral_preceder == -1) { - result = (int) floor(number); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogNew - No predecessor - Result = " + std::to_string(result) + - " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error)); + if (numeral_preceder == -1) + { + result = (int)floor(number); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalog - No predecessor - Result = " + std::to_string(result) + " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error)); return result; } - number_min = number - Analog_error / 10.0; - number_max = number + Analog_error / 10.0; + float number_min = number - Analog_error / 10.0; + float number_max = number + Analog_error / 10.0; - if ((int) floor(number_max) - (int) floor(number_min) != 0) { - if (numeral_preceder <= Analog_error) { - result = ((int) floor(number_max) + 10) % 10; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogNew - number ambiguous, correction upwards - result = " + std::to_string(result) + - " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error)); + if ((int)floor(number_max) - (int)floor(number_min) != 0) + { + if (numeral_preceder <= Analog_error) + { + result = ((int)floor(number_max) + 10) % 10; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalog - number ambiguous, correction upwards - result = " + std::to_string(result) + " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error)); return result; } - if (numeral_preceder >= 10 - Analog_error) { - result = ((int) floor(number_min) + 10) % 10; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogNew - number ambiguous, downward correction - result = " + std::to_string(result) + - " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error)); + if (numeral_preceder >= 10 - Analog_error) + { + result = ((int)floor(number_min) + 10) % 10; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalog - number ambiguous, downward correction - result = " + std::to_string(result) + " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error)); return result; } } - - result = ((int) floor(number) + 10) % 10; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalogNew - number unambiguous, no correction necessary - result = " + std::to_string(result) + - " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error)); + + result = ((int)floor(number) + 10) % 10; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "PointerEvalAnalog - number unambiguous, no correction necessary - result = " + std::to_string(result) + " number: " + std::to_string(number) + " numeral_preceder = " + std::to_string(numeral_preceder) + " Analog_error = " + std::to_string(Analog_error)); return result; } -bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph) { - std::vector splitted; - - aktparamgraph = trim(aktparamgraph); - - if (aktparamgraph.size() == 0) { - if (!this->GetNextParagraph(pfile, aktparamgraph)) { +bool ClassFlowCNNGeneral::ReadParameter(FILE *pfile, std::string &aktparamgraph) +{ + aktparamgraph = trim_string_left_right(aktparamgraph); + if (aktparamgraph.size() == 0) + { + if (!GetNextParagraph(pfile, aktparamgraph)) + { return false; } } - if ((toUpper(aktparamgraph) != "[ANALOG]") && (toUpper(aktparamgraph) != ";[ANALOG]") - && (toUpper(aktparamgraph) != "[DIGIT]") && (toUpper(aktparamgraph) != ";[DIGIT]") - && (toUpper(aktparamgraph) != "[DIGITS]") && (toUpper(aktparamgraph) != ";[DIGITS]")) { + if ((to_upper(aktparamgraph).compare("[ANALOG]") != 0) && (to_upper(aktparamgraph).compare(";[ANALOG]") != 0) && + (to_upper(aktparamgraph).compare("[DIGIT]") != 0) && (to_upper(aktparamgraph).compare(";[DIGIT]") != 0) && + (to_upper(aktparamgraph).compare("[DIGITS]") != 0) && (to_upper(aktparamgraph).compare(";[DIGITS]") != 0)) + { // Paragraph passt nicht return false; } - if (aktparamgraph[0] == ';') { + if (aktparamgraph[0] == ';') + { disabled = true; while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph)); ESP_LOGD(TAG, "[Analog/Digit] is disabled!"); + return true; } - while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) { - splitted = ZerlegeZeile(aktparamgraph); - if ((toUpper(splitted[0]) == "ROIIMAGESLOCATION") && (splitted.size() > 1)) { - this->imagesLocation = "/sdcard" + splitted[1]; - this->isLogImage = true; - } - - if ((toUpper(splitted[0]) == "LOGIMAGESELECT") && (splitted.size() > 1)) { - LogImageSelect = splitted[1]; - isLogImageSelect = true; - } + std::vector splitted; - if ((toUpper(splitted[0]) == "ROIIMAGESRETENTION") && (splitted.size() > 1)) { - if (isStringNumeric(splitted[1])) { - this->imagesRetention = std::stoi(splitted[1]); - } - } + while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph)) + { + splitted = split_line(aktparamgraph); - if ((toUpper(splitted[0]) == "MODEL") && (splitted.size() > 1)) { - this->cnnmodelfile = splitted[1]; - } - - if ((toUpper(splitted[0]) == "CNNGOODTHRESHOLD") && (splitted.size() > 1)) { - if (isStringNumeric(splitted[1])) { - CNNGoodThreshold = std::stof(splitted[1]); - } - } - - if (splitted.size() >= 5) { - general* _analog = GetGENERAL(splitted[0], true); - roi* neuroi = _analog->ROI[_analog->ROI.size()-1]; - neuroi->posx = std::stoi(splitted[1]); - neuroi->posy = std::stoi(splitted[2]); - neuroi->deltax = std::stoi(splitted[3]); - neuroi->deltay = std::stoi(splitted[4]); - neuroi->CCW = false; - - if (splitted.size() >= 6) { - neuroi->CCW = toUpper(splitted[5]) == "TRUE"; - } - - neuroi->result_float = -1; - neuroi->image = NULL; - neuroi->image_org = NULL; - } + if (splitted.size() > 1) + { + std::string _param = to_upper(splitted[0]); - if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1)) { - SaveAllFiles = alphanumericToBoolean(splitted[1]); + if (_param == "ROIIMAGESLOCATION") + { + imagesLocation = "/sdcard" + splitted[1]; + isLogImage = true; + } + else if (_param == "LOGIMAGESELECT") + { + LogImageSelect = splitted[1]; + isLogImageSelect = true; + } + else if (_param == "ROIIMAGESRETENTION") + { + if (is_string_numeric(splitted[1])) + { + imagesRetention = std::stoi(splitted[1]); + } + } + else if (_param == "MODEL") + { + cnn_model_file = splitted[1]; + } + else if (_param == "CNNGOODTHRESHOLD") + { + if (is_string_numeric(splitted[1])) + { + CNNGoodThreshold = std::stof(splitted[1]); + } + } + else if (splitted.size() >= 5) + { + general *_general = GetGENERAL(splitted[0], true); + roi *new_roi = _general->ROI[_general->ROI.size() - 1]; + + if (is_string_numeric(splitted[1]) && is_string_numeric(splitted[2]) && is_string_numeric(splitted[3]) && is_string_numeric(splitted[4])) + { + new_roi->pos_x = std::stoi(splitted[1]); + new_roi->pos_y = std::stoi(splitted[2]); + + new_roi->delta_x = std::stoi(splitted[3]); + new_roi->delta_y = std::stoi(splitted[4]); + } + else + { + new_roi->pos_x = 0; + new_roi->pos_y = 0; + + new_roi->delta_x = 30; + new_roi->delta_y = 30; + } + + new_roi->ccw = false; + if (splitted.size() >= 6) + { + new_roi->ccw = alphanumeric_to_boolean(splitted[5]); + } + + new_roi->result_float = -1; + new_roi->image = NULL; + new_roi->image_org = NULL; + } } } - if (!getNetworkParameter()) { + if (!getNetworkParameter()) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "An error occured on setting up the Network -> Disabling it!"); disabled = true; // An error occured, disable this CNN! return false; } - for (int _ana = 0; _ana < GENERAL.size(); ++_ana) { - for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) { - GENERAL[_ana]->ROI[i]->image = new CImageBasis("ROI " + GENERAL[_ana]->ROI[i]->name, - modelxsize, modelysize, modelchannel); - GENERAL[_ana]->ROI[i]->image_org = new CImageBasis("ROI " + GENERAL[_ana]->ROI[i]->name + " original", - GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, 3); + for (int i = 0; i < GENERAL.size(); ++i) + { + for (int j = 0; j < GENERAL[i]->ROI.size(); ++j) + { + GENERAL[i]->ROI[j]->image = new CImageBasis("ROI " + GENERAL[i]->ROI[j]->name, model_x_size, model_y_size, model_channel); + GENERAL[i]->ROI[j]->image_org = new CImageBasis("ROI " + GENERAL[i]->ROI[j]->name + " original", GENERAL[i]->ROI[j]->delta_x, GENERAL[i]->ROI[j]->delta_y, 3); } } return true; } -general* ClassFlowCNNGeneral::FindGENERAL(string _name_number) { - for (int i = 0; i < GENERAL.size(); ++i) { - if (GENERAL[i]->name == _name_number) { +general *ClassFlowCNNGeneral::FindGENERAL(std::string _name_number) +{ + for (int i = 0; i < GENERAL.size(); ++i) + { + if (GENERAL[i]->name == _name_number) + { return GENERAL[i]; } } - + return NULL; } -general* ClassFlowCNNGeneral::GetGENERAL(string _name, bool _create = true) { - string _analog, _roi; +general *ClassFlowCNNGeneral::GetGENERAL(std::string _name, bool _create = true) +{ + std::string _number_sequence, _roi_name; int _pospunkt = _name.find_first_of("."); - if (_pospunkt > -1) { - _analog = _name.substr(0, _pospunkt); - _roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1); + if (_pospunkt > -1) + { + _number_sequence = _name.substr(0, _pospunkt); + _roi_name = _name.substr(_pospunkt + 1, _name.length() - _pospunkt - 1); } - else { - _analog = "default"; - _roi = _name; + else + { + _number_sequence = "default"; + _roi_name = _name; } general *_ret = NULL; - for (int i = 0; i < GENERAL.size(); ++i) { - if (GENERAL[i]->name == _analog) { + for (int i = 0; i < GENERAL.size(); ++i) + { + if (GENERAL[i]->name == _number_sequence) + { _ret = GENERAL[i]; } } // not found and should not be created - if (!_create) { + if (!_create) + { return _ret; } - if (_ret == NULL) { + if (_ret == NULL) + { _ret = new general; - _ret->name = _analog; + _ret->name = _number_sequence; GENERAL.push_back(_ret); } - roi* neuroi = new roi; - neuroi->name = _roi; + roi *new_roi = new roi; + new_roi->name = _roi_name; - _ret->ROI.push_back(neuroi); + _ret->ROI.push_back(new_roi); - ESP_LOGD(TAG, "GetGENERAL - GENERAL %s - roi %s - CCW: %d", _analog.c_str(), _roi.c_str(), neuroi->CCW); + ESP_LOGD(TAG, "GetGENERAL - GENERAL %s - roi %s - ccw: %d", _number_sequence.c_str(), _roi_name.c_str(), new_roi->ccw); return _ret; } -string ClassFlowCNNGeneral::getHTMLSingleStep(string host) { - string result, zw; - std::vector htmlinfo; +std::string ClassFlowCNNGeneral::getHTMLSingleStep(std::string host) +{ + std::vector html_info; - result = "

Found ROIs:

\n"; + std::string result = "

Found ROIs:

\n"; result = result + "Analog Pointers:

"; - htmlinfo = GetHTMLInfo(); - - for (int i = 0; i < htmlinfo.size(); ++i) { - std::stringstream stream; - stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val; - zw = stream.str(); + html_info = GetHTMLInfo(); - result = result + "filename + "\"> " + zw; - delete htmlinfo[i]; + for (int i = 0; i < html_info.size(); ++i) + { + std::stringstream stream; + stream << std::fixed << std::setprecision(1) << html_info[i]->val; + std::string temp_string = stream.str(); + + result = result + "filename + "\"> " + temp_string; + delete html_info[i]; } - - htmlinfo.clear(); + + html_info.clear(); return result; } -bool ClassFlowCNNGeneral::doFlow(string time) { -#ifdef HEAP_TRACING_CLASS_FLOW_CNN_GENERAL_DO_ALING_AND_CUT - //register a buffer to record the memory trace - ESP_ERROR_CHECK( heap_trace_init_standalone(trace_record, NUM_RECORDS) ); - // start tracing - ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) ); -#endif - - if (disabled) { - return true; +bool ClassFlowCNNGeneral::doFlow(std::string time_value) +{ + if (disabled) + { + return true; } - if (!doAlignAndCut(time)){ + if (!doAlignAndCut(time_value)) + { return false; } LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doFlow after alignment"); - - doNeuralNetwork(time); - + doNeuralNetwork(time_value); RemoveOldLogs(); -#ifdef HEAP_TRACING_CLASS_FLOW_CNN_GENERAL_DO_ALING_AND_CUT - ESP_ERROR_CHECK( heap_trace_stop() ); - heap_trace_dump(); -#endif - return true; } -bool ClassFlowCNNGeneral::doAlignAndCut(string time) { - if (disabled) { +bool ClassFlowCNNGeneral::doAlignAndCut(std::string time_value) +{ + if (disabled) + { return true; } - CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage(); + CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage(); - for (int _ana = 0; _ana < GENERAL.size(); ++_ana) { - for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) { + for (int _ana = 0; _ana < GENERAL.size(); ++_ana) + { + for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) + { ESP_LOGD(TAG, "General %d - Align&Cut", i); - - caic->CutAndSave(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, GENERAL[_ana]->ROI[i]->image_org); - if (SaveAllFiles) { - if (GENERAL[_ana]->name == "default") { - GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg")); - } - else { - GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg")); - } - } - GENERAL[_ana]->ROI[i]->image_org->Resize(modelxsize, modelysize, GENERAL[_ana]->ROI[i]->image); - if (SaveAllFiles) { - if (GENERAL[_ana]->name == "default") { - GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg")); + caic->CutAndSave(GENERAL[_ana]->ROI[i]->pos_x, GENERAL[_ana]->ROI[i]->pos_y, GENERAL[_ana]->ROI[i]->delta_x, GENERAL[_ana]->ROI[i]->delta_y, GENERAL[_ana]->ROI[i]->image_org); + if (Camera.SaveAllFiles) + { + if (GENERAL[_ana]->name == "default") + { + GENERAL[_ana]->ROI[i]->image_org->SaveToFile(format_filename("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg")); } - else { - GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg")); + else + { + GENERAL[_ana]->ROI[i]->image_org->SaveToFile(format_filename("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg")); } - } + } + + GENERAL[_ana]->ROI[i]->image_org->Resize(model_x_size, model_y_size, GENERAL[_ana]->ROI[i]->image); + if (Camera.SaveAllFiles) + { + if (GENERAL[_ana]->name == "default") + { + GENERAL[_ana]->ROI[i]->image->SaveToFile(format_filename("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg")); + } + else + { + GENERAL[_ana]->ROI[i]->image->SaveToFile(format_filename("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg")); + } + } } } return true; -} +} -void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw) { - if (_zw->ImageOkay()) { - if (CNNType == Analogue || CNNType == Analogue100) { +void ClassFlowCNNGeneral::DrawROI(CImageBasis *Image) +{ + if (Image->ImageOkay()) + { + if (CNNType == Analogue || CNNType == Analogue100) + { int r = 0; int g = 255; int b = 0; - for (int _ana = 0; _ana < GENERAL.size(); ++_ana) { - for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) { - _zw->drawRect(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, r, g, b, 1); - _zw->drawEllipse( (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) (GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2); - _zw->drawLine((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) GENERAL[_ana]->ROI[i]->posy, (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay), r, g, b, 2); - _zw->drawLine((int) GENERAL[_ana]->ROI[i]->posx, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2); + for (int _ana = 0; _ana < GENERAL.size(); ++_ana) + { + for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) + { + Image->drawRect(GENERAL[_ana]->ROI[i]->pos_x, GENERAL[_ana]->ROI[i]->pos_y, GENERAL[_ana]->ROI[i]->delta_x, GENERAL[_ana]->ROI[i]->delta_y, r, g, b, 1); + Image->drawEllipse((int)(GENERAL[_ana]->ROI[i]->pos_x + GENERAL[_ana]->ROI[i]->delta_x / 2), (int)(GENERAL[_ana]->ROI[i]->pos_y + GENERAL[_ana]->ROI[i]->delta_y / 2), (int)(GENERAL[_ana]->ROI[i]->delta_x / 2), (int)(GENERAL[_ana]->ROI[i]->delta_y / 2), r, g, b, 2); + Image->drawLine((int)(GENERAL[_ana]->ROI[i]->pos_x + GENERAL[_ana]->ROI[i]->delta_x / 2), (int)GENERAL[_ana]->ROI[i]->pos_y, (int)(GENERAL[_ana]->ROI[i]->pos_x + GENERAL[_ana]->ROI[i]->delta_x / 2), (int)(GENERAL[_ana]->ROI[i]->pos_y + GENERAL[_ana]->ROI[i]->delta_y), r, g, b, 2); + Image->drawLine((int)GENERAL[_ana]->ROI[i]->pos_x, (int)(GENERAL[_ana]->ROI[i]->pos_y + GENERAL[_ana]->ROI[i]->delta_y / 2), (int)GENERAL[_ana]->ROI[i]->pos_x + GENERAL[_ana]->ROI[i]->delta_x, (int)(GENERAL[_ana]->ROI[i]->pos_y + GENERAL[_ana]->ROI[i]->delta_y / 2), r, g, b, 2); } } } - else { - for (int _dig = 0; _dig < GENERAL.size(); ++_dig) { - for (int i = 0; i < GENERAL[_dig]->ROI.size(); ++i) { - _zw->drawRect(GENERAL[_dig]->ROI[i]->posx, GENERAL[_dig]->ROI[i]->posy, GENERAL[_dig]->ROI[i]->deltax, GENERAL[_dig]->ROI[i]->deltay, 0, 0, (255 - _dig*100), 2); + else + { + for (int _dig = 0; _dig < GENERAL.size(); ++_dig) + { + for (int i = 0; i < GENERAL[_dig]->ROI.size(); ++i) + { + Image->drawRect(GENERAL[_dig]->ROI[i]->pos_x, GENERAL[_dig]->ROI[i]->pos_y, GENERAL[_dig]->ROI[i]->delta_x, GENERAL[_dig]->ROI[i]->delta_y, 0, 0, (255 - _dig * 100), 2); } } } } -} +} -bool ClassFlowCNNGeneral::getNetworkParameter() { - if (disabled) { +bool ClassFlowCNNGeneral::getNetworkParameter(void) +{ + if (disabled) + { return true; } - CTfLiteClass *tflite = new CTfLiteClass; - string zwcnn = "/sdcard" + cnnmodelfile; - zwcnn = FormatFileName(zwcnn); - ESP_LOGD(TAG, "%s", zwcnn.c_str()); - - if (!tflite->LoadModel(zwcnn)) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't load tflite model " + cnnmodelfile + " -> Init aborted!"); + std::string temp_cnn = "/sdcard" + cnn_model_file; + temp_cnn = format_filename(temp_cnn); + ESP_LOGD(TAG, "%s", temp_cnn.c_str()); + + CTfLiteClass *tflite = new CTfLiteClass; + + if (!tflite->LoadModel(temp_cnn)) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't load tflite model " + cnn_model_file + " -> Init aborted!"); LogFile.WriteHeapInfo("getNetworkParameter-LoadModel"); delete tflite; return false; - } + } - if (!tflite->MakeAllocate()) { + if (!tflite->MakeAllocate()) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tflite model -> Init aborted!"); LogFile.WriteHeapInfo("getNetworkParameter-MakeAllocate"); delete tflite; return false; } - if (CNNType == AutoDetect) { + if (CNNType == AutoDetect) + { tflite->GetInputDimension(false); - modelxsize = tflite->ReadInputDimenstion(0); - modelysize = tflite->ReadInputDimenstion(1); - modelchannel = tflite->ReadInputDimenstion(2); - int _anzoutputdimensions = tflite->GetAnzOutPut(); - switch (_anzoutputdimensions) { - case 2: - CNNType = Analogue; - ESP_LOGD(TAG, "TFlite-Type set to Analogue"); - break; - case 10: - CNNType = DoubleHyprid10; - ESP_LOGD(TAG, "TFlite-Type set to DoubleHyprid10"); - break; - case 11: - CNNType = Digit; + model_x_size = tflite->ReadInputDimenstion(0); + model_y_size = tflite->ReadInputDimenstion(1); + model_channel = tflite->ReadInputDimenstion(2); + + int anz_output_dimensions = tflite->GetAnzOutPut(); + switch (anz_output_dimensions) + { + case 2: + CNNType = Analogue; + ESP_LOGD(TAG, "TFlite-Type set to Analogue"); + break; + case 10: + CNNType = DoubleHyprid10; + ESP_LOGD(TAG, "TFlite-Type set to DoubleHyprid10"); + break; + case 11: + CNNType = Digit; + ESP_LOGD(TAG, "TFlite-Type set to Digit"); + break; + // case 20: + // CNNType = DigitHyprid10; + // ESP_LOGD(TAG, "TFlite-Type set to DigitHyprid10"); + // break; + // case 22: + // CNNType = DigitHyprid; + // ESP_LOGD(TAG, "TFlite-Type set to DigitHyprid"); + // break; + case 100: + if (model_x_size == 32 && model_y_size == 32) + { + CNNType = Analogue100; + ESP_LOGD(TAG, "TFlite-Type set to Analogue100"); + } + else + { + CNNType = Digit100; ESP_LOGD(TAG, "TFlite-Type set to Digit"); - break; -/* case 20: - CNNType = DigitHyprid10; - ESP_LOGD(TAG, "TFlite-Type set to DigitHyprid10"); - break; -*/ -// case 22: -// CNNType = DigitHyprid; -// ESP_LOGD(TAG, "TFlite-Type set to DigitHyprid"); -// break; - case 100: - if (modelxsize==32 && modelysize == 32) { - CNNType = Analogue100; - ESP_LOGD(TAG, "TFlite-Type set to Analogue100"); - } - else { - CNNType = Digit100; - ESP_LOGD(TAG, "TFlite-Type set to Digit"); - } - break; - default: - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "tflite does not fit the firmware (outout_dimension=" + std::to_string(_anzoutputdimensions) + ")"); + } + break; + default: + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "tflite does not fit the firmware (outout_dimension=" + std::to_string(anz_output_dimensions) + ")"); } } @@ -633,321 +733,574 @@ bool ClassFlowCNNGeneral::getNetworkParameter() { return true; } -bool ClassFlowCNNGeneral::doNeuralNetwork(string time) { - if (disabled) { - return true; +// wird von "bool ClassFlowCNNGeneral::doFlow(std::string time_value)" aufgerufen +bool ClassFlowCNNGeneral::doNeuralNetwork(std::string time_value) +{ + if (disabled) + { + return false; } - string logPath = CreateLogFolder(time); + std::string cnn_model = "/sdcard" + cnn_model_file; + cnn_model = format_filename(cnn_model); + ESP_LOGD(TAG, "%s", cnn_model.c_str()); - CTfLiteClass *tflite = new CTfLiteClass; - string zwcnn = "/sdcard" + cnnmodelfile; - zwcnn = FormatFileName(zwcnn); - ESP_LOGD(TAG, "%s", zwcnn.c_str()); + CTfLiteClass *tflite = new CTfLiteClass; - if (!tflite->LoadModel(zwcnn)) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't load tflite model " + cnnmodelfile + " -> Exec aborted this round!"); + if (!tflite->LoadModel(cnn_model)) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't load tflite model " + cnn_model_file + " -> Exec aborted this round!"); LogFile.WriteHeapInfo("doNeuralNetwork-LoadModel"); delete tflite; return false; } - if (!tflite->MakeAllocate()) { + if (!tflite->MakeAllocate()) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tfilte model -> Exec aborted this round!"); LogFile.WriteHeapInfo("doNeuralNetwork-MakeAllocate"); delete tflite; return false; } + std::string logPath = CreateLogFolder(time_value); + + std::vector numbers = flowctrl.getNumbers(); + + time_t _imagetime; // in seconds + time(&_imagetime); + localtime(&_imagetime); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doNeuralNetwork, _imagetime: " + std::to_string(_imagetime)); + // For each NUMBER - for (int n = 0; n < GENERAL.size(); ++n) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Processing Number '" + GENERAL[n]->name + "'"); + for (int j = 0; j < GENERAL.size(); ++j) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Processing Number '" + GENERAL[j]->name + "'"); + + int start_roi = 0; + + if ((numbers[j]->useMaxFlowRate) && (numbers[j]->PreValueOkay) && (numbers[j]->timeStampLastValue == numbers[j]->timeStampLastPreValue)) + { + int _AnzahlDigit = numbers[j]->AnzahlDigit; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doNeuralNetwork, _AnzahlDigit: " + std::to_string(_AnzahlDigit)); + + int _AnzahlAnalog = numbers[j]->AnzahlAnalog; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doNeuralNetwork, _AnzahlAnalog: " + std::to_string(_AnzahlAnalog)); + + float _MaxFlowRate = (numbers[j]->MaxFlowRate / 60); // in unit/minutes + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doNeuralNetwork, _MaxFlowRate: " + std::to_string(_MaxFlowRate)); + + float _LastPreValueTimeDifference = (float)((difftime(_imagetime, numbers[j]->timeStampLastPreValue)) / 60); // in minutes + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doNeuralNetwork, _LastPreValueTimeDifference: " + std::to_string(_LastPreValueTimeDifference) + " minutes"); + + std::string _PreValue_old = std::to_string((float)numbers[j]->PreValue); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doNeuralNetwork, _PreValue_old: " + _PreValue_old); + + //////////////////////////////////////////////////// + std::string _PreValue_new1 = std::to_string((float)numbers[j]->PreValue + (_MaxFlowRate * _LastPreValueTimeDifference)); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doNeuralNetwork, _PreValue_new1: " + _PreValue_new1); + + std::string _PreValue_new2 = std::to_string((float)numbers[j]->PreValue - (_MaxFlowRate * _LastPreValueTimeDifference)); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doNeuralNetwork, _PreValue_new2: " + _PreValue_new2); + + //////////////////////////////////////////////////// + // is necessary because there are always 6 numbers after the DecimalPoint due to float + int _CorrectionValue = 0; + int _pospunkt = _PreValue_old.find_first_of("."); + if (_pospunkt > -1) + { + _CorrectionValue = _PreValue_old.length() - _pospunkt; + _CorrectionValue = _CorrectionValue - numbers[j]->Nachkomma; + } + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doNeuralNetwork, _CorrectionValue: " + std::to_string(_CorrectionValue)); + + int _PreValue_len = ((int)_PreValue_old.length() - _CorrectionValue); + if (numbers[j]->isExtendedResolution) + { + _PreValue_len = _PreValue_len - 1; + } + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "doNeuralNetwork, _PreValue_len(without DecimalPoint and ExtendedResolution): " + std::to_string(_PreValue_len)); + + //////////////////////////////////////////////////// + // (+) Find out which Numbers should not change + int _DecimalPoint1 = 0; + int _NumbersNotChanged1 = 0; + while ((_PreValue_old.length() > _NumbersNotChanged1) && (_PreValue_old[_NumbersNotChanged1] == _PreValue_new1[_NumbersNotChanged1])) + { + if (_PreValue_old[_NumbersNotChanged1] == '.') + { + _DecimalPoint1 = 1; + } + _NumbersNotChanged1++; + } + _NumbersNotChanged1 = _NumbersNotChanged1 - _DecimalPoint1; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Number of ROIs that should not change: " + std::to_string(_NumbersNotChanged1)); + + if ((_AnzahlDigit + _AnzahlAnalog) > _PreValue_len) + { + _NumbersNotChanged1 = _NumbersNotChanged1 + ((_AnzahlDigit + _AnzahlAnalog) - _PreValue_len); + } + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Number of ROIs that should not change(corrected): " + std::to_string(_NumbersNotChanged1)); + + //////////////////////////////////////////////////// + // (-) Find out which Numbers should not change + int _NumbersNotChanged2 = _NumbersNotChanged1; + if (numbers[j]->AllowNegativeRates) + { + int _DecimalPoint2 = 0; + while ((_PreValue_old.length() > _NumbersNotChanged2) && (_PreValue_old[_NumbersNotChanged2] == _PreValue_new2[_NumbersNotChanged2])) + { + if (_PreValue_old[_NumbersNotChanged2] == '.') + { + _DecimalPoint2 = 1; + } + _NumbersNotChanged2++; + } + _NumbersNotChanged2 = _NumbersNotChanged2 - _DecimalPoint2; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Number of ROIs that should not change: " + std::to_string(_NumbersNotChanged2)); + + if ((_AnzahlDigit + _AnzahlAnalog) > _PreValue_len) + { + _NumbersNotChanged2 = _NumbersNotChanged2 + ((_AnzahlDigit + _AnzahlAnalog) - _PreValue_len); + } + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Number of ROIs that should not change(corrected): " + std::to_string(_NumbersNotChanged2)); + } + + //////////////////////////////////////////////////// + int start_digit_new = 0; + int start_analog_new = 0; + int _NumbersNotChanged = min(_NumbersNotChanged1, _NumbersNotChanged2); + if (_NumbersNotChanged <= _AnzahlDigit) + { + // The change already takes place at the digit ROIs + start_digit_new = (_AnzahlDigit - _NumbersNotChanged); + start_digit_new = (_AnzahlDigit - start_digit_new); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "From the " + std::to_string(start_digit_new) + " th digit ROI is evaluated"); + } + else + { + // The change only takes place at the analog ROIs + start_digit_new = _AnzahlDigit; + start_analog_new = (_AnzahlAnalog - (_NumbersNotChanged - _AnzahlDigit)); + start_analog_new = (_AnzahlAnalog - start_analog_new); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "From the " + std::to_string(start_analog_new) + " th analog ROI is evaluated"); + } + + //////////////////////////////////////////////////// + if (CNNType == Digit || CNNType == Digit100 || CNNType == DoubleHyprid10) + { + start_roi = start_digit_new; + } + else if (CNNType == Analogue || CNNType == Analogue100) + { + start_roi = start_analog_new; + } + } + // For each ROI - for (int roi = 0; roi < GENERAL[n]->ROI.size(); ++roi) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "ROI #" + std::to_string(roi) + " - TfLite"); - //ESP_LOGD(TAG, "General %d - TfLite", i); + for (int i = 0; i < GENERAL[j]->ROI.size(); ++i) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "ROI #" + std::to_string(i) + " - TfLite"); - switch (CNNType) { - case Analogue: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CNN Type: Analogue"); + switch (CNNType) + { + case Analogue: + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CNN Type: Analogue"); + + tflite->LoadInputImageBasis(GENERAL[j]->ROI[i]->image); + + tflite->Invoke(); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Analogue - After Invoke"); + + float _value1 = tflite->GetOutputValue(0); + float _value2 = tflite->GetOutputValue(1); + + float _result = fmod(atan2(_value1, _value2) / (M_PI * 2) + 2, 1); + + if (GENERAL[j]->ROI[i]->ccw) + { + _result = 10 - (_result * 10); + } + else + { + _result = _result * 10; + } + + if (i >= start_roi) + { + GENERAL[j]->ROI[i]->result_float = _result; + } + + GENERAL[j]->ROI[i]->raw_result_float = _result; + + if ((_result < 0.0f) || (_result >= 10.0f)) + { + GENERAL[j]->ROI[i]->isReject = true; + } + else + { + GENERAL[j]->ROI[i]->isReject = false; + } + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "General result (Analog) - roi_" + std::to_string(i) + ": " + std::to_string(GENERAL[j]->ROI[i]->raw_result_float)); + ESP_LOGD(TAG, "General result (Analog) - roi_%i - ccw: %d - %f", i, GENERAL[j]->ROI[i]->ccw, GENERAL[j]->ROI[i]->raw_result_float); + + if (isLogImage) + { + std::string _image_name = GENERAL[j]->name + "_" + GENERAL[j]->ROI[i]->name; + + if (isLogImageSelect) { - float f1, f2; - f1 = 0; f2 = 0; - - tflite->LoadInputImageBasis(GENERAL[n]->ROI[roi]->image); - tflite->Invoke(); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "After Invoke"); - - f1 = tflite->GetOutputValue(0); - f2 = tflite->GetOutputValue(1); - float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1); - - if(GENERAL[n]->ROI[roi]->CCW) { - GENERAL[n]->ROI[roi]->result_float = 10 - (result * 10); + if (LogImageSelect.find(GENERAL[j]->ROI[i]->name) != std::string::npos) + { + LogImage(logPath, _image_name, &GENERAL[j]->ROI[i]->raw_result_float, NULL, time_value, GENERAL[j]->ROI[i]->image_org); } - else { - GENERAL[n]->ROI[roi]->result_float = result * 10; - } - - ESP_LOGD(TAG, "General result (Analog)%i - CCW: %d - %f", roi, GENERAL[n]->ROI[roi]->CCW, GENERAL[n]->ROI[roi]->result_float); - if (isLogImage) { - LogImage(logPath, GENERAL[n]->ROI[roi]->name, &GENERAL[n]->ROI[roi]->result_float, NULL, time, GENERAL[n]->ROI[roi]->image_org); - } - } break; + } + } + } + break; - case Digit: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CNN Type: Digit"); + case Digit: + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CNN Type: Digit"); + + int _result = tflite->GetClassFromImageBasis(GENERAL[j]->ROI[i]->image); + + if (i >= start_roi) + { + GENERAL[j]->ROI[i]->result_klasse = _result; + } + + GENERAL[j]->ROI[i]->raw_result_klasse = _result; + + if ((_result < 0) || (_result >= 10)) + { + GENERAL[j]->ROI[i]->isReject = true; + } + else + { + GENERAL[j]->ROI[i]->isReject = false; + } + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "General result (Digit) - roi_" + std::to_string(i) + ": " + std::to_string(GENERAL[j]->ROI[i]->raw_result_klasse)); + ESP_LOGD(TAG, "General result (Digit) - roi_%i: %d", i, GENERAL[j]->ROI[i]->raw_result_klasse); + + if (isLogImage) + { + std::string _image_name = GENERAL[j]->name + "_" + GENERAL[j]->ROI[i]->name; + + if (isLogImageSelect) { - GENERAL[n]->ROI[roi]->result_klasse = 0; - GENERAL[n]->ROI[roi]->result_klasse = tflite->GetClassFromImageBasis(GENERAL[n]->ROI[roi]->image); - ESP_LOGD(TAG, "General result (Digit)%i: %d", roi, GENERAL[n]->ROI[roi]->result_klasse); - - if (isLogImage) { - string _imagename = GENERAL[n]->name + "_" + GENERAL[n]->ROI[roi]->name; - if (isLogImageSelect) { - if (LogImageSelect.find(GENERAL[n]->ROI[roi]->name) != std::string::npos) { - LogImage(logPath, _imagename, NULL, &GENERAL[n]->ROI[roi]->result_klasse, time, GENERAL[n]->ROI[roi]->image_org); - } - } - else { - LogImage(logPath, _imagename, NULL, &GENERAL[n]->ROI[roi]->result_klasse, time, GENERAL[n]->ROI[roi]->image_org); - } + if (LogImageSelect.find(GENERAL[j]->ROI[i]->name) != std::string::npos) + { + LogImage(logPath, _image_name, NULL, &GENERAL[j]->ROI[i]->raw_result_klasse, time_value, GENERAL[j]->ROI[i]->image_org); } - } break; + } + } + } + break; - case DoubleHyprid10: + case DoubleHyprid10: + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CNN Type: DoubleHyprid10"); + + tflite->LoadInputImageBasis(GENERAL[j]->ROI[i]->image); + + tflite->Invoke(); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "DoubleHyprid10 - After Invoke"); + + int _num = tflite->GetOutClassification(0, 9); + int _numplus = (_num + 1) % 10; + int _numminus = (_num - 1 + 10) % 10; + + float _value = tflite->GetOutputValue(_num); + float _valueplus = tflite->GetOutputValue(_numplus); + float _valueminus = tflite->GetOutputValue(_numminus); + + float _result = (float)_num - _valueminus / (_value + _valueminus); + float _fit = _value + _valueminus; + + if (_valueplus > _valueminus) + { + _result = (float)_num + _valueplus / (_valueplus + _value); + _fit = _value + _valueplus; + } + + std::string temp_bufer = "DoubleHyprid10 - _num (plus, minus): " + std::to_string(_num) + " (" + std::to_string(_numplus) + ", " + std::to_string(_numminus) + ")"; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, temp_bufer); + + temp_bufer = "DoubleHyprid10 - _val (plus, minus): " + std::to_string(_value) + " (" + std::to_string(_valueplus) + ", " + std::to_string(_valueminus) + ")"; + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, temp_bufer); + + temp_bufer = "DoubleHyprid10 - _result: " + std::to_string(_result) + ", _fit: " + std::to_string(_fit); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, temp_bufer); + + float _result_save_file = _result; + + if (_fit < CNNGoodThreshold) + { + GENERAL[j]->ROI[i]->isReject = true; + _result = -1; + temp_bufer = "DoubleHyprid10 - Value Rejected due to Threshold (Fit: " + std::to_string(_fit) + ", Threshold: " + std::to_string(CNNGoodThreshold) + ")"; + LogFile.WriteToFile(ESP_LOG_WARN, TAG, temp_bufer); + } + else + { + GENERAL[j]->ROI[i]->isReject = false; + } + + if (GENERAL[j]->ROI[i]->ccw) + { + } + + if (i >= start_roi) + { + GENERAL[j]->ROI[i]->result_float = _result; + } + + GENERAL[j]->ROI[i]->raw_result_float = _result; + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Result General(DoubleHyprid10) - roi_" + std::to_string(i) + ": " + std::to_string(GENERAL[j]->ROI[i]->raw_result_float)); + ESP_LOGD(TAG, "Result General(DoubleHyprid10) - roi_%i: %f", i, GENERAL[j]->ROI[i]->raw_result_float); + + if (isLogImage) + { + std::string _image_name = GENERAL[j]->name + "_" + GENERAL[j]->ROI[i]->name; + + if (isLogImageSelect) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CNN Type: DoubleHyprid10"); - int _num, _numplus, _numminus; - float _val, _valplus, _valminus; - float _fit; - float _result_save_file; - - tflite->LoadInputImageBasis(GENERAL[n]->ROI[roi]->image); - tflite->Invoke(); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "After Invoke"); - - _num = tflite->GetOutClassification(0, 9); - _numplus = (_num + 1) % 10; - _numminus = (_num - 1 + 10) % 10; - - _val = tflite->GetOutputValue(_num); - _valplus = tflite->GetOutputValue(_numplus); - _valminus = tflite->GetOutputValue(_numminus); - - float result = _num; - - if (_valplus > _valminus) { - result = result + _valplus / (_valplus + _val); - _fit = _val + _valplus; - } - else { - result = result - _valminus / (_val + _valminus); - _fit = _val + _valminus; - } - - if (result >= 10) { - result = result - 10; - } - - if (result < 0) { - result = result + 10; + if (LogImageSelect.find(GENERAL[j]->ROI[i]->name) != std::string::npos) + { + LogImage(logPath, _image_name, &_result_save_file, NULL, time_value, GENERAL[j]->ROI[i]->image_org); } + } + } + } + break; - string zw = "_num (p, m): " + to_string(_num) + " " + to_string(_numplus) + " " + to_string(_numminus); - zw = zw + " _val (p, m): " + to_string(_val) + " " + to_string(_valplus) + " " + to_string(_valminus); - zw = zw + " result: " + to_string(result) + " _fit: " + to_string(_fit); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw); + case Digit100: + case Analogue100: + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CNN Type: Digit100 or Analogue100"); - _result_save_file = result; + tflite->LoadInputImageBasis(GENERAL[j]->ROI[i]->image); + tflite->Invoke(); - if (_fit < CNNGoodThreshold) { - GENERAL[n]->ROI[roi]->isReject = true; - result = -1; - _result_save_file+= 100; // In case fit is not sufficient, the result should still be saved with "-10x.y". - string zw = "Value Rejected due to Threshold (Fit: " + to_string(_fit) + ", Threshold: " + to_string(CNNGoodThreshold) + ")"; - LogFile.WriteToFile(ESP_LOG_WARN, TAG, zw); - } - else { - GENERAL[n]->ROI[roi]->isReject = false; - } + int _num = tflite->GetOutClassification(); + float _result = 0.0f; - GENERAL[n]->ROI[roi]->result_float = result; - ESP_LOGD(TAG, "Result General(Analog)%i: %f", roi, GENERAL[n]->ROI[roi]->result_float); + if (GENERAL[j]->ROI[i]->ccw) + { + _result = 10 - ((float)_num / 10.0); + } + else + { + _result = (float)_num / 10.0; + } - if (isLogImage) { - string _imagename = GENERAL[n]->name + "_" + GENERAL[n]->ROI[roi]->name; - if (isLogImageSelect) { - if (LogImageSelect.find(GENERAL[n]->ROI[roi]->name) != std::string::npos) { - LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[n]->ROI[roi]->image_org); - } - } - else { - LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[n]->ROI[roi]->image_org); - } - } - } break; - case Digit100: - case Analogue100: + if (i >= start_roi) + { + GENERAL[j]->ROI[i]->result_float = _result; + } + + GENERAL[j]->ROI[i]->raw_result_float = _result; + + if ((_result < 0.0f) || (_result >= 10.0f)) + { + GENERAL[j]->ROI[i]->isReject = true; + } + else + { + GENERAL[j]->ROI[i]->isReject = false; + } + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Result General(Digit100 or Analogue100) - roi_" + std::to_string(i) + ": " + std::to_string(GENERAL[j]->ROI[i]->raw_result_float)); + ESP_LOGD(TAG, "Result General(Digit100 or Analogue100) - roi_%i - ccw: %d - %f", i, GENERAL[j]->ROI[i]->ccw, GENERAL[j]->ROI[i]->raw_result_float); + + if (isLogImage) + { + std::string _image_name = GENERAL[j]->name + "_" + GENERAL[j]->ROI[i]->name; + + if (isLogImageSelect) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CNN Type: Digit100 or Analogue100"); - int _num; - float _result_save_file; - - tflite->LoadInputImageBasis(GENERAL[n]->ROI[roi]->image); - tflite->Invoke(); - - _num = tflite->GetOutClassification(); - - if(GENERAL[n]->ROI[roi]->CCW) { - GENERAL[n]->ROI[roi]->result_float = 10 - ((float)_num / 10.0); - } - else { - GENERAL[n]->ROI[roi]->result_float = (float)_num / 10.0; + if (LogImageSelect.find(GENERAL[j]->ROI[i]->name) != std::string::npos) + { + LogImage(logPath, _image_name, &GENERAL[j]->ROI[i]->raw_result_float, NULL, time_value, GENERAL[j]->ROI[i]->image_org); } + } + } + } + break; - _result_save_file = GENERAL[n]->ROI[roi]->result_float; - - GENERAL[n]->ROI[roi]->isReject = false; - - ESP_LOGD(TAG, "Result General(Analog)%i - CCW: %d - %f", roi, GENERAL[n]->ROI[roi]->CCW, GENERAL[n]->ROI[roi]->result_float); - - if (isLogImage) { - string _imagename = GENERAL[n]->name + "_" + GENERAL[n]->ROI[roi]->name; - if (isLogImageSelect) { - if (LogImageSelect.find(GENERAL[n]->ROI[roi]->name) != std::string::npos) { - LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[n]->ROI[roi]->image_org); - } - } - else { - LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[n]->ROI[roi]->image_org); - } - } - - } break; - - default: - break; + default: + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CNN Type: unknown"); + } + break; } } } delete tflite; - return true; } -bool ClassFlowCNNGeneral::isExtendedResolution(int _number) { - if (CNNType == Digit) { +bool ClassFlowCNNGeneral::isExtendedResolution(int _number) +{ + if (CNNType == Digit) + { return false; } - + return true; } -std::vector ClassFlowCNNGeneral::GetHTMLInfo() { - std::vector result; +std::vector ClassFlowCNNGeneral::GetHTMLInfo(void) +{ + std::vector result; - for (int _ana = 0; _ana < GENERAL.size(); ++_ana) { - for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) { - ESP_LOGD(TAG, "Image: %d", (int) GENERAL[_ana]->ROI[i]->image); - if (GENERAL[_ana]->ROI[i]->image) { - if (GENERAL[_ana]->name == "default") { - GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg")); + for (int _ana = 0; _ana < GENERAL.size(); ++_ana) + { + for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i) + { + ESP_LOGD(TAG, "Image: %d", (int)GENERAL[_ana]->ROI[i]->image); + if (GENERAL[_ana]->ROI[i]->image) + { + if (GENERAL[_ana]->name == "default") + { + GENERAL[_ana]->ROI[i]->image->SaveToFile(format_filename("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg")); } - else { - GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg")); + else + { + GENERAL[_ana]->ROI[i]->image->SaveToFile(format_filename("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg")); } } - HTMLInfo *zw = new HTMLInfo; - if (GENERAL[_ana]->name == "default") { - zw->filename = GENERAL[_ana]->ROI[i]->name + ".jpg"; - zw->filename_org = GENERAL[_ana]->ROI[i]->name + ".jpg"; + HTMLInfo *temp_info = new HTMLInfo; + if (GENERAL[_ana]->name == "default") + { + temp_info->filename = GENERAL[_ana]->ROI[i]->name + ".jpg"; + temp_info->filename_org = GENERAL[_ana]->ROI[i]->name + ".jpg"; } - else { - zw->filename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg"; - zw->filename_org = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg"; + else + { + temp_info->filename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg"; + temp_info->filename_org = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg"; } - if (CNNType == Digit) { - zw->val = GENERAL[_ana]->ROI[i]->result_klasse; + if (CNNType == Digit) + { + temp_info->val = (float)GENERAL[_ana]->ROI[i]->raw_result_klasse; } - else { - zw->val = GENERAL[_ana]->ROI[i]->result_float; + else + { + temp_info->val = GENERAL[_ana]->ROI[i]->raw_result_float; } - - zw->image = GENERAL[_ana]->ROI[i]->image; - zw->image_org = GENERAL[_ana]->ROI[i]->image_org; - result.push_back(zw); + temp_info->image = GENERAL[_ana]->ROI[i]->image; + temp_info->image_org = GENERAL[_ana]->ROI[i]->image_org; + + result.push_back(temp_info); } } return result; } -int ClassFlowCNNGeneral::getNumberGENERAL() { +int ClassFlowCNNGeneral::getNumberGENERAL(void) +{ return GENERAL.size(); } -string ClassFlowCNNGeneral::getNameGENERAL(int _analog) { - if (_analog < GENERAL.size()) { - return GENERAL[_analog]->name; +std::string ClassFlowCNNGeneral::getNameGENERAL(int _number) +{ + if (_number < GENERAL.size()) + { + return GENERAL[_number]->name; } return "GENERAL DOES NOT EXIST"; } -general* ClassFlowCNNGeneral::GetGENERAL(int _analog) { - if (_analog < GENERAL.size()) { - return GENERAL[_analog]; +general *ClassFlowCNNGeneral::GetGENERAL(int _number) +{ + if (_number < GENERAL.size()) + { + return GENERAL[_number]; } return NULL; } -void ClassFlowCNNGeneral::UpdateNameNumbers(std::vector *_name_numbers) { - for (int _dig = 0; _dig < GENERAL.size(); _dig++) { +void ClassFlowCNNGeneral::UpdateNameNumbers(std::vector *_name_numbers) +{ + for (int _dig = 0; _dig < GENERAL.size(); _dig++) + { std::string _name = GENERAL[_dig]->name; bool found = false; - - for (int i = 0; i < (*_name_numbers).size(); ++i) { - if ((*_name_numbers)[i] == _name) { + + for (int i = 0; i < (*_name_numbers).size(); ++i) + { + if ((*_name_numbers)[i] == _name) + { found = true; } } - if (!found) { + if (!found) + { (*_name_numbers).push_back(_name); } } } -string ClassFlowCNNGeneral::getReadoutRawString(int _analog) +std::string ClassFlowCNNGeneral::getReadoutRawString(int _number) { - string rt = ""; + std::string temp_string = ""; - if (_analog >= GENERAL.size() || GENERAL[_analog]==NULL || GENERAL[_analog]->ROI.size() == 0) { - return rt; + if (_number >= GENERAL.size() || GENERAL[_number] == NULL || GENERAL[_number]->ROI.size() == 0) + { + return temp_string; } - - for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i) { - if (CNNType == Analogue || CNNType == Analogue100) { - rt = rt + "," + RundeOutput(GENERAL[_analog]->ROI[i]->result_float, 1); - } - if (CNNType == Digit) { - if (GENERAL[_analog]->ROI[i]->result_klasse >= 10) { - rt = rt + ",N"; + for (int i = 0; i < GENERAL[_number]->ROI.size(); ++i) + { + if (CNNType == Analogue || CNNType == Analogue100) + { + if ((GENERAL[_number]->ROI[i]->raw_result_float < 0.0f) || (GENERAL[_number]->ROI[i]->raw_result_float >= 10.0f)) + { + temp_string = temp_string + ",N"; } - else { - rt = rt + "," + RundeOutput(GENERAL[_analog]->ROI[i]->result_klasse, 0); + else + { + temp_string = temp_string + "," + round_output(GENERAL[_number]->ROI[i]->raw_result_float, 1); } } - if ((CNNType == DoubleHyprid10) || (CNNType == Digit100)) { - rt = rt + "," + RundeOutput(GENERAL[_analog]->ROI[i]->result_float, 1); + else if (CNNType == Digit) + { + if ((GENERAL[_number]->ROI[i]->raw_result_klasse < 0) || (GENERAL[_number]->ROI[i]->raw_result_klasse >= 10)) + { + temp_string = temp_string + ",N"; + } + else + { + temp_string = temp_string + "," + round_output(GENERAL[_number]->ROI[i]->raw_result_klasse, 0); + } + } + + else if ((CNNType == DoubleHyprid10) || (CNNType == Digit100)) + { + if ((GENERAL[_number]->ROI[i]->raw_result_float < 0.0f) || (GENERAL[_number]->ROI[i]->raw_result_float >= 10.0f)) + { + temp_string = temp_string + ",N"; + } + else + { + temp_string = temp_string + "," + round_output(GENERAL[_number]->ROI[i]->raw_result_float, 1); + } } } - return rt; + + return temp_string; } diff --git a/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h b/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h index 9946d551..658e095b 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h +++ b/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h @@ -3,11 +3,11 @@ #ifndef CLASSFLOWCNNGENERAL_H #define CLASSFLOWCNNGENERAL_H -#include"ClassFlowDefineTypes.h" +#include "ClassFlowDefineTypes.h" #include "ClassFlowAlignment.h" - -enum t_CNNType { +enum t_CNNType +{ AutoDetect, Analogue, Analogue100, @@ -16,64 +16,68 @@ enum t_CNNType { DoubleHyprid10, Digit100, None - }; +}; -class ClassFlowCNNGeneral : - public ClassFlowImage +class ClassFlowCNNGeneral : public ClassFlowImage { protected: t_CNNType CNNType; - std::vector GENERAL; + std::vector GENERAL; + float CNNGoodThreshold; - string cnnmodelfile; - int modelxsize, modelysize, modelchannel; + std::string cnn_model_file; + + int model_x_size; + int model_y_size; + int model_channel; + bool isLogImageSelect; - string LogImageSelect; - ClassFlowAlignment* flowpostalignment; + std::string LogImageSelect; - bool SaveAllFiles; + ClassFlowAlignment *flowpostalignment; - int PointerEvalAnalogNew(float zahl, int numeral_preceder); - int PointerEvalAnalogToDigitNew(float zahl, float numeral_preceder, int eval_predecessors, float AnalogToDigitTransitionStart); - int PointerEvalHybridNew(float zahl, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors = false, float AnalogToDigitTransitionStart=9.2); + int PointerEvalAnalog(float number, int numeral_preceder); + int PointerEvalAnalogToDigit(float number, float numeral_preceder, int eval_predecessors, float AnalogToDigitTransitionStart); + int PointerEvalHybrid(float number, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors = false, float AnalogToDigitTransitionStart = 9.2); + bool doNeuralNetwork(std::string time_value); - - bool doNeuralNetwork(string time); - bool doAlignAndCut(string time); + bool doAlignAndCut(std::string time_value); bool getNetworkParameter(); public: ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype = AutoDetect); - bool ReadParameter(FILE* pfile, string& aktparamgraph); - bool doFlow(string time); + bool ReadParameter(FILE *pfile, std::string &aktparamgraph); + bool doFlow(std::string time_value); - string getHTMLSingleStep(string host); - string getReadout(int _analog, bool _extendedResolution = false, int prev = -1, float _before_narrow_Analog = -1, float AnalogToDigitTransitionStart=9.2); + std::string getHTMLSingleStep(std::string host); - string getReadoutRawString(int _analog); + std::vector getMeterValues(int _number); - void DrawROI(CImageBasis *_zw); + std::string getReadout(int _number, bool _extendedResolution = false, int prev = -1, float _before_narrow_Analog = -1, float AnalogToDigitTransitionStart = 9.2); - std::vector GetHTMLInfo(); + std::string getReadoutRawString(int _number); - int getNumberGENERAL(); - general* GetGENERAL(int _analog); - general* GetGENERAL(string _name, bool _create); - general* FindGENERAL(string _name_number); - string getNameGENERAL(int _analog); + void DrawROI(CImageBasis *Image); + + std::vector GetHTMLInfo(void); + + int getNumberGENERAL(void); + general *GetGENERAL(int _number); + general *GetGENERAL(std::string _name, bool _create); + general *FindGENERAL(std::string _name_number); + std::string getNameGENERAL(int _number); bool isExtendedResolution(int _number = 0); void UpdateNameNumbers(std::vector *_name_numbers); - t_CNNType getCNNType(){return CNNType;}; + t_CNNType getCNNType() { return CNNType; }; - string name(){return "ClassFlowCNNGeneral";}; + std::string name() { return "ClassFlowCNNGeneral"; }; }; #endif - diff --git a/code/components/jomjol_flowcontroll/ClassFlowControll.cpp b/code/components/jomjol_flowcontroll/ClassFlowControll.cpp index 54073539..be220217 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowControll.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowControll.cpp @@ -1,37 +1,27 @@ +#include "defines.h" + #include "ClassFlowControll.h" -#include "connect_wlan.h" -#include "read_wlanini.h" +#include "connect_wifi_sta.h" +#include "read_network_config.h" #include "freertos/task.h" #include - -#ifdef __cplusplus -extern "C" { -#endif #include -#ifdef __cplusplus -} -#endif #include "ClassLogFile.h" #include "time_sntp.h" #include "Helper.h" #include "server_ota.h" -#ifdef ENABLE_MQTT - #include "interface_mqtt.h" - #include "server_mqtt.h" -#endif //ENABLE_MQTT +#include "interface_mqtt.h" +#include "server_mqtt.h" #include "server_help.h" #include "MainFlowControl.h" #include "basic_auth.h" -#include "../../include/defines.h" -static const char* TAG = "FLOWCTRL"; - -//#define DEBUG_DETAIL_ON +static const char *TAG = "FLOWCTRL"; std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _host) { @@ -40,50 +30,49 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _ ESP_LOGD(TAG, "Step %s start", _stepname.c_str()); - if ((_stepname.compare("[TakeImage]") == 0) || (_stepname.compare(";[TakeImage]") == 0)) { + if ((_stepname.compare("[TakeImage]") == 0) || (_stepname.compare(";[TakeImage]") == 0)) + { _classname = "ClassFlowTakeImage"; } - - if ((_stepname.compare("[Alignment]") == 0) || (_stepname.compare(";[Alignment]") == 0)) { + else if ((_stepname.compare("[Alignment]") == 0) || (_stepname.compare(";[Alignment]") == 0)) + { _classname = "ClassFlowAlignment"; } - - if ((_stepname.compare(0, 7, "[Digits") == 0) || (_stepname.compare(0, 8, ";[Digits") == 0)) { + else if ((_stepname.compare(0, 7, "[Digits") == 0) || (_stepname.compare(0, 8, ";[Digits") == 0)) + { _classname = "ClassFlowCNNGeneral"; } - - if ((_stepname.compare("[Analog]") == 0) || (_stepname.compare(";[Analog]") == 0)) { + else if ((_stepname.compare("[Analog]") == 0) || (_stepname.compare(";[Analog]") == 0)) + { _classname = "ClassFlowCNNGeneral"; } - - #ifdef ENABLE_MQTT - if ((_stepname.compare("[MQTT]") == 0) || (_stepname.compare(";[MQTT]") == 0)) { - _classname = "ClassFlowMQTT"; - } - #endif //ENABLE_MQTT + else if ((_stepname.compare("[MQTT]") == 0) || (_stepname.compare(";[MQTT]") == 0)) + { + _classname = "ClassFlowMQTT"; + } + else if ((_stepname.compare("[InfluxDB]") == 0) || (_stepname.compare(";[InfluxDB]") == 0)) + { + _classname = "ClassFlowInfluxDB"; + } + else if ((_stepname.compare("[InfluxDBv2]") == 0) || (_stepname.compare(";[InfluxDBv2]") == 0)) + { + _classname = "ClassFlowInfluxDBv2"; + } + else if ((_stepname.compare("[Webhook]") == 0) || (_stepname.compare(";[Webhook]") == 0)) + { + _classname = "ClassFlowWebhook"; + } - #ifdef ENABLE_INFLUXDB - if ((_stepname.compare("[InfluxDB]") == 0) || (_stepname.compare(";[InfluxDB]") == 0)) { - _classname = "ClassFlowInfluxDB"; - } - if ((_stepname.compare("[InfluxDBv2]") == 0) || (_stepname.compare(";[InfluxDBv2]") == 0)) { - _classname = "ClassFlowInfluxDBv2"; - } - #endif //ENABLE_INFLUXDB - - #ifdef ENABLE_WEBHOOK - if ((_stepname.compare("[Webhook]") == 0) || (_stepname.compare(";[Webhook]") == 0)) { - _classname = "ClassFlowWebhook"; - } - #endif //ENABLE_WEBHOOK - - for (int i = 0; i < FlowControll.size(); ++i) { - if (FlowControll[i]->name().compare(_classname) == 0) { - if (!(FlowControll[i]->name().compare("ClassFlowTakeImage") == 0)) { + for (int i = 0; i < FlowControll.size(); ++i) + { + if (FlowControll[i]->name().compare(_classname) == 0) + { + if (!(FlowControll[i]->name().compare("ClassFlowTakeImage") == 0)) + { // if it is a TakeImage, the image does not need to be included, this happens automatically with the html query. FlowControll[i]->doFlow(""); } - + result = FlowControll[i]->getHTMLSingleStep(_host); } } @@ -95,71 +84,69 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _ std::string ClassFlowControll::TranslateAktstatus(std::string _input) { - if (_input.compare("ClassFlowTakeImage") == 0) { + if (_input.compare("ClassFlowTakeImage") == 0) + { return ("Take Image"); } - - if (_input.compare("ClassFlowAlignment") == 0) { + else if (_input.compare("ClassFlowAlignment") == 0) + { return ("Aligning"); } - - if (_input.compare("ClassFlowCNNGeneral") == 0) { + else if (_input.compare("ClassFlowCNNGeneral") == 0) + { return ("Digitization of ROIs"); } - - #ifdef ENABLE_MQTT - if (_input.compare("ClassFlowMQTT") == 0) { - return ("Sending MQTT"); - } - #endif //ENABLE_MQTT - - #ifdef ENABLE_INFLUXDB - if (_input.compare("ClassFlowInfluxDB") == 0) { - return ("Sending InfluxDB"); - } - - if (_input.compare("ClassFlowInfluxDBv2") == 0) { - return ("Sending InfluxDBv2"); - } - #endif //ENABLE_INFLUXDB - - #ifdef ENABLE_WEBHOOK - if (_input.compare("ClassFlowWebhook") == 0) { - return ("Sending Webhook"); - } - #endif //ENABLE_WEBHOOK - - if (_input.compare("ClassFlowPostProcessing") == 0) { + else if (_input.compare("ClassFlowMQTT") == 0) + { + return ("Sending MQTT"); + } + else if (_input.compare("ClassFlowInfluxDB") == 0) + { + return ("Sending InfluxDB"); + } + else if (_input.compare("ClassFlowInfluxDBv2") == 0) + { + return ("Sending InfluxDBv2"); + } + else if (_input.compare("ClassFlowWebhook") == 0) + { + return ("Sending Webhook"); + } + else if (_input.compare("ClassFlowPostProcessing") == 0) + { return ("Post-Processing"); } return "Unkown Status"; } -std::vector ClassFlowControll::GetAllDigit() +std::vector ClassFlowControll::GetAllDigit() { - if (flowdigit) { + if (flowdigit) + { ESP_LOGD(TAG, "ClassFlowControll::GetAllDigit - flowdigit != NULL"); return flowdigit->GetHTMLInfo(); } - std::vector empty; + std::vector empty; return empty; } -std::vector ClassFlowControll::GetAllAnalog() +std::vector ClassFlowControll::GetAllAnalog() { - if (flowanalog) { + if (flowanalog) + { return flowanalog->GetHTMLInfo(); } - std::vector empty; + std::vector empty; return empty; } t_CNNType ClassFlowControll::GetTypeDigit() { - if (flowdigit) { + if (flowdigit) + { return flowdigit->getCNNType(); } @@ -168,7 +155,8 @@ t_CNNType ClassFlowControll::GetTypeDigit() t_CNNType ClassFlowControll::GetTypeAnalog() { - if (flowanalog) { + if (flowanalog) + { return flowanalog->getCNNType(); } @@ -176,33 +164,35 @@ t_CNNType ClassFlowControll::GetTypeAnalog() } #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG -void ClassFlowControll::DigitDrawROI(CImageBasis *_zw) +void ClassFlowControll::DigitDrawROI(CImageBasis *TempImage) { - if (flowdigit) { - flowdigit->DrawROI(_zw); + if (flowdigit) + { + flowdigit->DrawROI(TempImage); } } -void ClassFlowControll::AnalogDrawROI(CImageBasis *_zw) +void ClassFlowControll::AnalogDrawROI(CImageBasis *TempImage) { - if (flowanalog) { - flowanalog->DrawROI(_zw); + if (flowanalog) + { + flowanalog->DrawROI(TempImage); } } #endif -#ifdef ENABLE_MQTT -bool ClassFlowControll::StartMQTTService() +bool ClassFlowControll::StartMQTTService() { /* Start the MQTT service */ - for (int i = 0; i < FlowControll.size(); ++i) { - if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0) { - return ((ClassFlowMQTT*) (FlowControll[i]))->Start(AutoInterval); - } - } + for (int i = 0; i < FlowControll.size(); ++i) + { + if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0) + { + return ((ClassFlowMQTT *)(FlowControll[i]))->Start(AutoInterval); + } + } return false; } -#endif //ENABLE_MQTT void ClassFlowControll::SetInitialParameter(void) { @@ -212,94 +202,103 @@ void ClassFlowControll::SetInitialParameter(void) flowdigit = NULL; flowanalog = NULL; flowpostprocessing = NULL; - disabled = false; + aktRunNr = 0; aktstatus = "Flow task not yet created"; aktstatusWithTime = aktstatus; + + disabled = false; } bool ClassFlowControll::getIsAutoStart(void) { - //return AutoStart; + // return AutoStart; return true; // Flow must always be enabled, else the manual trigger (REST, MQTT) will not work! } - void ClassFlowControll::setAutoStartInterval(long &_interval) { _interval = AutoInterval * 60 * 1000; // AutoInterval: minutes -> ms } -ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type) +ClassFlow *ClassFlowControll::CreateClassFlow(std::string _type) { - ClassFlow* cfc = NULL; - _type = trim(_type); + ClassFlow *cfc = NULL; + _type = to_upper(trim_string_left_right(_type)); - if (toUpper(_type).compare("[TAKEIMAGE]") == 0) { + if (_type.compare("[TAKEIMAGE]") == 0) + { cfc = new ClassFlowTakeImage(&FlowControll); - flowtakeimage = (ClassFlowTakeImage*) cfc; + flowtakeimage = (ClassFlowTakeImage *)cfc; } - - if (toUpper(_type).compare("[ALIGNMENT]") == 0) { + + else if (_type.compare("[ALIGNMENT]") == 0) + { cfc = new ClassFlowAlignment(&FlowControll); - flowalignment = (ClassFlowAlignment*) cfc; + flowalignment = (ClassFlowAlignment *)cfc; } - - if (toUpper(_type).compare("[ANALOG]") == 0) { + + else if (_type.compare("[ANALOG]") == 0) + { cfc = new ClassFlowCNNGeneral(flowalignment); - flowanalog = (ClassFlowCNNGeneral*) cfc; + flowanalog = (ClassFlowCNNGeneral *)cfc; } - - if (toUpper(_type).compare(0, 7, "[DIGITS") == 0) { + + else if (_type.compare(0, 7, "[DIGITS") == 0) + { cfc = new ClassFlowCNNGeneral(flowalignment); - flowdigit = (ClassFlowCNNGeneral*) cfc; - } - - #ifdef ENABLE_MQTT - if (toUpper(_type).compare("[MQTT]") == 0) { - cfc = new ClassFlowMQTT(&FlowControll); - } - #endif //ENABLE_MQTT - - #ifdef ENABLE_INFLUXDB - if (toUpper(_type).compare("[INFLUXDB]") == 0) { - cfc = new ClassFlowInfluxDB(&FlowControll); - } - - if (toUpper(_type).compare("[INFLUXDBV2]") == 0) { - cfc = new ClassFlowInfluxDBv2(&FlowControll); - } - #endif //ENABLE_INFLUXDB - - #ifdef ENABLE_WEBHOOK - if (toUpper(_type).compare("[WEBHOOK]") == 0) { - cfc = new ClassFlowWebhook(&FlowControll); - } - #endif //ENABLE_WEBHOOK - - if (toUpper(_type).compare("[POSTPROCESSING]") == 0) { - cfc = new ClassFlowPostProcessing(&FlowControll, flowanalog, flowdigit); - flowpostprocessing = (ClassFlowPostProcessing*) cfc; + flowdigit = (ClassFlowCNNGeneral *)cfc; } - if (cfc) { + else if (_type.compare("[MQTT]") == 0) + { + cfc = new ClassFlowMQTT(&FlowControll); + } + + else if (_type.compare("[INFLUXDB]") == 0) + { + cfc = new ClassFlowInfluxDB(&FlowControll); + } + + else if (_type.compare("[INFLUXDBV2]") == 0) + { + cfc = new ClassFlowInfluxDBv2(&FlowControll); + } + + else if (_type.compare("[WEBHOOK]") == 0) + { + cfc = new ClassFlowWebhook(&FlowControll); + } + + else if (_type.compare("[POSTPROCESSING]") == 0) + { + cfc = new ClassFlowPostProcessing(&FlowControll, flowanalog, flowdigit); + flowpostprocessing = (ClassFlowPostProcessing *)cfc; + } + + if (cfc) + { // Attached only if it is not [AutoTimer], because this is for FlowControll FlowControll.push_back(cfc); } - if (toUpper(_type).compare("[AUTOTIMER]") == 0) { + if (_type.compare("[AUTOTIMER]") == 0) + { cfc = this; } - if (toUpper(_type).compare("[DATALOGGING]") == 0) { + else if (_type.compare("[DATALOGGING]") == 0) + { cfc = this; } - if (toUpper(_type).compare("[DEBUG]") == 0) { + else if (_type.compare("[DEBUG]") == 0) + { cfc = this; } - if (toUpper(_type).compare("[SYSTEM]") == 0) { + else if (_type.compare("[SYSTEM]") == 0) + { cfc = this; } @@ -310,43 +309,40 @@ void ClassFlowControll::InitFlow(std::string config) { aktstatus = "Initialization"; aktstatusWithTime = aktstatus; - - //#ifdef ENABLE_MQTT - //MQTTPublish(mqttServer_getMainTopic() + "/" + "status", "Initialization", 1, false); // Right now, not possible -> MQTT Service is going to be started later - //#endif //ENABLE_MQTT - - string line; flowpostprocessing = NULL; - ClassFlow* cfc; - FILE* pFile; - config = FormatFileName(config); - pFile = fopen(config.c_str(), "r"); + ClassFlow *cfc; + config = format_filename(config); + FILE *pFile = fopen(config.c_str(), "r"); - line = ""; + std::string line = ""; - char zw[1024]; - - if (pFile != NULL) { - fgets(zw, 1024, pFile); - ESP_LOGD(TAG, "%s", zw); - line = std::string(zw); + char temp_char[1024]; + + if (pFile != NULL) + { + fgets(temp_char, 1024, pFile); + ESP_LOGD(TAG, "%s", temp_char); + line = std::string(temp_char); } - while ((line.size() > 0) && !(feof(pFile))) { + while ((line.size() > 0) && !(feof(pFile))) + { cfc = CreateClassFlow(line); - // printf("Name: %s\n", cfc->name().c_str()); - - if (cfc) { + + if (cfc) + { ESP_LOGE(TAG, "Start ReadParameter (%s)", line.c_str()); cfc->ReadParameter(pFile, line); } - else { + else + { line = ""; - - if (fgets(zw, 1024, pFile) && !feof(pFile)) { - ESP_LOGD(TAG, "Read: %s", zw); - line = std::string(zw); + + if (fgets(temp_char, 1024, pFile) && !feof(pFile)) + { + ESP_LOGD(TAG, "Read: %s", temp_char); + line = std::string(temp_char); } } } @@ -354,12 +350,12 @@ void ClassFlowControll::InitFlow(std::string config) fclose(pFile); } -std::string* ClassFlowControll::getActStatusWithTime() +std::string *ClassFlowControll::getActStatusWithTime() { return &aktstatusWithTime; } -std::string* ClassFlowControll::getActStatus() +std::string *ClassFlowControll::getActStatus() { return &aktstatus; } @@ -370,312 +366,340 @@ void ClassFlowControll::setActStatus(std::string _aktstatus) aktstatusWithTime = aktstatus; } -void ClassFlowControll::doFlowTakeImageOnly(string time) +void ClassFlowControll::doFlowTakeImageOnly(std::string time) { - std::string zw_time; + std::string temp_time; - for (int i = 0; i < FlowControll.size(); ++i) { - if (FlowControll[i]->name() == "ClassFlowTakeImage") { - zw_time = getCurrentTimeString("%H:%M:%S"); + for (int i = 0; i < FlowControll.size(); ++i) + { + if (FlowControll[i]->name() == "ClassFlowTakeImage") + { + temp_time = getCurrentTimeString("%H:%M:%S"); aktstatus = TranslateAktstatus(FlowControll[i]->name()); - aktstatusWithTime = aktstatus + " (" + zw_time + ")"; - #ifdef ENABLE_MQTT - MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, 1, false); - #endif //ENABLE_MQTT - + aktstatusWithTime = aktstatus + " (" + temp_time + ")"; + MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, 1, false); FlowControll[i]->doFlow(time); } } } -bool ClassFlowControll::doFlow(string time) +bool ClassFlowControll::doFlow(std::string time) { bool result = true; - std::string zw_time; + std::string temp_time; int repeat = 0; int qos = 1; - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("ClassFlowControll::doFlow - Start"); - #endif - - /* Check if we have a valid date/time and if not restart the NTP client */ - /* if (! getTimeIsSet()) { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Time not set, restarting NTP Client!"); - restartNtpClient(); - }*/ - - //checkNtpStatus(0); - - for (int i = 0; i < FlowControll.size(); ++i) { - zw_time = getCurrentTimeString("%H:%M:%S"); + for (int i = 0; i < FlowControll.size(); ++i) + { + temp_time = getCurrentTimeString("%H:%M:%S"); aktstatus = TranslateAktstatus(FlowControll[i]->name()); - aktstatusWithTime = aktstatus + " (" + zw_time + ")"; + aktstatusWithTime = aktstatus + " (" + temp_time + ")"; LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Status: " + aktstatusWithTime); - #ifdef ENABLE_MQTT - MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false); - #endif //ENABLE_MQTT - #ifdef DEBUG_DETAIL_ON - string zw = "FlowControll.doFlow - " + FlowControll[i]->name(); - LogFile.WriteHeapInfo(zw); - #endif + MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false); - if (!FlowControll[i]->doFlow(time)) { + if (!FlowControll[i]->doFlow(time)) + { repeat++; - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Fehler im vorheriger Schritt - wird zum " + to_string(repeat) + ". Mal wiederholt"); - if (i) { i -= 1; } // vPrevious step must be repeated (probably take pictures) + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Fehler beim " + aktstatus + " Schritt - wird zum " + std::to_string(repeat) + ". Mal wiederholt"); + + if (i) + { + i -= 1; + } // vPrevious step must be repeated (probably take pictures) + result = false; - if (repeat > 5) { + + if (repeat > 5) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Wiederholung 5x nicht erfolgreich --> reboot"); doReboot(); - //Step was repeated 5x --> reboot + // Step was repeated 5x --> reboot } } - else { + else + { result = true; } - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("ClassFlowControll::doFlow"); - #endif } - zw_time = getCurrentTimeString("%H:%M:%S"); + temp_time = getCurrentTimeString("%H:%M:%S"); aktstatus = "Flow finished"; - aktstatusWithTime = aktstatus + " (" + zw_time + ")"; - //LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatusWithTime); - #ifdef ENABLE_MQTT - MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false); - #endif //ENABLE_MQTT + aktstatusWithTime = aktstatus + " (" + temp_time + ")"; + + // LogFile.WriteToFile(ESP_LOG_INFO, TAG, aktstatusWithTime); + MQTTPublish(mqttServer_getMainTopic() + "/" + "status", aktstatus, qos, false); return result; } - -string ClassFlowControll::getReadoutAll(int _type) +std::string ClassFlowControll::getReadoutAll(int _type) { std::string out = ""; - - if (flowpostprocessing) { - std::vector *numbers = flowpostprocessing->GetNumbers(); - for (int i = 0; i < (*numbers).size(); ++i) { + if (flowpostprocessing) + { + std::vector *numbers = flowpostprocessing->GetNumbers(); + + for (int i = 0; i < (*numbers).size(); ++i) + { out = out + (*numbers)[i]->name + "\t"; - - switch (_type) { - case READOUT_TYPE_VALUE: - out = out + (*numbers)[i]->ReturnValue; - break; - case READOUT_TYPE_PREVALUE: - if (flowpostprocessing->PreValueUse) { - if ((*numbers)[i]->PreValueOkay) { - out = out + (*numbers)[i]->ReturnPreValue; - } - else { - out = out + "PreValue too old"; - } + + switch (_type) + { + case READOUT_TYPE_VALUE: + out = out + (*numbers)[i]->ReturnValue; + break; + case READOUT_TYPE_PREVALUE: + if (flowpostprocessing->PreValueUse) + { + if ((*numbers)[i]->PreValueOkay) + { + out = out + (*numbers)[i]->ReturnPreValue; } - else { - out = out + "PreValue deactivated"; + else + { + out = out + "PreValue too old"; } - break; - case READOUT_TYPE_RAWVALUE: - out = out + (*numbers)[i]->ReturnRawValue; - break; - case READOUT_TYPE_ERROR: - out = out + (*numbers)[i]->ErrorMessageText; - break; + } + else + { + out = out + "PreValue deactivated"; + } + break; + case READOUT_TYPE_RAWVALUE: + out = out + (*numbers)[i]->ReturnRawValue; + break; + case READOUT_TYPE_ERROR: + out = out + (*numbers)[i]->ErrorMessageText; + break; } - - if (i < (*numbers).size()-1) { + + if (i < (*numbers).size() - 1) + { out = out + "\r\n"; } } - // ESP_LOGD(TAG, "OUT: %s", out.c_str()); + // ESP_LOGD(TAG, "OUT: %s", out.c_str()); } return out; -} +} -string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = false, int _number = 0) +std::string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = false, int _number = 0) { - if (flowpostprocessing) { + if (flowpostprocessing) + { return flowpostprocessing->getReadoutParam(_rawvalue, _noerror, _number); } return std::string(""); } -string ClassFlowControll::GetPrevalue(std::string _number) +std::string ClassFlowControll::GetPrevalue(std::string _number) { - if (flowpostprocessing) { - return flowpostprocessing->GetPreValue(_number); + if (flowpostprocessing) + { + return flowpostprocessing->GetPreValue(_number); } - return std::string(""); + return std::string(""); } bool ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern) { double newvalueAsDouble; - char* p; + char *p; - _newvalue = trim(_newvalue); + _newvalue = trim_string_left_right(_newvalue); // ESP_LOGD(TAG, "Input UpdatePreValue: %s", _newvalue.c_str()); - if (_newvalue.substr(0,8).compare("0.000000") == 0 || _newvalue.compare("0.0") == 0 || _newvalue.compare("0") == 0) { - newvalueAsDouble = 0; // preset to value = 0 + if (_newvalue.substr(0, 8).compare("0.000000") == 0 || _newvalue.compare("0.0") == 0 || _newvalue.compare("0") == 0) + { + newvalueAsDouble = 0; // preset to value = 0 } - else { + else + { newvalueAsDouble = strtod(_newvalue.c_str(), &p); - if (newvalueAsDouble == 0) { + if (newvalueAsDouble == 0) + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "UpdatePrevalue: No valid value for processing: " + _newvalue); return false; } } - - if (flowpostprocessing) { - if (flowpostprocessing->SetPreValue(newvalueAsDouble, _numbers, _extern)) { + + if (flowpostprocessing) + { + if (flowpostprocessing->SetPreValue(newvalueAsDouble, _numbers, _extern)) + { return true; } - else { + else + { return false; } } - else { + else + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "UpdatePrevalue: ERROR - Class Post-Processing not initialized"); return false; } } -bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph) +bool ClassFlowControll::ReadParameter(FILE *pFile, std::string &aktparamgraph) { - std::vector splitted; - aktparamgraph = trim(aktparamgraph); - - if (aktparamgraph.size() == 0) { - if (!this->GetNextParagraph(pfile, aktparamgraph)) { + aktparamgraph = trim_string_left_right(aktparamgraph); + if (aktparamgraph.size() == 0) + { + if (!GetNextParagraph(pFile, aktparamgraph)) + { return false; } } - if ((toUpper(aktparamgraph).compare("[AUTOTIMER]") != 0) && (toUpper(aktparamgraph).compare("[DEBUG]") != 0) && - (toUpper(aktparamgraph).compare("[SYSTEM]") != 0 && (toUpper(aktparamgraph).compare("[DATALOGGING]") != 0))) { + if ((to_upper(aktparamgraph).compare("[AUTOTIMER]") != 0) && (to_upper(aktparamgraph).compare("[DEBUG]") != 0) && + (to_upper(aktparamgraph).compare("[SYSTEM]") != 0 && (to_upper(aktparamgraph).compare("[DATALOGGING]") != 0))) + { // Paragraph passt nicht zu Debug oder DataLogging return false; } - while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) { - splitted = ZerlegeZeile(aktparamgraph, " ="); + std::vector splitted; - if ((toUpper(splitted[0]) == "INTERVAL") && (splitted.size() > 1)) { - if (isStringNumeric(splitted[1])) { - AutoInterval = std::stof(splitted[1]); - } - } + while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph)) + { + splitted = split_line(aktparamgraph); - if ((toUpper(splitted[0]) == "DATALOGACTIVE") && (splitted.size() > 1)) { - LogFile.SetDataLogToSD(alphanumericToBoolean(splitted[1])); - } + if (splitted.size() > 1) + { + std::string _param = to_upper(splitted[0]); - if ((toUpper(splitted[0]) == "DATAFILESRETENTION") && (splitted.size() > 1)) { - if (isStringNumeric(splitted[1])) { - LogFile.SetDataLogRetention(std::stoi(splitted[1])); + if (_param == "INTERVAL") + { + if (is_string_numeric(splitted[1])) + { + AutoInterval = std::stof(splitted[1]); + } } - } + else if (_param == "DATALOGACTIVE") + { + LogFile.SetDataLogToSD(alphanumeric_to_boolean(splitted[1])); + } + else if (_param == "DATAFILESRETENTION") + { + if (is_string_numeric(splitted[1])) + { + LogFile.SetDataLogRetention(std::stoi(splitted[1])); + } + } + else if (_param == "LOGLEVEL") + { + /* matches esp_log_level_t */ + if ((to_upper(splitted[1]) == "TRUE") || (to_upper(splitted[1]) == "2")) + { + LogFile.setLogLevel(ESP_LOG_WARN); + } + else if ((to_upper(splitted[1]) == "FALSE") || (to_upper(splitted[1]) == "0") || (to_upper(splitted[1]) == "1")) + { + LogFile.setLogLevel(ESP_LOG_ERROR); + } + else if (to_upper(splitted[1]) == "3") + { + LogFile.setLogLevel(ESP_LOG_INFO); + } + else if (to_upper(splitted[1]) == "4") + { + LogFile.setLogLevel(ESP_LOG_DEBUG); + } - if ((toUpper(splitted[0]) == "LOGLEVEL") && (splitted.size() > 1)) { - /* matches esp_log_level_t */ - if ((toUpper(splitted[1]) == "TRUE") || (toUpper(splitted[1]) == "2")) { - LogFile.setLogLevel(ESP_LOG_WARN); + /* If system reboot was not triggered by user and reboot was caused by execption -> keep log level to DEBUG */ + if (!getIsPlannedReboot() && (esp_reset_reason() == ESP_RST_PANIC)) + { + LogFile.setLogLevel(ESP_LOG_DEBUG); + } } - else if ((toUpper(splitted[1]) == "FALSE") || (toUpper(splitted[1]) == "0") || (toUpper(splitted[1]) == "1")) { - LogFile.setLogLevel(ESP_LOG_ERROR); - } - else if (toUpper(splitted[1]) == "3") { - LogFile.setLogLevel(ESP_LOG_INFO); - } - else if (toUpper(splitted[1]) == "4") { - LogFile.setLogLevel(ESP_LOG_DEBUG); + else if (_param == "LOGFILESRETENTION") + { + if (is_string_numeric(splitted[1])) + { + LogFile.SetLogFileRetention(std::stoi(splitted[1])); + } } +#if (defined WLAN_USE_ROAMING_BY_SCANNING || (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES)) + else if (_param == "RSSITHRESHOLD") + { + int RSSIThresholdTMP = atoi(splitted[1].c_str()); + RSSIThresholdTMP = min(0, max(-100, RSSIThresholdTMP)); // Verify input limits (-100 - 0) - /* If system reboot was not triggered by user and reboot was caused by execption -> keep log level to DEBUG */ - if (!getIsPlannedReboot() && (esp_reset_reason() == ESP_RST_PANIC)) { - LogFile.setLogLevel(ESP_LOG_DEBUG); + if (ChangeRSSIThreshold(NETWORK_CONFIG_FILE, RSSIThresholdTMP)) + { + // reboot necessary so that the new wlan.ini is also used !!! + fclose(pFile); + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Rebooting to activate new RSSITHRESHOLD ..."); + doReboot(); + } } - } - - if ((toUpper(splitted[0]) == "LOGFILESRETENTION") && (splitted.size() > 1)) { - if (isStringNumeric(splitted[1])) { - LogFile.SetLogFileRetention(std::stoi(splitted[1])); +#endif + else if (_param == "HOSTNAME") + { + if (ChangeHostName(NETWORK_CONFIG_FILE, splitted[1])) + { + // reboot necessary so that the new wlan.ini is also used !!! + fclose(pFile); + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Rebooting to activate new HOSTNAME..."); + doReboot(); + } } - } - - /* TimeServer and TimeZone got already read from the config, see setupTime () */ - - #if (defined WLAN_USE_ROAMING_BY_SCANNING || (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES)) - if ((toUpper(splitted[0]) == "RSSITHRESHOLD") && (splitted.size() > 1)) { - int RSSIThresholdTMP = atoi(splitted[1].c_str()); - RSSIThresholdTMP = min(0, max(-100, RSSIThresholdTMP)); // Verify input limits (-100 - 0) - - if (ChangeRSSIThreshold(WLAN_CONFIG_FILE, RSSIThresholdTMP)) { - // reboot necessary so that the new wlan.ini is also used !!! - fclose(pfile); - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Rebooting to activate new RSSITHRESHOLD ..."); - doReboot(); + else if (_param == "SETUPMODE") + { + SetupModeActive = alphanumeric_to_boolean(splitted[1]); } } - #endif - - if ((toUpper(splitted[0]) == "HOSTNAME") && (splitted.size() > 1)) { - if (ChangeHostName(WLAN_CONFIG_FILE, splitted[1])) { - // reboot necessary so that the new wlan.ini is also used !!! - fclose(pfile); - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Rebooting to activate new HOSTNAME..."); - doReboot(); - } - } - - if ((toUpper(splitted[0]) == "SETUPMODE") && (splitted.size() > 1)) { - SetupModeActive = alphanumericToBoolean(splitted[1]); - } } + return true; } -int ClassFlowControll::CleanTempFolder() { - const char* folderPath = "/sdcard/img_tmp"; - +int ClassFlowControll::CleanTempFolder() +{ + const char *folderPath = "/sdcard/img_tmp"; + ESP_LOGD(TAG, "Clean up temporary folder to avoid damage of sdcard sectors: %s", folderPath); DIR *dir = opendir(folderPath); - - if (!dir) { + + if (!dir) + { ESP_LOGE(TAG, "Failed to stat dir: %s", folderPath); return -1; } struct dirent *entry; int deleted = 0; - - while ((entry = readdir(dir)) != NULL) { - std::string path = string(folderPath) + "/" + entry->d_name; - if (entry->d_type == DT_REG) { - if (unlink(path.c_str()) == 0) { - deleted ++; - } - else { + + while ((entry = readdir(dir)) != NULL) + { + std::string path = std::string(folderPath) + "/" + entry->d_name; + if (entry->d_type == DT_REG) + { + if (unlink(path.c_str()) == 0) + { + deleted++; + } + else + { ESP_LOGE(TAG, "can't delete file: %s", path.c_str()); } - } - else if (entry->d_type == DT_DIR) { - deleted += removeFolder(path.c_str(), TAG); + } + else if (entry->d_type == DT_DIR) + { + deleted += remove_folder(path.c_str(), TAG); } } - + closedir(dir); ESP_LOGD(TAG, "%d files deleted", deleted); - + return 0; } @@ -684,212 +708,166 @@ esp_err_t ClassFlowControll::SendRawJPG(httpd_req_t *req) return flowtakeimage != NULL ? flowtakeimage->SendRawJPG(req) : ESP_FAIL; } -esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req) +esp_err_t ClassFlowControll::GetJPGStream(std::string file_name, httpd_req_t *req) { - ESP_LOGD(TAG, "ClassFlowControll::GetJPGStream %s", _fn.c_str()); + ESP_LOGD(TAG, "ClassFlowControl::GetJPGStream %s", file_name.c_str()); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("ClassFlowControll::GetJPGStream - Start"); - #endif - - CImageBasis *_send = NULL; + CImageBasis *image_to_send = NULL; esp_err_t result = ESP_FAIL; - bool _sendDelete = false; + bool delete_image_to_send = false; - if (_fn == "alg.jpg") { - if (flowalignment && flowalignment->ImageBasis->ImageOkay()) { - _send = flowalignment->ImageBasis; + if (file_name == "alg.jpg") + { + if (flowalignment && flowalignment->ImageBasis->ImageOkay()) + { + image_to_send = flowalignment->ImageBasis; } - else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ClassFlowControll::GetJPGStream: alg.jpg cannot be served"); + else + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "ClassFlowControl::GetJPGStream: alg.jpg cannot be served"); + result = send_file(req, "/sdcard/html/Flowstate_at_work.jpg"); + httpd_resp_send_chunk(req, NULL, 0); + } + } + else if (file_name == "alg_roi.jpg") + { +#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG // no CImageBasis needed to create alg_roi.jpg (ca. 790kB less RAM) + if (aktstatus.find("Initialization (delayed)") != -1) + { + result = send_file(req, "/sdcard/html/Flowstate_init_delayed.jpg"); + } + else if (aktstatus.find("Initialization") != -1) + { + result = send_file(req, "/sdcard/html/Flowstate_init.jpg"); + } + else if (aktstatus.find("Take Image") != -1) + { + if (flowalignment && flowalignment->AlgROI) + { + result = send_file(req, "/sdcard/html/Flowstate_take_image.jpg"); + } + else + { + if (flowalignment && flowalignment->ImageBasis->ImageOkay()) + { + image_to_send = flowalignment->ImageBasis; + } + else + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "ClassFlowControl::GetJPGStream: alg_roi.jpg cannot be served -> alg.jpg is going to be served!"); + result = send_file(req, "/sdcard/html/Flowstate_at_work.jpg"); + httpd_resp_send_chunk(req, NULL, 0); + } + } + } + else + { + if (flowalignment && flowalignment->AlgROI) + { + httpd_resp_set_type(req, "image/jpeg"); + result = httpd_resp_send(req, (const char *)flowalignment->AlgROI->data, flowalignment->AlgROI->size); + } + else + { + if (flowalignment && flowalignment->ImageBasis->ImageOkay()) + { + image_to_send = flowalignment->ImageBasis; + } + else + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "ClassFlowControl::GetJPGStream: alg_roi.jpg cannot be served -> alg.jpg is going to be served!"); + result = send_file(req, "/sdcard/html/Flowstate_at_work.jpg"); + httpd_resp_send_chunk(req, NULL, 0); + } + } + } +#else + if (!flowalignment) + { + ESP_LOGD(TAG, "ClassFloDControl::GetJPGStream: FlowAlignment is not (yet) initialized. Interrupt serving!"); + httpd_resp_send(req, NULL, 0); return ESP_FAIL; } + + image_to_send = new CImageBasis("alg_roi", flowalignment->ImageBasis); + if (image_to_send->ImageOkay()) + { + if (flowalignment) + { + flowalignment->DrawRef(image_to_send); + } + if (flowdigit) + { + flowdigit->DrawROI(image_to_send); + } + if (flowanalog) + { + flowanalog->DrawROI(image_to_send); + } + + delete_image_to_send = true; // delete temporary image_to_send element after sending + } + else + { + if (flowalignment && flowalignment->ImageBasis->ImageOkay()) + { + image_to_send = flowalignment->ImageBasis; + } + else + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "ClassFlowControl::GetJPGStream: Not enough memory to create alg_roi.jpg -> alg.jpg is going to be served!"); + result = send_file(req, "/sdcard/html/Flowstate_at_work.jpg"); + httpd_resp_send_chunk(req, NULL, 0); + } + } +#endif } - else if (_fn == "alg_roi.jpg") { - #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG // no CImageBasis needed to create alg_roi.jpg (ca. 790kB less RAM) - if (aktstatus.find("Initialization (delayed)") != -1) { - std::string filename = "/sdcard/html/Flowstate_initialization_delayed.jpg"; - result = send_file(req, filename); - /* - FILE* file = fopen("/sdcard/html/Flowstate_initialization_delayed.jpg", "rb"); + else + { + std::vector htmlinfo = GetAllDigit(); + ESP_LOGD(TAG, "After getClassFlowControl::GetAllDigit"); - if (!file) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File /sdcard/html/Flowstate_initialization_delayed.jpg not found"); - return ESP_FAIL; - } - - fseek(file, 0, SEEK_END); - long fileSize = ftell(file); // how long is the file ? - fseek(file, 0, SEEK_SET); // reset - - unsigned char* fileBuffer = (unsigned char*) malloc(fileSize); - - if (!fileBuffer) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ClassFlowControll::GetJPGStream: Not enough memory to create fileBuffer: " + std::to_string(fileSize)); - fclose(file); - return ESP_FAIL; - } - - fread(fileBuffer, fileSize, 1, file); - fclose(file); - - httpd_resp_set_type(req, "image/jpeg"); - result = httpd_resp_send(req, (const char *)fileBuffer, fileSize); - free(fileBuffer); - */ - } - else if (aktstatus.find("Initialization") != -1) { - std::string filename = "/sdcard/html/Flowstate_initialization.jpg"; - result = send_file(req, filename); - /* - FILE* file = fopen("/sdcard/html/Flowstate_initialization.jpg", "rb"); - - if (!file) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File /sdcard/html/Flowstate_initialization.jpg not found"); - return ESP_FAIL; - } - - fseek(file, 0, SEEK_END); - long fileSize = ftell(file); // how long is the file ? - fseek(file, 0, SEEK_SET); // reset - - unsigned char* fileBuffer = (unsigned char*) malloc(fileSize); - - if (!fileBuffer) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ClassFlowControll::GetJPGStream: Not enough memory to create fileBuffer: " + std::to_string(fileSize)); - fclose(file); - return ESP_FAIL; - } - - fread(fileBuffer, fileSize, 1, file); - fclose(file); - - httpd_resp_set_type(req, "image/jpeg"); - result = httpd_resp_send(req, (const char *)fileBuffer, fileSize); - free(fileBuffer); - */ - } - else if (aktstatus.find("Take Image") != -1) { - if (flowalignment && flowalignment->AlgROI) { - std::string filename = "/sdcard/html/Flowstate_take_image.jpg"; - result = send_file(req, filename); - /* - FILE* file = fopen("/sdcard/html/Flowstate_take_image.jpg", "rb"); - - if (!file) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File /sdcard/html/Flowstate_take_image.jpg not found"); - return ESP_FAIL; - } - - fseek(file, 0, SEEK_END); - flowalignment->AlgROI->size = ftell(file); // how long is the file ? - fseek(file, 0, SEEK_SET); // reset - - if (flowalignment->AlgROI->size > MAX_JPG_SIZE) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "File /sdcard/html/Flowstate_take_image.jpg (" + std::to_string(flowalignment->AlgROI->size) + - ") > allocated buffer (" + std::to_string(MAX_JPG_SIZE) + ")"); - fclose(file); - return ESP_FAIL; - } - - fread(flowalignment->AlgROI->data, flowalignment->AlgROI->size, 1, file); - fclose(file); - - httpd_resp_set_type(req, "image/jpeg"); - result = httpd_resp_send(req, (const char *)flowalignment->AlgROI->data, flowalignment->AlgROI->size); - */ - } - else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ClassFlowControll::GetJPGStream: alg_roi.jpg cannot be served -> alg.jpg is going to be served!"); - if (flowalignment && flowalignment->ImageBasis->ImageOkay()) { - _send = flowalignment->ImageBasis; - } - else { - httpd_resp_send(req, NULL, 0); - return ESP_OK; - } - } - } - else { - if (flowalignment && flowalignment->AlgROI) { - httpd_resp_set_type(req, "image/jpeg"); - result = httpd_resp_send(req, (const char *)flowalignment->AlgROI->data, flowalignment->AlgROI->size); - } - else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ClassFlowControll::GetJPGStream: alg_roi.jpg cannot be served -> alg.jpg is going to be served!"); - if (flowalignment && flowalignment->ImageBasis->ImageOkay()) { - _send = flowalignment->ImageBasis; - } - else { - httpd_resp_send(req, NULL, 0); - return ESP_OK; - } - } - } - #else - if (!flowalignment) { - ESP_LOGD(TAG, "ClassFloDControll::GetJPGStream: FlowAlignment is not (yet) initialized. Interrupt serving!"); - httpd_resp_send(req, NULL, 0); - return ESP_FAIL; - } - - _send = new CImageBasis("alg_roi", flowalignment->ImageBasis); - - if (_send->ImageOkay()) { - if (flowalignment) { flowalignment->DrawRef(_send); } - if (flowdigit) { flowdigit->DrawROI(_send); } - if (flowanalog) { flowanalog->DrawROI(_send); } - _sendDelete = true; // delete temporary _send element after sending - } - else { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "ClassFlowControll::GetJPGStream: Not enough memory to create alg_roi.jpg -> alg.jpg is going to be served!"); - - if (flowalignment && flowalignment->ImageBasis->ImageOkay()) { - _send = flowalignment->ImageBasis; - } - else { - httpd_resp_send(req, NULL, 0); - return ESP_OK; - } - } - #endif - } - else { - std::vector htmlinfo; - - htmlinfo = GetAllDigit(); - ESP_LOGD(TAG, "After getClassFlowControll::GetAllDigit"); - - for (int i = 0; i < htmlinfo.size(); ++i) { - if (_fn == htmlinfo[i]->filename) { - if (htmlinfo[i]->image) { - _send = htmlinfo[i]->image; + for (int i = 0; i < htmlinfo.size(); ++i) + { + if (file_name == htmlinfo[i]->filename) + { + if (htmlinfo[i]->image) + { + image_to_send = htmlinfo[i]->image; } } - if (_fn == htmlinfo[i]->filename_org) { - if (htmlinfo[i]->image_org) { - _send = htmlinfo[i]->image_org; + if (file_name == htmlinfo[i]->filename_org) + { + if (htmlinfo[i]->image_org) + { + image_to_send = htmlinfo[i]->image_org; } } delete htmlinfo[i]; } htmlinfo.clear(); - if (!_send) { + if (!image_to_send) + { htmlinfo = GetAllAnalog(); - ESP_LOGD(TAG, "After getClassFlowControll::GetAllAnalog"); - - for (int i = 0; i < htmlinfo.size(); ++i) { - if (_fn == htmlinfo[i]->filename) { - if (htmlinfo[i]->image) { - _send = htmlinfo[i]->image; + ESP_LOGD(TAG, "After getClassFlowControl::GetAllAnalog"); + + for (int i = 0; i < htmlinfo.size(); ++i) + { + if (file_name == htmlinfo[i]->filename) + { + if (htmlinfo[i]->image) + { + image_to_send = htmlinfo[i]->image; } } - if (_fn == htmlinfo[i]->filename_org) { - if (htmlinfo[i]->image_org) { - _send = htmlinfo[i]->image_org; + if (file_name == htmlinfo[i]->filename_org) + { + if (htmlinfo[i]->image_org) + { + image_to_send = htmlinfo[i]->image_org; } } delete htmlinfo[i]; @@ -898,47 +876,41 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req) } } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("ClassFlowControll::GetJPGStream - before send"); - #endif + if (image_to_send) + { + ESP_LOGD(TAG, "Sending file: %s ...", file_name.c_str()); + set_content_type_from_file(req, file_name.c_str()); + result = image_to_send->SendJPGtoHTTP(req); - if (_send) { - ESP_LOGD(TAG, "Sending file: %s ...", _fn.c_str()); - set_content_type_from_file(req, _fn.c_str()); - result = _send->SendJPGtoHTTP(req); - /* Respond with an empty chunk to signal HTTP response completion */ httpd_resp_send_chunk(req, NULL, 0); ESP_LOGD(TAG, "File sending complete"); - if (_sendDelete) { - delete _send; + if (delete_image_to_send) + { + delete image_to_send; } - - _send = NULL; - } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("ClassFlowControll::GetJPGStream - done"); - #endif + image_to_send = NULL; + } return result; } -string ClassFlowControll::getNumbersName() +std::string ClassFlowControll::getNumbersName() { return flowpostprocessing->getNumbersName(); } -string ClassFlowControll::getJSON() +std::string ClassFlowControll::getJSON() { return flowpostprocessing->GetJSON(); } -/** +/** * @returns a vector of all current sequences **/ -const std::vector &ClassFlowControll::getNumbers() +const std::vector &ClassFlowControll::getNumbers() { return *flowpostprocessing->GetNumbers(); } diff --git a/code/components/jomjol_flowcontroll/ClassFlowControll.h b/code/components/jomjol_flowcontroll/ClassFlowControll.h index 40b5ba11..d7caf7a3 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowControll.h +++ b/code/components/jomjol_flowcontroll/ClassFlowControll.h @@ -10,62 +10,57 @@ #include "ClassFlowAlignment.h" #include "ClassFlowCNNGeneral.h" #include "ClassFlowPostProcessing.h" -#ifdef ENABLE_MQTT - #include "ClassFlowMQTT.h" -#endif //ENABLE_MQTT -#ifdef ENABLE_INFLUXDB - #include "ClassFlowInfluxDB.h" - #include "ClassFlowInfluxDBv2.h" -#endif //ENABLE_INFLUXDB -#ifdef ENABLE_WEBHOOK - #include "ClassFlowWebhook.h" -#endif //ENABLE_WEBHOOK +#include "ClassFlowMQTT.h" +#include "ClassFlowInfluxDB.h" +#include "ClassFlowInfluxDBv2.h" +#include "ClassFlowWebhook.h" #include "ClassFlowCNNGeneral.h" -class ClassFlowControll : - public ClassFlow +class ClassFlowControll : public ClassFlow { protected: - std::vector FlowControll; - ClassFlowPostProcessing* flowpostprocessing; - ClassFlowAlignment* flowalignment; - ClassFlowCNNGeneral* flowanalog; - ClassFlowCNNGeneral* flowdigit; -// ClassFlowDigit* flowdigit; - ClassFlowTakeImage* flowtakeimage; - ClassFlow* CreateClassFlow(std::string _type); + std::vector FlowControll; + ClassFlowPostProcessing *flowpostprocessing; + ClassFlowAlignment *flowalignment; + ClassFlowCNNGeneral *flowanalog; + ClassFlowCNNGeneral *flowdigit; + // ClassFlowDigit* flowdigit; + ClassFlowTakeImage *flowtakeimage; + ClassFlow *CreateClassFlow(std::string _type); - bool AutoStart; - float AutoInterval; - void SetInitialParameter(void); + void SetInitialParameter(void); std::string aktstatusWithTime; std::string aktstatus; int aktRunNr; public: - bool SetupModeActive; + bool AutoStart = false; + float AutoInterval = 5; + + bool SetupModeActive = false; + bool AlignmentOk = false; void InitFlow(std::string config); - bool doFlow(string time); - void doFlowTakeImageOnly(string time); - bool getStatusSetupModus(){return SetupModeActive;}; - string getReadout(bool _rawvalue, bool _noerror, int _number); - string getReadoutAll(int _type); + bool doFlow(std::string time); + void doFlowTakeImageOnly(std::string time); + bool getStatusSetupModus() { return SetupModeActive; }; + std::string getReadout(bool _rawvalue, bool _noerror, int _number); + std::string getReadoutAll(int _type); bool UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern); - string GetPrevalue(std::string _number = ""); - bool ReadParameter(FILE* pfile, string& aktparamgraph); - string getJSON(); - const std::vector &getNumbers(); - string getNumbersName(); + std::string GetPrevalue(std::string _number = ""); + bool ReadParameter(FILE *pFile, string &aktparamgraph); + std::string getJSON(); + const std::vector &getNumbers(); + std::string getNumbersName(); - string TranslateAktstatus(std::string _input); + std::string TranslateAktstatus(std::string _input); - #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG - void DigitDrawROI(CImageBasis *_zw); - void AnalogDrawROI(CImageBasis *_zw); - #endif +#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG + void DigitDrawROI(CImageBasis *TempImage); + void AnalogDrawROI(CImageBasis *TempImage); +#endif - esp_err_t GetJPGStream(std::string _fn, httpd_req_t *req); + esp_err_t GetJPGStream(std::string file_name, httpd_req_t *req); esp_err_t SendRawJPG(httpd_req_t *req); std::string doSingleStep(std::string _stepname, std::string _host); @@ -73,26 +68,21 @@ public: bool getIsAutoStart(); void setAutoStartInterval(long &_interval); - std::string* getActStatusWithTime(); - std::string* getActStatus(); + std::string *getActStatusWithTime(); + std::string *getActStatus(); void setActStatus(std::string _aktstatus); - std::vector GetAllDigit(); - std::vector GetAllAnalog(); + std::vector GetAllDigit(); + std::vector GetAllAnalog(); t_CNNType GetTypeDigit(); t_CNNType GetTypeAnalog(); - - #ifdef ENABLE_MQTT + bool StartMQTTService(); - #endif //ENABLE_MQTT int CleanTempFolder(); - string name(){return "ClassFlowControll";}; + std::string name() { return "ClassFlowControll"; }; }; #endif - - - diff --git a/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h b/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h index 7a2b82b1..517dd491 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h +++ b/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h @@ -5,81 +5,81 @@ #include "ClassFlowImage.h" -/** - * Properties of one ROI - * FIXME: naming of members could use some refactoring to comply with common C++ coding style guidelines - */ -struct roi { - int posx, posy, deltax, deltay; +struct roi +{ + int pos_x; + int pos_y; + int delta_x; + int delta_y; + float raw_result_float; + int raw_result_klasse; float result_float; int result_klasse; - bool isReject, CCW; + bool isReject; + bool ccw; string name; - CImageBasis *image, *image_org; + CImageBasis *image; + CImageBasis *image_org; }; -/** - * FIXME: Why is this additional layer needed? - */ -struct general { +struct general +{ string name; - std::vector ROI; + std::vector ROI; }; -enum t_RateType { +enum t_RateType +{ AbsoluteChange, // ignores the time difference; only the value difference is used comparison with NumberPost.maxRate RateChange // time difference is considered and a normalized rate is used for comparison with NumberPost.maxRate - }; +}; -/** - * Holds all properties and settings of a sequence. A sequence is a set of digit and/or analog ROIs that are combined to - * provide one meter reading (value). - * FIXME: can be renamed to `Sequence` - */ -struct NumberPost { - float MaxRateValue; // maxRate; upper bound for the difference between two consecutive readings; affected by maxRateType; - bool useMaxRateValue; // consistencyChecksEnabled; enables consistency checks; uses maxRate and maxRateType - t_RateType MaxRateType; // maxRateType; affects how the value of maxRate is used for comparing the current and previous value - bool ErrorMessage; // FIXME: not used; can be removed - int ChangeRateThreshold; // threshold parameter for negative rate detection - bool PreValueOkay; // previousValueValid; indicates that the reading of the previous round has no errors - bool AllowNegativeRates; // allowNegativeRate; defines if the consistency checks allow negative rates between consecutive meter readings. - bool IgnoreLeadingNaN; - bool checkDigitIncreaseConsistency; // extendedConsistencyCheck; performs an additional consistency check to avoid wrong readings - time_t timeStampLastValue; // Timestamp for the last read value; is used for the log - time_t timeStampLastPreValue; // Timestamp for the last PreValue set; is used for useMaxRateValue - time_t timeStampTimeUTC; // FIXME: not used; can be removed. - string timeStamp; // localTimeStr; timestamp of last valid reading formatted as local time - double FlowRateAct; // currentRate; ΔValue/min; since usage is not limited to water meters, the physical unit is not known. - double PreValue; // lastValidValue; most recent value that could be read w/o any errors - double Value; // value; most recent readout; may include corrections - string ReturnRateValue; // currentRateStr; current normalized rate; ΔValue/min - string ReturnChangeAbsolute; // currentChangeStr; absolute difference between current and previous measurement - string ReturnRawValue; // rawValueStr; Raw value (with N & leading 0) - string ReturnValue; // valueStr; corrected return value, if necessary with error message - string ReturnPreValue; // lastValidValueStr; corrected return value without error message - string ErrorMessageText; // errorMessage; Error message for consistency checks - int AnzahlAnalog; // numAnalogRoi; number of analog ROIs used in this sequence - int AnzahlDigit; // numDigitRoi; number of digit ROIs used in this sequence - int DecimalShift; // decimalShift; each increment shifts the decimal separator by one digit; value=value*10^decimalShift; pos. value shifts to the right - int DecimalShiftInitial; // decimalShiftInitial; same as decimalShift but is a const to reset decimalShift after calculations +struct NumberPost +{ + float MaxFlowRate; + bool useMaxFlowRate; + float MaxRateValue; // maxRate; upper bound for the difference between two consecutive readings; affected by maxRateType; + bool useMaxRateValue; // consistencyChecksEnabled; enables consistency checks; uses maxRate and maxRateType + t_RateType MaxRateType; // maxRateType; affects how the value of maxRate is used for comparing the current and previous value + bool ErrorMessage; // FIXME: not used; can be removed + int ChangeRateThreshold; // threshold parameter for negative rate detection + bool PreValueOkay; // previousValueValid; indicates that the reading of the previous round has no errors + bool AllowNegativeRates; // allowNegativeRate; defines if the consistency checks allow negative rates between consecutive meter readings. + bool IgnoreLeadingNaN; // + time_t timeStampLastValue; // Timestamp for the last read value; is used for the log + time_t timeStampLastPreValue; // Timestamp for the last PreValue set; is used for useMaxRateValue + time_t timeStampTimeUTC; // + string timeStamp; // localTimeStr; timestamp of last valid reading formatted as local time + double FlowRateAct; // currentRate; ΔValue/min; since usage is not limited to water meters, the physical unit is not known. + double PreValue; // lastValidValue; most recent value that could be read w/o any errors + double Value; // value; most recent readout; may include corrections + string ReturnRateValue; // currentRateStr; current normalized rate; ΔValue/min + string ReturnChangeAbsolute; // currentChangeStr; absolute difference between current and previous measurement + string ReturnRawValue; // rawValueStr; Raw value (with N & leading 0) + string ReturnValue; // valueStr; corrected return value, if necessary with error message + string ReturnPreValue; // lastValidValueStr; corrected return value without error message + string ErrorMessageText; // errorMessage; Error message for consistency checks + int AnzahlAnalog; // numAnalogRoi; number of analog ROIs used in this sequence + int AnzahlDigit; // numDigitRoi; number of digit ROIs used in this sequence + int DecimalShift; // decimalShift; each increment shifts the decimal separator by one digit; value=value*10^decimalShift; pos. value shifts to the right + int DecimalShiftInitial; // decimalShiftInitial; same as decimalShift but is a const to reset decimalShift after calculations float AnalogToDigitTransitionStart; // AnalogToDigitTransitionStartValue; FIXME: need a better description; When is the digit > x.1, i.e. when does it start to tilt? - int Nachkomma; // decimalPlaces; usually defined by the number of analog ROIs; affected by DecimalShift + int Nachkomma; // decimalPlaces; usually defined by the number of analog ROIs; affected by DecimalShift - string DomoticzIdx; // Domoticz counter Idx - - string FieldV1; // influxdbFieldName_v1; Name of the Field in InfluxDBv1 - string MeasurementV1; // influxdbMeasurementName_v1; Name of the Measurement in InfluxDBv1 + string DomoticzIdx; // Domoticz counter Idx - string FieldV2; // influxdbFieldName_v2; Name of the Field in InfluxDBv2 - string MeasurementV2; // influxdbMeasurementName_v2; Name of the Measurement in InfluxDBv2 + string FieldV1; // influxdbFieldName_v1; Name of the Field in InfluxDBv1 + string MeasurementV1; // influxdbMeasurementName_v1; Name of the Measurement in InfluxDBv1 - bool isExtendedResolution; // extendResolution; Adds the decimal place of the least significant analog ROI to the value + string FieldV2; // influxdbFieldName_v2; Name of the Field in InfluxDBv2 + string MeasurementV2; // influxdbMeasurementName_v2; Name of the Measurement in InfluxDBv2 - general *digit_roi; // digitRoi; set of digit ROIs for the sequence - general *analog_roi; // analogRoi; set of analog ROIs for the sequence + bool isExtendedResolution; // extendResolution; Adds the decimal place of the least significant analog ROI to the value - string name; // name; Designation for the sequence + general *digit_roi; // digitRoi; set of digit ROIs for the sequence + general *analog_roi; // analogRoi; set of analog ROIs for the sequence + + string name; // name; Designation for the sequence }; #endif diff --git a/code/components/jomjol_flowcontroll/ClassFlowImage.cpp b/code/components/jomjol_flowcontroll/ClassFlowImage.cpp index 79a32190..767daa51 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowImage.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowImage.cpp @@ -1,71 +1,66 @@ +#include "defines.h" + #include "ClassFlowImage.h" #include #include #include - -#ifdef __cplusplus -extern "C" { -#endif #include -#ifdef __cplusplus -} -#endif #include "time_sntp.h" #include "ClassLogFile.h" #include "CImageBasis.h" #include "esp_log.h" -#include "../../include/defines.h" -static const char* TAG = "FLOWIMAGE"; +static const char *TAG = "FLOWIMAGE"; - -ClassFlowImage::ClassFlowImage(const char* logTag) +ClassFlowImage::ClassFlowImage(const char *logTag) { - this->logTag = logTag; - isLogImage = false; + this->logTag = logTag; + isLogImage = false; disabled = false; this->imagesRetention = 5; } -ClassFlowImage::ClassFlowImage(std::vector * lfc, const char* logTag) : ClassFlow(lfc) +ClassFlowImage::ClassFlowImage(std::vector *lfc, const char *logTag) : ClassFlow(lfc) { - this->logTag = logTag; - isLogImage = false; + this->logTag = logTag; + isLogImage = false; disabled = false; this->imagesRetention = 5; } -ClassFlowImage::ClassFlowImage(std::vector * lfc, ClassFlow *_prev, const char* logTag) : ClassFlow(lfc, _prev) +ClassFlowImage::ClassFlowImage(std::vector *lfc, ClassFlow *_prev, const char *logTag) : ClassFlow(lfc, _prev) { - this->logTag = logTag; - isLogImage = false; + this->logTag = logTag; + isLogImage = false; disabled = false; this->imagesRetention = 5; } +string ClassFlowImage::CreateLogFolder(string time) +{ + if (!isLogImage) + return ""; -string ClassFlowImage::CreateLogFolder(string time) { - if (!isLogImage) - return ""; - - string logPath = imagesLocation + "/" + time.LOGFILE_TIME_FORMAT_DATE_EXTR + "/" + time.LOGFILE_TIME_FORMAT_HOUR_EXTR; + string logPath = imagesLocation + "/" + time.LOGFILE_TIME_FORMAT_DATE_EXTR + "/" + time.LOGFILE_TIME_FORMAT_HOUR_EXTR; isLogImage = mkdir_r(logPath.c_str(), S_IRWXU) == 0; - if (!isLogImage) { + if (!isLogImage) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't create log folder for analog images. Path " + logPath); } - return logPath; + return logPath; } -void ClassFlowImage::LogImage(string logPath, string name, float *resultFloat, int *resultInt, string time, CImageBasis *_img) { - if (!isLogImage) - return; - - - char buf[10]; +void ClassFlowImage::LogImage(string logPath, string name, float *resultFloat, int *resultInt, string time, CImageBasis *_img) +{ + if (!isLogImage) + return; - if (resultFloat != NULL) { + char buf[10]; + + if (resultFloat != NULL) + { if (*resultFloat < 0) sprintf(buf, "N.N_"); else @@ -74,47 +69,51 @@ void ClassFlowImage::LogImage(string logPath, string name, float *resultFloat, i if (strcmp(buf, "10.0_") == 0) sprintf(buf, "0.0_"); } - - } else if (resultInt != NULL) { - sprintf(buf, "%d_", *resultInt); - } else { - buf[0] = '\0'; - } + } + else if (resultInt != NULL) + { + sprintf(buf, "%d_", *resultInt); + } + else + { + buf[0] = '\0'; + } - string nm = logPath + "/" + buf + name + "_" + time + ".jpg"; - nm = FormatFileName(nm); - string output = "/sdcard/img_tmp/" + name + ".jpg"; - output = FormatFileName(output); - ESP_LOGD(logTag, "save to file: %s", nm.c_str()); - _img->SaveToFile(nm); -// CopyFile(output, nm); + string nm = logPath + "/" + buf + name + "_" + time + ".jpg"; + nm = format_filename(nm); + string output = "/sdcard/img_tmp/" + name + ".jpg"; + output = format_filename(output); + ESP_LOGD(logTag, "save to file: %s", nm.c_str()); + _img->SaveToFile(nm); } void ClassFlowImage::RemoveOldLogs() { - if (!isLogImage) - return; - - ESP_LOGD(TAG, "remove old images"); - if (imagesRetention == 0) { + if (!isLogImage) + { + return; + } + + ESP_LOGD(TAG, "remove old images"); + if (imagesRetention == 0) + { return; } time_t rawtime; - struct tm* timeinfo; + struct tm *timeinfo; char cmpfilename[30]; time(&rawtime); - rawtime = addDays(rawtime, -1 * imagesRetention + 1); + rawtime = add_days(rawtime, -1 * imagesRetention + 1); timeinfo = localtime(&rawtime); - //ESP_LOGD(TAG, "ImagefileRetentionInDays: %d", imagesRetention); - + strftime(cmpfilename, 30, LOGFILE_TIME_FORMAT, timeinfo); - //ESP_LOGD(TAG, "file name to compare: %s", cmpfilename); - string folderName = string(cmpfilename).LOGFILE_TIME_FORMAT_DATE_EXTR; + string folderName = string(cmpfilename).LOGFILE_TIME_FORMAT_DATE_EXTR; DIR *dir = opendir(imagesLocation.c_str()); - if (!dir) { + if (!dir) + { ESP_LOGE(TAG, "Failed to stat dir: %s", imagesLocation.c_str()); return; } @@ -122,19 +121,22 @@ void ClassFlowImage::RemoveOldLogs() struct dirent *entry; int deleted = 0; int notDeleted = 0; - while ((entry = readdir(dir)) != NULL) { + while ((entry = readdir(dir)) != NULL) + { string folderPath = imagesLocation + "/" + entry->d_name; - if (entry->d_type == DT_DIR) { - //ESP_LOGD(TAG, "Compare %s to %s", entry->d_name, folderName.c_str()); - if ((strlen(entry->d_name) == folderName.length()) && (strcmp(entry->d_name, folderName.c_str()) < 0)) { - removeFolder(folderPath.c_str(), logTag); + if (entry->d_type == DT_DIR) + { + if ((strlen(entry->d_name) == folderName.length()) && (strcmp(entry->d_name, folderName.c_str()) < 0)) + { + remove_folder(folderPath.c_str(), logTag); deleted++; - } else { - notDeleted ++; } - } + else + { + notDeleted++; + } + } } - ESP_LOGD(TAG, "Image folder deleted: %d | Image folder not deleted: %d", deleted, notDeleted); + ESP_LOGD(TAG, "Image folder deleted: %d | Image folder not deleted: %d", deleted, notDeleted); closedir(dir); } - diff --git a/code/components/jomjol_flowcontroll/ClassFlowImage.h b/code/components/jomjol_flowcontroll/ClassFlowImage.h index 5d41a18b..1bf8db92 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowImage.h +++ b/code/components/jomjol_flowcontroll/ClassFlowImage.h @@ -11,20 +11,19 @@ class ClassFlowImage : public ClassFlow { protected: string imagesLocation; - bool isLogImage; - unsigned short imagesRetention; - const char* logTag; + bool isLogImage; + unsigned short imagesRetention; + const char *logTag; string CreateLogFolder(string time); void LogImage(string logPath, string name, float *resultFloat, int *resultInt, string time, CImageBasis *_img); - public: - ClassFlowImage(const char* logTag); - ClassFlowImage(std::vector * lfc, const char* logTag); - ClassFlowImage(std::vector * lfc, ClassFlow *_prev, const char* logTag); - + ClassFlowImage(const char *logTag); + ClassFlowImage(std::vector *lfc, const char *logTag); + ClassFlowImage(std::vector *lfc, ClassFlow *_prev, const char *logTag); + void RemoveOldLogs(); }; -#endif //CLASSFLOWIMAGE_H \ No newline at end of file +#endif // CLASSFLOWIMAGE_H diff --git a/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp b/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp index 7626645a..9d9dba92 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp @@ -1,60 +1,62 @@ -#ifdef ENABLE_INFLUXDB +#include "defines.h" + #include +#include + #include "ClassFlowInfluxDB.h" + #include "Helper.h" -#include "connect_wlan.h" +#include "connect_wifi_sta.h" #include "time_sntp.h" #include "interface_influxdb.h" #include "ClassFlowPostProcessing.h" #include "esp_log.h" -#include "../../include/defines.h" - #include "ClassLogFile.h" -#include +static const char *TAG = "INFLUXDB"; -static const char* TAG = "INFLUXDB"; +influxDBv1_controll_config_t influxDBv1_controll_config; void ClassFlowInfluxDB::SetInitialParameter(void) { - uri = ""; - database = ""; + influxDBv1_controll_config.enabled = false; + influxDBv1_controll_config.uri = ""; + influxDBv1_controll_config.database = ""; + influxDBv1_controll_config.user = ""; + influxDBv1_controll_config.password = ""; + influxDBv1_controll_config.oldValue = ""; - OldValue = ""; - flowpostprocessing = NULL; - user = ""; - password = ""; + flowpostprocessing = NULL; previousElement = NULL; - ListFlowControll = NULL; + ListFlowControll = NULL; + disabled = false; - InfluxDBenable = false; -} +} ClassFlowInfluxDB::ClassFlowInfluxDB() { SetInitialParameter(); } -ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector* lfc) +ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector *lfc) { SetInitialParameter(); - ListFlowControll = lfc; + for (int i = 0; i < ListFlowControll->size(); ++i) { if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0) { - flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i]; + flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i]; } } } -ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector* lfc, ClassFlow *_prev) +ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector *lfc, ClassFlow *_prev) { SetInitialParameter(); - previousElement = _prev; ListFlowControll = lfc; @@ -62,176 +64,182 @@ ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector* lfc, ClassFlow *_p { if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0) { - flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i]; + flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i]; } } } - -bool ClassFlowInfluxDB::ReadParameter(FILE* pfile, string& aktparamgraph) +bool ClassFlowInfluxDB::ReadParameter(FILE *pFile, std::string &aktparamgraph) { - std::vector splitted; - - aktparamgraph = trim(aktparamgraph); - + aktparamgraph = trim_string_left_right(aktparamgraph); if (aktparamgraph.size() == 0) - if (!this->GetNextParagraph(pfile, aktparamgraph)) - return false; - - if (toUpper(aktparamgraph).compare("[INFLUXDB]") != 0) - return false; - - while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) { - ESP_LOGD(TAG, "while loop reading line: %s", aktparamgraph.c_str()); - splitted = ZerlegeZeile(aktparamgraph); - std::string _param = GetParameterName(splitted[0]); - - if ((toUpper(_param) == "USER") && (splitted.size() > 1)) + if (!GetNextParagraph(pFile, aktparamgraph)) { - this->user = splitted[1]; - } - if ((toUpper(_param) == "PASSWORD") && (splitted.size() > 1)) - { - this->password = splitted[1]; - } - if ((toUpper(_param) == "URI") && (splitted.size() > 1)) - { - this->uri = splitted[1]; - } - if (((toUpper(_param) == "DATABASE")) && (splitted.size() > 1)) - { - this->database = splitted[1]; - } - if (((toUpper(_param) == "MEASUREMENT")) && (splitted.size() > 1)) - { - handleMeasurement(splitted[0], splitted[1]); - } - if (((toUpper(_param) == "FIELD")) && (splitted.size() > 1)) - { - handleFieldname(splitted[0], splitted[1]); + return false; } } - if ((uri.length() > 0) && (database.length() > 0)) - { -// ESP_LOGD(TAG, "Init InfluxDB with uri: %s, measurement: %s, user: %s, password: %s", uri.c_str(), measurement.c_str(), user.c_str(), password.c_str()); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", user: " + user + ", password: " + password); - -/////////////////////// NEW ////////////////////////// -// InfluxDBInit(uri, database, user, password); - influxDB.InfluxDBInitV1(uri, database, user, password); -/////////////////////// NEW ////////////////////////// - - InfluxDBenable = true; - } else { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDB init skipped as we are missing some parameters"); + if ((to_upper(aktparamgraph).compare("[INFLUXDB]") != 0) && (to_upper(aktparamgraph).compare(";[INFLUXDB]") != 0)) + { + return false; } - + + if (aktparamgraph[0] == ';') + { + influxDBv1_controll_config.enabled = false; + while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph)); + ESP_LOGD(TAG, "InfluxDBv1 is disabled!"); + + return true; + } + + std::vector splitted; + + while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph)) + { + splitted = split_line(aktparamgraph); + + if (splitted.size() > 1) + { + std::string _param = to_upper(GetParameterName(splitted[0])); + + if (_param == "USER") + { + influxDBv1_controll_config.user = splitted[1]; + } + else if (_param == "PASSWORD") + { + influxDBv1_controll_config.password = splitted[1]; + } + else if (_param == "URI") + { + influxDBv1_controll_config.uri = splitted[1]; + } + else if (_param == "DATABASE") + { + influxDBv1_controll_config.database = splitted[1]; + } + else if (_param == "MEASUREMENT") + { + handleMeasurement(splitted[0], splitted[1]); + } + else if (_param == "FIELD") + { + handleFieldname(splitted[0], splitted[1]); + } + } + } + + if ((influxDBv1_controll_config.uri.length() > 0) && (influxDBv1_controll_config.database.length() > 0)) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDBv1 with uri: " + influxDBv1_controll_config.uri + ", user: " + influxDBv1_controll_config.user + ", password: " + influxDBv1_controll_config.password); + influxDB.InfluxDBInitV1(influxDBv1_controll_config.uri, influxDBv1_controll_config.database, influxDBv1_controll_config.user, influxDBv1_controll_config.password); + influxDBv1_controll_config.enabled = true; + } + else + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBv1 init skipped as we are missing some parameters"); + } + return true; } -bool ClassFlowInfluxDB::doFlow(string zwtime) +bool ClassFlowInfluxDB::doFlow(std::string temp_time) { - if (!InfluxDBenable) + if (!influxDBv1_controll_config.enabled) + { return true; + } - std::string result; - std::string measurement; - std::string resulterror = ""; - std::string resultraw = ""; - std::string resultrate = ""; - std::string resulttimestamp = ""; - long int timeutc; - string zw = ""; - string namenumber = ""; + std::string result = ""; + std::string measurement = ""; + long int result_timeutc = 0; + std::string name_number = ""; if (flowpostprocessing) { - std::vector* NUMBERS = flowpostprocessing->GetNumbers(); + std::vector *NUMBERS = flowpostprocessing->GetNumbers(); for (int i = 0; i < (*NUMBERS).size(); ++i) { measurement = (*NUMBERS)[i]->MeasurementV1; - result = (*NUMBERS)[i]->ReturnValue; - resultraw = (*NUMBERS)[i]->ReturnRawValue; - resulterror = (*NUMBERS)[i]->ErrorMessageText; - resultrate = (*NUMBERS)[i]->ReturnRateValue; - resulttimestamp = (*NUMBERS)[i]->timeStamp; - timeutc = (*NUMBERS)[i]->timeStampTimeUTC; + result = (*NUMBERS)[i]->ReturnValue; + result_timeutc = (*NUMBERS)[i]->timeStampTimeUTC; if ((*NUMBERS)[i]->FieldV1.length() > 0) { - namenumber = (*NUMBERS)[i]->FieldV1; + name_number = (*NUMBERS)[i]->FieldV1; } else { - namenumber = (*NUMBERS)[i]->name; - if (namenumber == "default") - namenumber = "value"; + name_number = (*NUMBERS)[i]->name; + if (name_number == "default") + { + name_number = "value"; + } else - namenumber = namenumber + "/value"; + { + name_number = name_number + "/value"; + } } - if (result.length() > 0) -//////////////////////// NEW ////////////////////////// -// InfluxDBPublish(measurement, namenumber, result, timeutc); - influxDB.InfluxDBPublish(measurement, namenumber, result, timeutc); -//////////////////////// NEW ////////////////////////// - - + if (result.length() > 0) + { + influxDB.InfluxDBPublish(measurement, name_number, result, result_timeutc); + } } } - - OldValue = result; - + + influxDBv1_controll_config.oldValue = result; + return true; } -void ClassFlowInfluxDB::handleMeasurement(string _decsep, string _value) +void ClassFlowInfluxDB::handleMeasurement(std::string _decsep, std::string _value) { - string _digit, _decpos; + std::string _digit; int _pospunkt = _decsep.find_first_of("."); -// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); + if (_pospunkt > -1) + { _digit = _decsep.substr(0, _pospunkt); + } else + { _digit = "default"; + } + for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j) { - if (_digit == "default") // Set to default first (if nothing else is set) - { - flowpostprocessing->NUMBERS[j]->MeasurementV1 = _value; - } - if (flowpostprocessing->NUMBERS[j]->name == _digit) + // Set to default first (if nothing else is set) + if ((_digit == "default") || (flowpostprocessing->NUMBERS[j]->name == _digit)) { flowpostprocessing->NUMBERS[j]->MeasurementV1 = _value; } } } - -void ClassFlowInfluxDB::handleFieldname(string _decsep, string _value) +void ClassFlowInfluxDB::handleFieldname(std::string _decsep, std::string _value) { - string _digit, _decpos; + std::string _digit; int _pospunkt = _decsep.find_first_of("."); -// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); + if (_pospunkt > -1) + { _digit = _decsep.substr(0, _pospunkt); + } else + { _digit = "default"; + } + for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j) { - if (_digit == "default") // Set to default first (if nothing else is set) - { - flowpostprocessing->NUMBERS[j]->FieldV1 = _value; - } - if (flowpostprocessing->NUMBERS[j]->name == _digit) + // Set to default first (if nothing else is set) + if ((_digit == "default") || (flowpostprocessing->NUMBERS[j]->name == _digit)) { flowpostprocessing->NUMBERS[j]->FieldV1 = _value; } } } - - -#endif //ENABLE_INFLUXDB \ No newline at end of file diff --git a/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.h b/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.h index 00744dd3..8dbb7356 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.h +++ b/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.h @@ -1,47 +1,47 @@ -#ifdef ENABLE_INFLUXDB - #pragma once #ifndef CLASSFINFLUXDB_H #define CLASSFINFLUXDB_H -#include "ClassFlow.h" +#include +#include "ClassFlow.h" #include "ClassFlowPostProcessing.h" #include "interface_influxdb.h" -#include +typedef struct +{ + bool enabled; + std::string uri; + std::string database; + std::string measurement; + std::string user; + std::string password; + std::string oldValue; -class ClassFlowInfluxDB : - public ClassFlow +} influxDBv1_controll_config_t; + +extern influxDBv1_controll_config_t influxDBv1_controll_config; + +class ClassFlowInfluxDB : public ClassFlow { protected: - std::string uri, database, measurement; - std::string OldValue; - ClassFlowPostProcessing* flowpostprocessing; - std::string user, password; - bool InfluxDBenable; - InfluxDB influxDB; - void SetInitialParameter(void); - - void handleFieldname(string _decsep, string _value); - void handleMeasurement(string _decsep, string _value); + ClassFlowPostProcessing *flowpostprocessing; - + void SetInitialParameter(void); + void handleFieldname(std::string _decsep, std::string _value); + void handleMeasurement(std::string _decsep, std::string _value); public: ClassFlowInfluxDB(); - ClassFlowInfluxDB(std::vector* lfc); - ClassFlowInfluxDB(std::vector* lfc, ClassFlow *_prev); + ClassFlowInfluxDB(std::vector *lfc); + ClassFlowInfluxDB(std::vector *lfc, ClassFlow *_prev); -// string GetInfluxDBMeasurement(); - - bool ReadParameter(FILE* pfile, string& aktparamgraph); - bool doFlow(string time); - string name(){return "ClassFlowInfluxDB";}; + bool ReadParameter(FILE *pfile, std::string &aktparamgraph); + bool doFlow(std::string temp_time); + std::string name() { return "ClassFlowInfluxDB"; }; }; -#endif //CLASSFINFLUXDB_H -#endif //ENABLE_INFLUXDB \ No newline at end of file +#endif // CLASSFINFLUXDB_H diff --git a/code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp b/code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp index 0a823812..012d3659 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.cpp @@ -1,61 +1,62 @@ -#ifdef ENABLE_INFLUXDB +#include "defines.h" + #include +#include + #include "ClassFlowInfluxDBv2.h" + #include "Helper.h" -#include "connect_wlan.h" +#include "connect_wifi_sta.h" #include "time_sntp.h" #include "interface_influxdb.h" #include "ClassFlowPostProcessing.h" #include "esp_log.h" -#include "../../include/defines.h" - #include "ClassLogFile.h" -#include +static const char *TAG = "INFLUXDBV2"; -static const char* TAG = "INFLUXDBV2"; +influxDBv2_controll_config_t influxDBv2_controll_config; void ClassFlowInfluxDBv2::SetInitialParameter(void) { - uri = ""; - bucket = ""; - dborg = ""; - dbtoken = ""; -// dbfield = ""; + influxDBv2_controll_config.enabled = false; + influxDBv2_controll_config.uri = ""; + influxDBv2_controll_config.bucket = ""; + influxDBv2_controll_config.dborg = ""; + influxDBv2_controll_config.dbtoken = ""; + influxDBv2_controll_config.oldValue = ""; - OldValue = ""; - flowpostprocessing = NULL; + flowpostprocessing = NULL; previousElement = NULL; - ListFlowControll = NULL; + ListFlowControll = NULL; + disabled = false; - InfluxDBenable = false; -} +} ClassFlowInfluxDBv2::ClassFlowInfluxDBv2() { SetInitialParameter(); } -ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector* lfc) +ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector *lfc) { SetInitialParameter(); - ListFlowControll = lfc; + for (int i = 0; i < ListFlowControll->size(); ++i) { if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0) { - flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i]; + flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i]; } } } -ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector* lfc, ClassFlow *_prev) +ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector *lfc, ClassFlow *_prev) { SetInitialParameter(); - previousElement = _prev; ListFlowControll = lfc; @@ -63,190 +64,182 @@ ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector* lfc, ClassFlow { if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0) { - flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i]; + flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i]; } } } - -bool ClassFlowInfluxDBv2::ReadParameter(FILE* pfile, string& aktparamgraph) +bool ClassFlowInfluxDBv2::ReadParameter(FILE *pFile, std::string &aktparamgraph) { - std::vector splitted; - - aktparamgraph = trim(aktparamgraph); - printf("akt param: %s\n", aktparamgraph.c_str()); - + aktparamgraph = trim_string_left_right(aktparamgraph); if (aktparamgraph.size() == 0) - if (!this->GetNextParagraph(pfile, aktparamgraph)) - return false; - - if (toUpper(aktparamgraph).compare("[INFLUXDBV2]") != 0) - return false; - - while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) { -// ESP_LOGD(TAG, "while loop reading line: %s", aktparamgraph.c_str()); - splitted = ZerlegeZeile(aktparamgraph); - std::string _param = GetParameterName(splitted[0]); - - if ((toUpper(_param) == "ORG") && (splitted.size() > 1)) + if (!GetNextParagraph(pFile, aktparamgraph)) { - this->dborg = splitted[1]; - } - if ((toUpper(_param) == "TOKEN") && (splitted.size() > 1)) - { - this->dbtoken = splitted[1]; - } - if ((toUpper(_param) == "URI") && (splitted.size() > 1)) - { - this->uri = splitted[1]; - } - if (((toUpper(_param) == "FIELD")) && (splitted.size() > 1)) - { - handleFieldname(splitted[0], splitted[1]); - } - if (((toUpper(_param) == "MEASUREMENT")) && (splitted.size() > 1)) - { - handleMeasurement(splitted[0], splitted[1]); - } - if (((toUpper(splitted[0]) == "BUCKET")) && (splitted.size() > 1)) - { - this->bucket = splitted[1]; + return false; } } - printf("uri: %s\n", uri.c_str()); - printf("org: %s\n", dborg.c_str()); - printf("token: %s\n", dbtoken.c_str()); - - if ((uri.length() > 0) && (bucket.length() > 0) && (dbtoken.length() > 0) && (dborg.length() > 0)) - { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", org: " + dborg + ", token: *****"); -// printf("vor V2 Init\n"); - - -////////////////////////////////////////// NEW //////////////////////////////////////////// -// InfluxDB_V2_Init(uri, bucket, dborg, dbtoken); -// InfluxDB_V2_Init(uri, bucket, dborg, dbtoken); - influxdb.InfluxDBInitV2(uri, bucket, dborg, dbtoken); -////////////////////////////////////////// NEW //////////////////////////////////////////// - -// printf("nach V2 Init\n"); - InfluxDBenable = true; - } else { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBv2 (Verion2 !!!) init skipped as we are missing some parameters"); + if ((to_upper(aktparamgraph).compare("[INFLUXDBV2]") != 0) && (to_upper(aktparamgraph).compare(";[INFLUXDBV2]") != 0)) + { + return false; } - + + if (aktparamgraph[0] == ';') + { + influxDBv2_controll_config.enabled = false; + while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph)); + ESP_LOGD(TAG, "InfluxDBv2 is disabled!"); + + return true; + } + + std::vector splitted; + + while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph)) + { + splitted = split_line(aktparamgraph); + + if (splitted.size() > 1) + { + std::string _param = to_upper(GetParameterName(splitted[0])); + + if (_param == "ORG") + { + influxDBv2_controll_config.dborg = splitted[1]; + } + else if (_param == "TOKEN") + { + influxDBv2_controll_config.dbtoken = splitted[1]; + } + else if (_param == "URI") + { + influxDBv2_controll_config.uri = splitted[1]; + } + else if (_param == "FIELD") + { + handleFieldname(splitted[0], splitted[1]); + } + else if (_param == "MEASUREMENT") + { + handleMeasurement(splitted[0], splitted[1]); + } + else if (_param == "BUCKET") + { + influxDBv2_controll_config.bucket = splitted[1]; + } + } + } + + if ((influxDBv2_controll_config.uri.length() > 0) && (influxDBv2_controll_config.bucket.length() > 0) && (influxDBv2_controll_config.dbtoken.length() > 0) && (influxDBv2_controll_config.dborg.length() > 0)) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDBv2 with uri: " + influxDBv2_controll_config.uri + ", org: " + influxDBv2_controll_config.dborg + ", token: *****"); + influxDB.InfluxDBInitV2(influxDBv2_controll_config.uri, influxDBv2_controll_config.bucket, influxDBv2_controll_config.dborg, influxDBv2_controll_config.dbtoken); + influxDBv2_controll_config.enabled = true; + } + else + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBv2 init skipped as we are missing some parameters"); + } + return true; } -/* -string ClassFlowInfluxDBv2::GetInfluxDBMeasurement() +bool ClassFlowInfluxDBv2::doFlow(std::string temp_time) { - return measurement; -} -*/ - -void ClassFlowInfluxDBv2::handleFieldname(string _decsep, string _value) -{ - string _digit, _decpos; - int _pospunkt = _decsep.find_first_of("."); -// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); - if (_pospunkt > -1) - _digit = _decsep.substr(0, _pospunkt); - else - _digit = "default"; - for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j) + if (!influxDBv2_controll_config.enabled) { - if (_digit == "default") // Set to default first (if nothing else is set) - { - flowpostprocessing->NUMBERS[j]->FieldV2 = _value; - } - if (flowpostprocessing->NUMBERS[j]->name == _digit) - { - flowpostprocessing->NUMBERS[j]->FieldV2 = _value; - } - } -} - -void ClassFlowInfluxDBv2::handleMeasurement(string _decsep, string _value) -{ - string _digit, _decpos; - int _pospunkt = _decsep.find_first_of("."); -// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); - if (_pospunkt > -1) - _digit = _decsep.substr(0, _pospunkt); - else - _digit = "default"; - for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j) - { - if (_digit == "default") // Set to default first (if nothing else is set) - { - flowpostprocessing->NUMBERS[j]->MeasurementV2 = _value; - } - if (flowpostprocessing->NUMBERS[j]->name == _digit) - { - flowpostprocessing->NUMBERS[j]->MeasurementV2 = _value; - } - } -} - - -bool ClassFlowInfluxDBv2::doFlow(string zwtime) -{ - if (!InfluxDBenable) return true; + } - std::string measurement; - std::string result; - std::string resulterror = ""; - std::string resultraw = ""; - std::string resultrate = ""; - std::string resulttimestamp = ""; - long int resulttimeutc = 0; - string zw = ""; - string namenumber = ""; - + std::string measurement = ""; + std::string result = ""; + long int result_timeutc = 0; + std::string name_number = ""; if (flowpostprocessing) { - std::vector* NUMBERS = flowpostprocessing->GetNumbers(); + std::vector *NUMBERS = flowpostprocessing->GetNumbers(); for (int i = 0; i < (*NUMBERS).size(); ++i) { measurement = (*NUMBERS)[i]->MeasurementV2; - result = (*NUMBERS)[i]->ReturnValue; - resultraw = (*NUMBERS)[i]->ReturnRawValue; - resulterror = (*NUMBERS)[i]->ErrorMessageText; - resultrate = (*NUMBERS)[i]->ReturnRateValue; - resulttimestamp = (*NUMBERS)[i]->timeStamp; - resulttimeutc = (*NUMBERS)[i]->timeStampTimeUTC; - + result = (*NUMBERS)[i]->ReturnValue; + result_timeutc = (*NUMBERS)[i]->timeStampTimeUTC; if ((*NUMBERS)[i]->FieldV2.length() > 0) { - namenumber = (*NUMBERS)[i]->FieldV2; + name_number = (*NUMBERS)[i]->FieldV2; } else { - namenumber = (*NUMBERS)[i]->name; - if (namenumber == "default") - namenumber = "value"; + name_number = (*NUMBERS)[i]->name; + if (name_number == "default") + { + name_number = "value"; + } else - namenumber = namenumber + "/value"; + { + name_number = name_number + "/value"; + } } - - printf("vor sende Influx_DB_V2 - namenumber. %s, result: %s, timestampt: %s", namenumber.c_str(), result.c_str(), resulttimestamp.c_str()); - if (result.length() > 0) - influxdb.InfluxDBPublish(measurement, namenumber, result, resulttimeutc); -// InfluxDB_V2_Publish(measurement, namenumber, result, resulttimeutc); + if (result.length() > 0) + { + influxDB.InfluxDBPublish(measurement, name_number, result, result_timeutc); + } } } - - OldValue = result; - + + influxDBv2_controll_config.oldValue = result; + return true; } -#endif //ENABLE_INFLUXDB \ No newline at end of file +void ClassFlowInfluxDBv2::handleFieldname(std::string _decsep, std::string _value) +{ + std::string _digit; + int _pospunkt = _decsep.find_first_of("."); + + if (_pospunkt > -1) + { + _digit = _decsep.substr(0, _pospunkt); + } + else + { + _digit = "default"; + } + + for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j) + { + // Set to default first (if nothing else is set) + if ((_digit == "default") || (flowpostprocessing->NUMBERS[j]->name == _digit)) + { + flowpostprocessing->NUMBERS[j]->FieldV2 = _value; + } + } +} + +void ClassFlowInfluxDBv2::handleMeasurement(std::string _decsep, std::string _value) +{ + std::string _digit; + int _pospunkt = _decsep.find_first_of("."); + + if (_pospunkt > -1) + { + _digit = _decsep.substr(0, _pospunkt); + } + else + { + _digit = "default"; + } + + for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j) + { + // Set to default first (if nothing else is set) + if ((_digit == "default") || (flowpostprocessing->NUMBERS[j]->name == _digit)) + { + flowpostprocessing->NUMBERS[j]->MeasurementV2 = _value; + } + } +} diff --git a/code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.h b/code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.h index 72287894..991d7b3c 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.h +++ b/code/components/jomjol_flowcontroll/ClassFlowInfluxDBv2.h @@ -1,47 +1,46 @@ -#ifdef ENABLE_INFLUXDB - #pragma once #ifndef CLASSFINFLUXDBv2_H #define CLASSFINFLUXDBv2_H -#include "ClassFlow.h" - -#include "ClassFlowPostProcessing.h" - -#include "interface_influxdb.h" - #include -class ClassFlowInfluxDBv2 : - public ClassFlow +#include "ClassFlow.h" +#include "ClassFlowPostProcessing.h" +#include "interface_influxdb.h" + +typedef struct +{ + bool enabled; + std::string uri; + std::string bucket; + std::string dborg; + std::string dbtoken; + std::string dbfield; + std::string oldValue; + +} influxDBv2_controll_config_t; + +extern influxDBv2_controll_config_t influxDBv2_controll_config; + +class ClassFlowInfluxDBv2 : public ClassFlow { protected: - std::string uri, bucket; - std::string dborg, dbtoken, dbfield; - std::string OldValue; - ClassFlowPostProcessing* flowpostprocessing; - bool InfluxDBenable; - - InfluxDB influxdb; - - void SetInitialParameter(void); - - void handleFieldname(string _decsep, string _value); - void handleMeasurement(string _decsep, string _value); + InfluxDB influxDB; + ClassFlowPostProcessing *flowpostprocessing; + void SetInitialParameter(void); + void handleFieldname(std::string _decsep, std::string _value); + void handleMeasurement(std::string _decsep, std::string _value); public: ClassFlowInfluxDBv2(); - ClassFlowInfluxDBv2(std::vector* lfc); - ClassFlowInfluxDBv2(std::vector* lfc, ClassFlow *_prev); + ClassFlowInfluxDBv2(std::vector *lfc); + ClassFlowInfluxDBv2(std::vector *lfc, ClassFlow *_prev); -// string GetInfluxDBMeasurement(); - - bool ReadParameter(FILE* pfile, string& aktparamgraph); - bool doFlow(string time); - string name(){return "ClassFlowInfluxDBv2";}; + bool ReadParameter(FILE *pFile, std::string &aktparamgraph); + bool doFlow(std::string temp_time); + std::string name() { return "ClassFlowInfluxDBv2"; }; }; -#endif //CLASSFINFLUXDBv2_H -#endif //ENABLE_INFLUXDB \ No newline at end of file +#endif // CLASSFINFLUXDBv2_H diff --git a/code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp b/code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp index 00b03444..45bb9af7 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp @@ -1,11 +1,16 @@ -#ifdef ENABLE_MQTT +#include "defines.h" + +#include "ClassFlowMQTT.h" #include #include -#include "ClassFlowMQTT.h" +#include + #include "Helper.h" -#include "connect_wlan.h" -#include "read_wlanini.h" + +#include "connect_wifi_sta.h" +#include "read_network_config.h" + #include "ClassLogFile.h" #include "time_sntp.h" @@ -15,52 +20,73 @@ #include "server_mqtt.h" -#include -#include "../../include/defines.h" - static const char *TAG = "MQTT"; -extern const char* libfive_git_version(void); -extern const char* libfive_git_revision(void); -extern const char* libfive_git_branch(void); +mqtt_controll_config_t mqtt_controll_config; +extern const char *libfive_git_version(void); +extern const char *libfive_git_revision(void); +extern const char *libfive_git_branch(void); void ClassFlowMQTT::SetInitialParameter(void) { - uri = ""; - topic = ""; - topicError = ""; - topicRate = ""; - topicTimeStamp = ""; - maintopic = wlan_config.hostname; + mqtt_controll_config.mqtt_enabled = false; + mqtt_controll_config.mqtt_configOK = false; + mqtt_controll_config.mqtt_initialized = false; + mqtt_controll_config.mqtt_connected = false; - topicUptime = ""; - topicFreeMem = ""; + mqtt_controll_config.HomeAssistantDiscovery = false; - caCertFilename = ""; - clientCertFilename = ""; - clientKeyFilename = ""; - validateServerCert = true; - clientname = wlan_config.hostname; + mqtt_controll_config.esp_mqtt_ID = MQTT_EVENT_ANY; - OldValue = ""; - flowpostprocessing = NULL; - user = ""; - password = ""; - SetRetainFlag = false; + mqtt_controll_config.uri = ""; + mqtt_controll_config.topic = ""; + mqtt_controll_config.topicError = ""; + mqtt_controll_config.topicRate = ""; + mqtt_controll_config.topicTimeStamp = ""; + mqtt_controll_config.maintopic = network_config.hostname; + mqtt_controll_config.discoveryprefix = "homeassistant"; + + mqtt_controll_config.topicUptime = ""; + mqtt_controll_config.topicFreeMem = ""; + + mqtt_controll_config.caCertFilename = ""; + mqtt_controll_config.clientCertFilename = ""; + mqtt_controll_config.clientKeyFilename = ""; + mqtt_controll_config.validateServerCert = true; + mqtt_controll_config.clientname = network_config.hostname; + + mqtt_controll_config.OldValue = ""; + + mqtt_controll_config.user = ""; + mqtt_controll_config.password = ""; + + mqtt_controll_config.lwt_topic = mqtt_controll_config.maintopic + "/" + LWT_TOPIC; + mqtt_controll_config.lwt_connected = LWT_CONNECTED; + mqtt_controll_config.lwt_disconnected = LWT_DISCONNECTED; + + mqtt_controll_config.meterType = ""; + mqtt_controll_config.valueUnit = ""; + mqtt_controll_config.timeUnit = ""; + mqtt_controll_config.rateUnit = "Unit/Minute"; + + mqtt_controll_config.retainFlag = false; + mqtt_controll_config.keepAlive = 25 * 60; + mqtt_controll_config.domoticzintopic = ""; + + flowpostprocessing = NULL; previousElement = NULL; - ListFlowControll = NULL; - disabled = false; - keepAlive = 25*60; - domoticzintopic = ""; -} + ListFlowControll = NULL; -ClassFlowMQTT::ClassFlowMQTT() + disabled = false; +} + +ClassFlowMQTT::ClassFlowMQTT(void) { SetInitialParameter(); } -ClassFlowMQTT::ClassFlowMQTT(std::vector* lfc) +ClassFlowMQTT::ClassFlowMQTT(std::vector *lfc) { SetInitialParameter(); @@ -69,12 +95,12 @@ ClassFlowMQTT::ClassFlowMQTT(std::vector* lfc) { if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0) { - flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i]; + flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i]; } } } -ClassFlowMQTT::ClassFlowMQTT(std::vector* lfc, ClassFlow *_prev) +ClassFlowMQTT::ClassFlowMQTT(std::vector *lfc, ClassFlow *_prev) { SetInitialParameter(); @@ -85,301 +111,348 @@ ClassFlowMQTT::ClassFlowMQTT(std::vector* lfc, ClassFlow *_prev) { if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0) { - flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i]; + flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i]; } } } - -bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph) +bool ClassFlowMQTT::ReadParameter(FILE *pFile, std::string &aktparamgraph) { - std::vector splitted; - - aktparamgraph = trim(aktparamgraph); - + aktparamgraph = trim_string_left_right(aktparamgraph); if (aktparamgraph.size() == 0) - if (!this->GetNextParagraph(pfile, aktparamgraph)) - return false; - - if (toUpper(aktparamgraph).compare("[MQTT]") != 0) // Paragraph does not fit MQTT - return false; - - while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) { - splitted = ZerlegeZeile(aktparamgraph); - std::string _param = GetParameterName(splitted[0]); - if ((toUpper(_param) == "CACERT") && (splitted.size() > 1)) + if (!GetNextParagraph(pFile, aktparamgraph)) { - this->caCertFilename = "/sdcard" + splitted[1]; + return false; } - if ((toUpper(_param) == "VALIDATESERVERCERT") && (splitted.size() > 1)) - { - validateServerCert = alphanumericToBoolean(splitted[1]); - } - if ((toUpper(_param) == "CLIENTCERT") && (splitted.size() > 1)) - { - this->clientCertFilename = "/sdcard" + splitted[1]; - } - if ((toUpper(_param) == "CLIENTKEY") && (splitted.size() > 1)) - { - this->clientKeyFilename = "/sdcard" + splitted[1]; - } - if ((toUpper(_param) == "USER") && (splitted.size() > 1)) - { - this->user = splitted[1]; - } - if ((toUpper(_param) == "PASSWORD") && (splitted.size() > 1)) - { - this->password = splitted[1]; - } - if ((toUpper(_param) == "URI") && (splitted.size() > 1)) - { - this->uri = splitted[1]; - } - if ((toUpper(_param) == "RETAINMESSAGES") && (splitted.size() > 1)) - { - SetRetainFlag = alphanumericToBoolean(splitted[1]); - setMqtt_Server_Retain(SetRetainFlag); - } - if ((toUpper(_param) == "HOMEASSISTANTDISCOVERY") && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "TRUE") - SetHomeassistantDiscoveryEnabled(true); - } - if ((toUpper(_param) == "METERTYPE") && (splitted.size() > 1)) { - /* Use meter type for the device class - Make sure it is a listed one on https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes */ - if (toUpper(splitted[1]) == "WATER_M3") { - mqttServer_setMeterType("water", "m³", "h", "m³/h"); - } - else if (toUpper(splitted[1]) == "WATER_L") { - mqttServer_setMeterType("water", "L", "h", "L/h"); - } - else if (toUpper(splitted[1]) == "WATER_FT3") { - mqttServer_setMeterType("water", "ft³", "min", "ft³/min"); // min = Minutes - } - else if (toUpper(splitted[1]) == "WATER_GAL") { - mqttServer_setMeterType("water", "gal", "h", "gal/h"); - } - else if (toUpper(splitted[1]) == "WATER_GAL_MIN") { - mqttServer_setMeterType("water", "gal", "min", "gal/min"); // min = Minutes - } - else if (toUpper(splitted[1]) == "GAS_M3") { - mqttServer_setMeterType("gas", "m³", "h", "m³/h"); - } - else if (toUpper(splitted[1]) == "GAS_FT3") { - mqttServer_setMeterType("gas", "ft³", "min", "ft³/min"); // min = Minutes - } - else if (toUpper(splitted[1]) == "ENERGY_WH") { - mqttServer_setMeterType("energy", "Wh", "h", "W"); - } - else if (toUpper(splitted[1]) == "ENERGY_KWH") { - mqttServer_setMeterType("energy", "kWh", "h", "kW"); - } - else if (toUpper(splitted[1]) == "ENERGY_MWH") { - mqttServer_setMeterType("energy", "MWh", "h", "MW"); - } - else if (toUpper(splitted[1]) == "ENERGY_GJ") { - mqttServer_setMeterType("energy", "GJ", "h", "GJ/h"); - } - else if (toUpper(splitted[1]) == "TEMPERATURE_C") { - mqttServer_setMeterType("temperature", "°C", "min", "°C/min"); // min = Minutes - } - else if (toUpper(splitted[1]) == "TEMPERATURE_F") { - mqttServer_setMeterType("temperature", "°F", "min", "°F/min"); // min = Minutes - } - else if (toUpper(splitted[1]) == "TEMPERATURE_K") { - mqttServer_setMeterType("temperature", "K", "min", "K/m"); // min = Minutes - } - } - - if ((toUpper(_param) == "CLIENTID") && (splitted.size() > 1)) - { - this->clientname = splitted[1]; - } - - if (((toUpper(_param) == "TOPIC") || (toUpper(splitted[0]) == "MAINTOPIC")) && (splitted.size() > 1)) - { - maintopic = splitted[1]; - } - - if (((toUpper(_param) == "DOMOTICZTOPICIN")) && (splitted.size() > 1)) - { - this->domoticzintopic = splitted[1]; - } - - if (((toUpper(_param) == "DOMOTICZIDX")) && (splitted.size() > 1)) - { - handleIdx(splitted[0], splitted[1]); - } - } - /* Note: - * Originally, we started the MQTT client here. - * How ever we need the interval parameter from the ClassFlowControll, but that only gets started later. - * To work around this, we delay the start and trigger it from ClassFlowControll::ReadParameter() */ + if ((to_upper(aktparamgraph).compare("[MQTT]") != 0) && (to_upper(aktparamgraph).compare(";[MQTT]") != 0)) + { + // Paragraph does not fit MQTT + return false; + } - mqttServer_setMainTopic(maintopic); - mqttServer_setDmoticzInTopic(domoticzintopic); + if (aktparamgraph[0] == ';') + { + mqtt_controll_config.mqtt_enabled = false; + while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph)); + ESP_LOGD(TAG, "mqtt is disabled!"); + + return true; + } + + std::vector splitted; + + while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph)) + { + splitted = split_line(aktparamgraph); + + if (splitted.size() > 1) + { + std::string _param = to_upper(GetParameterName(splitted[0])); + + if (_param == "CACERT") + { + mqtt_controll_config.caCertFilename = "/sdcard" + splitted[1]; + } + else if (_param == "VALIDATESERVERCERT") + { + mqtt_controll_config.validateServerCert = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "CLIENTCERT") + { + mqtt_controll_config.clientCertFilename = "/sdcard" + splitted[1]; + } + else if (_param == "CLIENTKEY") + { + mqtt_controll_config.clientKeyFilename = "/sdcard" + splitted[1]; + } + else if (_param == "USER") + { + mqtt_controll_config.user = splitted[1]; + } + else if (_param == "PASSWORD") + { + mqtt_controll_config.password = splitted[1]; + } + else if (_param == "URI") + { + mqtt_controll_config.uri = splitted[1]; + } + else if (_param == "RETAINMESSAGES") + { + mqtt_controll_config.retainFlag = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "HOMEASSISTANTDISCOVERY") + { + if (to_upper(splitted[1]) == "TRUE") + { + mqtt_controll_config.HomeAssistantDiscovery = true; + } + } + else if (_param == "METERTYPE") + { + std::string _value = to_upper(splitted[1]); + + /* Use meter type for the device class + Make sure it is a listed one on https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes */ + if (_value == "WATER_M3") + { + SetMeterType("water", "m³", "h", "m³/h"); + } + else if (_value == "WATER_L") + { + SetMeterType("water", "L", "h", "L/h"); + } + else if (_value == "WATER_FT3") + { + SetMeterType("water", "ft³", "min", "ft³/min"); // min = Minutes + } + else if (_value == "WATER_GAL") + { + SetMeterType("water", "gal", "h", "gal/h"); + } + else if (_value == "WATER_GAL_MIN") + { + SetMeterType("water", "gal", "min", "gal/min"); // min = Minutes + } + else if (_value == "GAS_M3") + { + SetMeterType("gas", "m³", "h", "m³/h"); + } + else if (_value == "GAS_FT3") + { + SetMeterType("gas", "ft³", "min", "ft³/min"); // min = Minutes + } + else if (_value == "ENERGY_WH") + { + SetMeterType("energy", "Wh", "h", "W"); + } + else if (_value == "ENERGY_KWH") + { + SetMeterType("energy", "kWh", "h", "kW"); + } + else if (_value == "ENERGY_MWH") + { + SetMeterType("energy", "MWh", "h", "MW"); + } + else if (_value == "ENERGY_GJ") + { + SetMeterType("energy", "GJ", "h", "GJ/h"); + } + else if (_value == "TEMPERATURE_C") + { + SetMeterType("temperature", "°C", "min", "°C/min"); // min = Minutes + } + else if (_value == "TEMPERATURE_F") + { + SetMeterType("temperature", "°F", "min", "°F/min"); // min = Minutes + } + else if (_value == "TEMPERATURE_K") + { + SetMeterType("temperature", "K", "min", "K/m"); // min = Minutes + } + } + else if (_param == "CLIENTID") + { + mqtt_controll_config.clientname = splitted[1]; + } + else if ((_param == "TOPIC") || (_param == "MAINTOPIC")) + { + mqtt_controll_config.maintopic = splitted[1]; + } + else if (_param == "DISCOVERYPREFIX") + { + mqtt_controll_config.discoveryprefix = splitted[1]; + } + else if (_param == "DOMOTICZTOPICIN") + { + mqtt_controll_config.domoticzintopic = splitted[1]; + } + else if (_param == "DOMOTICZIDX") + { + handleIdx(splitted[0], splitted[1]); + } + } + } + + if ((mqtt_controll_config.uri.length() > 0) && (mqtt_controll_config.user.length() > 0)) + { + /* Note: + * Originally, we started the MQTT client here. + * How ever we need the interval parameter from the ClassFlowControl, but that only gets started later. + * To work around this, we delay the start and trigger it from ClassFlowControl::ReadParameter() */ + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init MQTT with uri: " + mqtt_controll_config.uri + ", user: " + mqtt_controll_config.user); + + if (mqtt_controll_config.domoticzintopic.length() > 0) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init MQTT with domoticzintopic: " + mqtt_controll_config.domoticzintopic); + } + + mqtt_controll_config.mqtt_enabled = true; + } + else + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "MQTT init skipped as we are missing some parameters"); + } return true; } - -bool ClassFlowMQTT::Start(float AutoInterval) +void ClassFlowMQTT::SetMeterType(std::string _meterType, std::string _valueUnit, std::string _timeUnit, std::string _rateUnit) { - roundInterval = AutoInterval; // Minutes - keepAlive = roundInterval * 60 * 2.5; // Seconds, make sure it is greater thatn 2 rounds! + mqtt_controll_config.meterType = _meterType; + mqtt_controll_config.valueUnit = _valueUnit; + mqtt_controll_config.timeUnit = _timeUnit; + mqtt_controll_config.rateUnit = _rateUnit; +} - std::stringstream stream; - stream << std::fixed << std::setprecision(1) << "Digitizer interval is " << roundInterval << - " minutes => setting MQTT LWT timeout to " << ((float)keepAlive/60) << " minutes."; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, stream.str()); +bool ClassFlowMQTT::Start(float AutoInterval) +{ + mqtt_controll_config.roundInterval = AutoInterval; // Minutes + mqtt_controll_config.keepAlive = mqtt_controll_config.roundInterval * 60 * 2.5; // Seconds, make sure it is greater thatn 2 rounds! - mqttServer_setParameter(flowpostprocessing->GetNumbers(), keepAlive, roundInterval); + mqttServer_setParameter(flowpostprocessing->GetNumbers()); - bool MQTTConfigCheck = MQTT_Configure(uri, clientname, user, password, maintopic, domoticzintopic, LWT_TOPIC, LWT_CONNECTED, - LWT_DISCONNECTED, caCertFilename, validateServerCert, clientCertFilename, clientKeyFilename, - keepAlive, SetRetainFlag, (void *)&GotConnected); - - if (!MQTTConfigCheck) { + if (!MQTT_Configure((void *)&GotConnected)) + { return false; } return (MQTT_Init() == 1); } - -bool ClassFlowMQTT::doFlow(string zwtime) +bool ClassFlowMQTT::doFlow(std::string time) { - bool success; - std::string result; - std::string resulterror = ""; - std::string resultraw = ""; - std::string resultpre = ""; - std::string resultrate = ""; // Always Unit / Minute - std::string resultRatePerTimeUnit = ""; // According to selection - std::string resulttimestamp = ""; - std::string resultchangabs = ""; - string zw = ""; - string namenumber = ""; - string domoticzpayload = ""; - string DomoticzIdx = ""; int qos = 1; + std::string value_temp = ""; + std::string error_message_text_temp = ""; + std::string raw_value_temp = ""; + // std::string pre_value_temp = ""; + std::string rate_value_temp = ""; // Always Unit / Minute + std::string time_stamp_temp = ""; + std::string change_absolute_temp = ""; + /* Send the the Homeassistant Discovery and the Static Topics in case they where scheduled */ sendDiscovery_and_static_Topics(); - - success = publishSystemData(qos); + bool success = publishSystemData(qos); if (flowpostprocessing && getMQTTisConnected()) { - std::vector* NUMBERS = flowpostprocessing->GetNumbers(); - + std::vector *NUMBERS = flowpostprocessing->GetNumbers(); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing MQTT topics..."); for (int i = 0; i < (*NUMBERS).size(); ++i) { - result = (*NUMBERS)[i]->ReturnValue; - resultraw = (*NUMBERS)[i]->ReturnRawValue; - resultpre = (*NUMBERS)[i]->ReturnPreValue; - resulterror = (*NUMBERS)[i]->ErrorMessageText; - resultrate = (*NUMBERS)[i]->ReturnRateValue; // Unit per minutes - resultchangabs = (*NUMBERS)[i]->ReturnChangeAbsolute; // Units per round - resulttimestamp = (*NUMBERS)[i]->timeStamp; + value_temp = (*NUMBERS)[i]->ReturnValue; + raw_value_temp = (*NUMBERS)[i]->ReturnRawValue; + // pre_value_temp = (*NUMBERS)[i]->ReturnPreValue; + error_message_text_temp = (*NUMBERS)[i]->ErrorMessageText; + rate_value_temp = (*NUMBERS)[i]->ReturnRateValue; // Unit per minutes + change_absolute_temp = (*NUMBERS)[i]->ReturnChangeAbsolute; // Units per round + time_stamp_temp = (*NUMBERS)[i]->timeStamp; - DomoticzIdx = (*NUMBERS)[i]->DomoticzIdx; - domoticzpayload = "{\"command\":\"udevice\",\"idx\":" + DomoticzIdx + ",\"svalue\":\""+ result + "\"}"; + std::string DomoticzIdx = (*NUMBERS)[i]->DomoticzIdx; + std::string domoticzpayload = "{\"command\":\"udevice\",\"idx\":" + DomoticzIdx + ",\"svalue\":\"" + value_temp + "\"}"; - namenumber = (*NUMBERS)[i]->name; - if (namenumber == "default") - namenumber = maintopic + "/"; + std::string name_temp = (*NUMBERS)[i]->name; + + if (name_temp == "default") + { + name_temp = mqtt_controll_config.maintopic + "/"; + } else - namenumber = maintopic + "/" + namenumber + "/"; + { + name_temp = mqtt_controll_config.maintopic + "/" + name_temp + "/"; + } - if ((domoticzintopic.length() > 0) && (result.length() > 0)) - success |= MQTTPublish(domoticzintopic, domoticzpayload, qos, SetRetainFlag); + if ((mqtt_controll_config.domoticzintopic.length() > 0) && (value_temp.length() > 0)) + { + success |= MQTTPublish(mqtt_controll_config.domoticzintopic, domoticzpayload, qos, mqtt_controll_config.retainFlag); + } - if (result.length() > 0) - success |= MQTTPublish(namenumber + "value", result, qos, SetRetainFlag); - if (resulterror.length() > 0) - success |= MQTTPublish(namenumber + "error", resulterror, qos, SetRetainFlag); + if (value_temp.length() > 0) + { + success |= MQTTPublish(name_temp + "value", value_temp, qos, mqtt_controll_config.retainFlag); + } + + if (error_message_text_temp.length() > 0) + { + success |= MQTTPublish(name_temp + "error", error_message_text_temp, qos, mqtt_controll_config.retainFlag); + } + + if (rate_value_temp.length() > 0) + { + success |= MQTTPublish(name_temp + "rate", rate_value_temp, qos, mqtt_controll_config.retainFlag); - if (resultrate.length() > 0) { - success |= MQTTPublish(namenumber + "rate", resultrate, qos, SetRetainFlag); - std::string resultRatePerTimeUnit; - if (getTimeUnit() == "h") { // Need conversion to be per hour + if (mqtt_controll_config.timeUnit == "h") + { + // Need conversion to be per hour resultRatePerTimeUnit = resultRatePerTimeUnit = to_string((*NUMBERS)[i]->FlowRateAct * 60); // per minutes => per hour } - else { // Keep per minute - resultRatePerTimeUnit = resultrate; + else + { + // Keep per minute + resultRatePerTimeUnit = rate_value_temp; } - success |= MQTTPublish(namenumber + "rate_per_time_unit", resultRatePerTimeUnit, qos, SetRetainFlag); + success |= MQTTPublish(name_temp + "rate_per_time_unit", resultRatePerTimeUnit, qos, mqtt_controll_config.retainFlag); } - if (resultchangabs.length() > 0) { - success |= MQTTPublish(namenumber + "changeabsolut", resultchangabs, qos, SetRetainFlag); // Legacy API - success |= MQTTPublish(namenumber + "rate_per_digitization_round", resultchangabs, qos, SetRetainFlag); + if (change_absolute_temp.length() > 0) + { + success |= MQTTPublish(name_temp + "changeabsolut", change_absolute_temp, qos, mqtt_controll_config.retainFlag); // Legacy API + success |= MQTTPublish(name_temp + "rate_per_digitization_round", change_absolute_temp, qos, mqtt_controll_config.retainFlag); } - if (resultraw.length() > 0) - success |= MQTTPublish(namenumber + "raw", resultraw, qos, SetRetainFlag); + if (raw_value_temp.length() > 0) + { + success |= MQTTPublish(name_temp + "raw", raw_value_temp, qos, mqtt_controll_config.retainFlag); + } - if (resulttimestamp.length() > 0) - success |= MQTTPublish(namenumber + "timestamp", resulttimestamp, qos, SetRetainFlag); + if (time_stamp_temp.length() > 0) + { + success |= MQTTPublish(name_temp + "timestamp", time_stamp_temp, qos, mqtt_controll_config.retainFlag); + } std::string json = flowpostprocessing->getJsonFromNumber(i, "\n"); - success |= MQTTPublish(namenumber + "json", json, qos, SetRetainFlag); + success |= MQTTPublish(name_temp + "json", json, qos, mqtt_controll_config.retainFlag); } } - - /* Disabled because this is no longer a use case */ - // else - // { - // for (int i = 0; i < ListFlowControll->size(); ++i) - // { - // zw = (*ListFlowControll)[i]->getReadout(); - // if (zw.length() > 0) - // { - // if (result.length() == 0) - // result = zw; - // else - // result = result + "\t" + zw; - // } - // } - // success |= MQTTPublish(topic, result, qos, SetRetainFlag); - // } - - OldValue = result; - if (!success) { + mqtt_controll_config.OldValue = value_temp; + + if (!success) + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "One or more MQTT topics failed to be published!"); } - + return true; } -void ClassFlowMQTT::handleIdx(string _decsep, string _value) + +void ClassFlowMQTT::handleIdx(std::string _decsep, std::string _value) { - string _digit, _decpos; + std::string _digit; int _pospunkt = _decsep.find_first_of("."); -// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); + if (_pospunkt > -1) + { _digit = _decsep.substr(0, _pospunkt); + } else + { _digit = "default"; + } + for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j) { - if (_digit == "default") // Set to default first (if nothing else is set) - { - flowpostprocessing->NUMBERS[j]->DomoticzIdx = _value; - } - if (flowpostprocessing->NUMBERS[j]->name == _digit) + // Set to default first (if nothing else is set) + if ((_digit == "default") || (flowpostprocessing->NUMBERS[j]->name == _digit)) { flowpostprocessing->NUMBERS[j]->DomoticzIdx = _value; } } } - -#endif //ENABLE_MQTT diff --git a/code/components/jomjol_flowcontroll/ClassFlowMQTT.h b/code/components/jomjol_flowcontroll/ClassFlowMQTT.h index 272d9a21..d756e1f8 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowMQTT.h +++ b/code/components/jomjol_flowcontroll/ClassFlowMQTT.h @@ -1,43 +1,80 @@ -#ifdef ENABLE_MQTT - #pragma once #ifndef CLASSFFLOWMQTT_H #define CLASSFFLOWMQTT_H -#include "ClassFlow.h" +#include +#include +#include "ClassFlow.h" #include "ClassFlowPostProcessing.h" -#include +typedef struct +{ + bool mqtt_enabled; + bool mqtt_configOK; + bool mqtt_initialized; + bool mqtt_connected; -class ClassFlowMQTT : - public ClassFlow + bool HomeAssistantDiscovery; + + esp_mqtt_event_id_t esp_mqtt_ID; + + std::string uri; + std::string topic; + std::string topicError; + std::string clientname; + std::string topicRate; + std::string topicTimeStamp; + std::string topicUptime; + std::string topicFreeMem; + std::string OldValue; + + std::string user; + std::string password; + std::string caCertFilename; + std::string clientCertFilename; + std::string clientKeyFilename; + bool validateServerCert; + + std::string maintopic; + std::string discoveryprefix; + std::string domoticzintopic; + + std::string lwt_topic; + std::string lwt_connected; + std::string lwt_disconnected; + + std::string meterType; + std::string valueUnit; + std::string timeUnit; + std::string rateUnit; + + float roundInterval; // in Minutes + bool retainFlag; + int keepAlive; // in Seconds +} mqtt_controll_config_t; + +extern mqtt_controll_config_t mqtt_controll_config; + +class ClassFlowMQTT : public ClassFlow { protected: - std::string uri, topic, topicError, clientname, topicRate, topicTimeStamp, topicUptime, topicFreeMem; - std::string OldValue; - ClassFlowPostProcessing* flowpostprocessing; - std::string user, password; - std::string caCertFilename, clientCertFilename, clientKeyFilename; - bool validateServerCert; - bool SetRetainFlag; - int keepAlive; // Seconds - float roundInterval; // Minutes - std::string maintopic, domoticzintopic; - void SetInitialParameter(void); - void handleIdx(string _decsep, string _value); + ClassFlowPostProcessing *flowpostprocessing; + + void SetInitialParameter(void); + void SetMeterType(std::string _meterType, std::string _valueUnit, std::string _timeUnit, std::string _rateUnit); + void handleIdx(std::string _decsep, std::string _value); public: - ClassFlowMQTT(); - ClassFlowMQTT(std::vector* lfc); - ClassFlowMQTT(std::vector* lfc, ClassFlow *_prev); + ClassFlowMQTT(void); + ClassFlowMQTT(std::vector *lfc); + ClassFlowMQTT(std::vector *lfc, ClassFlow *_prev); bool Start(float AutoInterval); - bool ReadParameter(FILE* pfile, string& aktparamgraph); - bool doFlow(string time); - string name(){return "ClassFlowMQTT";}; + bool ReadParameter(FILE *pFile, std::string &aktparamgraph); + bool doFlow(std::string time); + std::string name(void) { return "ClassFlowMQTT"; }; }; -#endif //CLASSFFLOWMQTT_H -#endif //ENABLE_MQTT \ No newline at end of file +#endif // CLASSFFLOWMQTT_H diff --git a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp index 70cf7791..85ccf984 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp @@ -1,62 +1,69 @@ -#include "ClassFlowPostProcessing.h" -#include "Helper.h" -#include "ClassFlowTakeImage.h" -#include "ClassLogFile.h" +#include "defines.h" #include #include - #include +#include +#include "ClassFlowPostProcessing.h" +#include "MainFlowControl.h" +#include "ClassFlowTakeImage.h" +#include "ClassLogFile.h" + +#include "Helper.h" #include "time_sntp.h" -#include "esp_log.h" -#include "../../include/defines.h" +static const char *TAG = "POSTPROCESS"; -static const char* TAG = "POSTPROC"; +std::string ClassFlowPostProcessing::getNumbersName() +{ + std::string ret = ""; -std::string ClassFlowPostProcessing::getNumbersName() { - std::string ret=""; - - for (int i = 0; i < NUMBERS.size(); ++i) { + for (int i = 0; i < NUMBERS.size(); ++i) + { ret += NUMBERS[i]->name; - - if (i < NUMBERS.size()-1) { + + if (i < NUMBERS.size() - 1) + { ret = ret + "\t"; } } - // ESP_LOGI(TAG, "Result ClassFlowPostProcessing::getNumbersName: %s", ret.c_str()); - return ret; } -std::string ClassFlowPostProcessing::GetJSON(std::string _lineend) { - std::string json="{" + _lineend; +std::string ClassFlowPostProcessing::GetJSON(std::string _lineend) +{ + std::string json = "{" + _lineend; - for (int i = 0; i < NUMBERS.size(); ++i) { - json += "\"" + NUMBERS[i]->name + "\":" + _lineend; + for (int i = 0; i < NUMBERS.size(); ++i) + { + json += "\"" + NUMBERS[i]->name + "\":" + _lineend; json += getJsonFromNumber(i, _lineend) + _lineend; - if ((i+1) < NUMBERS.size()) { + if ((i + 1) < NUMBERS.size()) + { json += "," + _lineend; } } - + json += "}"; return json; } -string ClassFlowPostProcessing::getJsonFromNumber(int i, std::string _lineend) { +std::string ClassFlowPostProcessing::getJsonFromNumber(int i, std::string _lineend) +{ std::string json = ""; json += " {" + _lineend; - if (NUMBERS[i]->ReturnValue.length() > 0) { + if (NUMBERS[i]->ReturnValue.length() > 0) + { json += " \"value\": \"" + NUMBERS[i]->ReturnValue + "\"," + _lineend; } - else { + else + { json += " \"value\": \"\"," + _lineend; } @@ -64,10 +71,12 @@ string ClassFlowPostProcessing::getJsonFromNumber(int i, std::string _lineend) { json += " \"pre\": \"" + NUMBERS[i]->ReturnPreValue + "\"," + _lineend; json += " \"error\": \"" + NUMBERS[i]->ErrorMessageText + "\"," + _lineend; - if (NUMBERS[i]->ReturnRateValue.length() > 0) { + if (NUMBERS[i]->ReturnRateValue.length() > 0) + { json += " \"rate\": \"" + NUMBERS[i]->ReturnRateValue + "\"," + _lineend; } - else { + else + { json += " \"rate\": \"\"," + _lineend; } @@ -77,120 +86,131 @@ string ClassFlowPostProcessing::getJsonFromNumber(int i, std::string _lineend) { return json; } -string ClassFlowPostProcessing::GetPreValue(std::string _number) { +std::string ClassFlowPostProcessing::GetPreValue(std::string _number) +{ std::string result; int index = -1; - if (_number == "") { + if (_number == "") + { _number = "default"; } - for (int i = 0; i < NUMBERS.size(); ++i) { - if (NUMBERS[i]->name == _number) { + for (int i = 0; i < NUMBERS.size(); ++i) + { + if (NUMBERS[i]->name == _number) + { index = i; } } - if (index == -1) { + if (index == -1) + { return std::string(""); } - result = RundeOutput(NUMBERS[index]->PreValue, NUMBERS[index]->Nachkomma); + result = round_output(NUMBERS[index]->PreValue, NUMBERS[index]->Nachkomma); return result; } -bool ClassFlowPostProcessing::SetPreValue(double _newvalue, string _numbers, bool _extern) { - //ESP_LOGD(TAG, "SetPrevalue: %f, %s", zw, _numbers.c_str()); - - for (int j = 0; j < NUMBERS.size(); ++j) { - //ESP_LOGD(TAG, "Number %d, %s", j, NUMBERS[j]->name.c_str()); - - if (NUMBERS[j]->name == _numbers) { - if (_newvalue >= 0) { +bool ClassFlowPostProcessing::SetPreValue(double _newvalue, std::string _numbers, bool _extern) +{ + for (int j = 0; j < NUMBERS.size(); ++j) + { + if (NUMBERS[j]->name == _numbers) + { + if (_newvalue >= 0) + { // if new value posivive, use provided value to preset PreValue NUMBERS[j]->PreValue = _newvalue; } - else { + else + { // if new value negative, use last raw value to preset PreValue - char* p; + char *p; double ReturnRawValueAsDouble = strtod(NUMBERS[j]->ReturnRawValue.c_str(), &p); - - if (ReturnRawValueAsDouble == 0) { + + if (ReturnRawValueAsDouble == 0) + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "SetPreValue: RawValue not a valid value for further processing: " + NUMBERS[j]->ReturnRawValue); return false; } - + NUMBERS[j]->PreValue = ReturnRawValueAsDouble; } NUMBERS[j]->ReturnPreValue = std::to_string(NUMBERS[j]->PreValue); NUMBERS[j]->PreValueOkay = true; - if (_extern) { + if (_extern) + { time(&(NUMBERS[j]->timeStampLastPreValue)); localtime(&(NUMBERS[j]->timeStampLastPreValue)); } - //ESP_LOGD(TAG, "Found %d! - set to %.8f", j, NUMBERS[j]->PreValue); - - UpdatePreValueINI = true; // Only update prevalue file if a new value is set + // ESP_LOGD(TAG, "Found %d! - set to %.8f", j, NUMBERS[j]->PreValue); + + UpdatePreValueINI = true; // Only update prevalue file if a new value is set SavePreValue(); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "SetPreValue: PreValue for " + NUMBERS[j]->name + " set to " + std::to_string(NUMBERS[j]->PreValue)); return true; } } - + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "SetPreValue: Numbersname not found or not valid"); - return false; // No new value was set (e.g. wrong numbersname, no numbers at all) + return false; // No new value was set (e.g. wrong numbersname, no numbers at all) } -bool ClassFlowPostProcessing::LoadPreValue(void) { - std::vector splitted; - FILE* pFile; - char zw[1024]; - string zwtime, zwvalue, name; - bool _done = false; +bool ClassFlowPostProcessing::LoadPreValue(void) +{ + UpdatePreValueINI = false; // Conversion to the new format - UpdatePreValueINI = false; // Conversion to the new format - - pFile = fopen(FilePreValue.c_str(), "r"); - - if (pFile == NULL) { + FILE *pFile = fopen(FilePreValue.c_str(), "r"); + if (pFile == NULL) + { return false; } // Makes sure that an empty file is treated as such. - zw[0] = '\0'; + char temp_char[1024]; + temp_char[0] = '\0'; - fgets(zw, 1024, pFile); - ESP_LOGD(TAG, "Read line Prevalue.ini: %s", zw); - zwtime = trim(std::string(zw)); - - if (zwtime.length() == 0) { + fgets(temp_char, 1024, pFile); + ESP_LOGD(TAG, "Read line Prevalue.ini: %s", temp_char); + std::string temp_time = trim_string_left_right(std::string(temp_char)); + + if (temp_time.length() == 0) + { return false; } - splitted = HelperZerlegeZeile(zwtime, "\t"); - - // Conversion to the new format - if (splitted.size() > 1) { - while ((splitted.size() > 1) && !_done) { - name = trim(splitted[0]); - zwtime = trim(splitted[1]); - zwvalue = trim(splitted[2]); + std::string temp_value, temp_name; + bool _done = false; + std::vector splitted = split_line(temp_time, "\t"); - for (int j = 0; j < NUMBERS.size(); ++j) { - if (NUMBERS[j]->name == name) { - NUMBERS[j]->PreValue = stod(zwvalue.c_str()); - NUMBERS[j]->ReturnPreValue = RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma + 1); // To be on the safe side, 1 digit more, as Exgtended Resolution may be on (will only be set during the first run). + // Conversion to the new format + if (splitted.size() > 1) + { + while ((splitted.size() > 1) && !_done) + { + temp_name = trim_string_left_right(splitted[0]); + temp_time = trim_string_left_right(splitted[1]); + temp_value = trim_string_left_right(splitted[2]); + + for (int j = 0; j < NUMBERS.size(); ++j) + { + if (NUMBERS[j]->name == temp_name) + { + NUMBERS[j]->PreValue = stod(temp_value.c_str()); + NUMBERS[j]->ReturnPreValue = round_output(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma + 1); // To be on the safe side, 1 digit more, as Exgtended Resolution may be on (will only be set during the first run). time_t tStart; int yy, month, dd, hh, mm, ss; struct tm whenStart; - sscanf(zwtime.c_str(), PREVALUE_TIME_FORMAT_INPUT, &yy, &month, &dd, &hh, &mm, &ss); + sscanf(temp_time.c_str(), PREVALUE_TIME_FORMAT_INPUT, &yy, &month, &dd, &hh, &mm, &ss); whenStart.tm_year = yy - 1900; whenStart.tm_mon = month - 1; whenStart.tm_mday = dd; @@ -205,45 +225,51 @@ bool ClassFlowPostProcessing::LoadPreValue(void) { localtime(&tStart); double difference = difftime(tStart, NUMBERS[j]->timeStampLastPreValue); difference /= 60; - - if (difference > PreValueAgeStartup) { + + if (difference > PreValueAgeStartup) + { NUMBERS[j]->PreValueOkay = false; } - else { + else + { NUMBERS[j]->PreValueOkay = true; } } } - if (!fgets(zw, 1024, pFile)) { + if (!fgets(temp_char, 1024, pFile)) + { _done = true; } - else { - ESP_LOGD(TAG, "Read line Prevalue.ini: %s", zw); - splitted = HelperZerlegeZeile(trim(std::string(zw)), "\t"); - - if (splitted.size() > 1) { - name = trim(splitted[0]); - zwtime = trim(splitted[1]); - zwvalue = trim(splitted[2]); + else + { + ESP_LOGD(TAG, "Read line Prevalue.ini: %s", temp_char); + splitted = split_line(trim_string_left_right(std::string(temp_char)), "\t"); + + if (splitted.size() > 1) + { + temp_name = trim_string_left_right(splitted[0]); + temp_time = trim_string_left_right(splitted[1]); + temp_value = trim_string_left_right(splitted[2]); } } } fclose(pFile); - } - else { + } + else + { // Old Format - fgets(zw, 1024, pFile); + fgets(temp_char, 1024, pFile); fclose(pFile); - ESP_LOGD(TAG, "%s", zw); - zwvalue = trim(std::string(zw)); - NUMBERS[0]->PreValue = stod(zwvalue.c_str()); + ESP_LOGD(TAG, "%s", temp_char); + temp_value = trim_string_left_right(std::string(temp_char)); + NUMBERS[0]->PreValue = stod(temp_value.c_str()); time_t tStart; int yy, month, dd, hh, mm, ss; struct tm whenStart; - sscanf(zwtime.c_str(), PREVALUE_TIME_FORMAT_INPUT, &yy, &month, &dd, &hh, &mm, &ss); + sscanf(temp_time.c_str(), PREVALUE_TIME_FORMAT_INPUT, &yy, &month, &dd, &hh, &mm, &ss); whenStart.tm_year = yy - 1900; whenStart.tm_mon = month - 1; whenStart.tm_mday = dd; @@ -260,49 +286,52 @@ bool ClassFlowPostProcessing::LoadPreValue(void) { localtime(&tStart); double difference = difftime(tStart, NUMBERS[0]->timeStampLastPreValue); difference /= 60; - - if (difference > PreValueAgeStartup) { + + if (difference > PreValueAgeStartup) + { return false; } NUMBERS[0]->Value = NUMBERS[0]->PreValue; - NUMBERS[0]->ReturnValue = to_string(NUMBERS[0]->Value); + NUMBERS[0]->ReturnValue = std::to_string(NUMBERS[0]->Value); - if (NUMBERS[0]->digit_roi || NUMBERS[0]->analog_roi) { - NUMBERS[0]->ReturnValue = RundeOutput(NUMBERS[0]->Value, NUMBERS[0]->Nachkomma); + if (NUMBERS[0]->digit_roi || NUMBERS[0]->analog_roi) + { + NUMBERS[0]->ReturnValue = round_output(NUMBERS[0]->Value, NUMBERS[0]->Nachkomma); } - UpdatePreValueINI = true; // Conversion to the new format + UpdatePreValueINI = true; // Conversion to the new format SavePreValue(); - } + } return true; } -void ClassFlowPostProcessing::SavePreValue() { - FILE* pFile; - string _zw; - +void ClassFlowPostProcessing::SavePreValue() +{ // PreValues unchanged --> File does not have to be rewritten - if (!UpdatePreValueINI) { + if (!UpdatePreValueINI) + { return; } - pFile = fopen(FilePreValue.c_str(), "w"); + FILE *pFile = fopen(FilePreValue.c_str(), "w"); - for (int j = 0; j < NUMBERS.size(); ++j) { + for (int j = 0; j < NUMBERS.size(); ++j) + { char buffer[80]; - struct tm* timeinfo = localtime(&NUMBERS[j]->timeStampLastPreValue); + struct tm *timeinfo = localtime(&NUMBERS[j]->timeStampLastPreValue); strftime(buffer, 80, PREVALUE_TIME_FORMAT_OUTPUT, timeinfo); NUMBERS[j]->timeStamp = std::string(buffer); NUMBERS[j]->timeStampTimeUTC = NUMBERS[j]->timeStampLastPreValue; // ESP_LOGD(TAG, "SaverPreValue %d, Value: %f, Nachkomma %d", j, NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma); - _zw = NUMBERS[j]->name + "\t" + NUMBERS[j]->timeStamp + "\t" + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + "\n"; - ESP_LOGD(TAG, "Write PreValue line: %s", _zw.c_str()); - - if (pFile) { - fputs(_zw.c_str(), pFile); + std::string temp_string = NUMBERS[j]->name + "\t" + NUMBERS[j]->timeStamp + "\t" + round_output(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + "\n"; + ESP_LOGD(TAG, "Write PreValue line: %s", temp_string.c_str()); + + if (pFile) + { + fputs(temp_string.c_str(), pFile); } } @@ -311,678 +340,707 @@ void ClassFlowPostProcessing::SavePreValue() { fclose(pFile); } -ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit) { +ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector *lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit) +{ PreValueUse = false; PreValueAgeStartup = 30; ErrorMessage = false; ListFlowControll = NULL; - FilePreValue = FormatFileName("/sdcard/config/prevalue.ini"); + FilePreValue = format_filename("/sdcard/config/prevalue.ini"); ListFlowControll = lfc; flowTakeImage = NULL; UpdatePreValueINI = false; flowAnalog = _analog; flowDigit = _digit; - for (int i = 0; i < ListFlowControll->size(); ++i) { - if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0) { - flowTakeImage = (ClassFlowTakeImage*) (*ListFlowControll)[i]; + for (int i = 0; i < ListFlowControll->size(); ++i) + { + if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0) + { + flowTakeImage = (ClassFlowTakeImage *)(*ListFlowControll)[i]; } } } -void ClassFlowPostProcessing::handleDecimalExtendedResolution(string _decsep, string _value) { - string _digit, _decpos; +void ClassFlowPostProcessing::handleDecimalExtendedResolution(std::string _decsep, std::string _value) +{ + std::string _digit; int _pospunkt = _decsep.find_first_of("."); - // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); - if (_pospunkt > -1) { + if (_pospunkt > -1) + { _digit = _decsep.substr(0, _pospunkt); } - else { + else + { _digit = "default"; } - for (int j = 0; j < NUMBERS.size(); ++j) { - bool _zwdc = alphanumericToBoolean(_value); + for (int j = 0; j < NUMBERS.size(); ++j) + { + bool temp_value = alphanumeric_to_boolean(_value); // Set to default first (if nothing else is set) - if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { - NUMBERS[j]->isExtendedResolution = _zwdc; + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) + { + NUMBERS[j]->isExtendedResolution = temp_value; } } } -void ClassFlowPostProcessing::handleDecimalSeparator(string _decsep, string _value) { - string _digit, _decpos; +void ClassFlowPostProcessing::handleDecimalSeparator(std::string _decsep, std::string _value) +{ + std::string _digit; int _pospunkt = _decsep.find_first_of("."); - // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); - if (_pospunkt > -1) { + if (_pospunkt > -1) + { _digit = _decsep.substr(0, _pospunkt); } - else { + else + { _digit = "default"; } - for (int j = 0; j < NUMBERS.size(); ++j) { - int _zwdc = 0; - - if (isStringNumeric(_value)) { - _zwdc = std::stoi(_value); + for (int j = 0; j < NUMBERS.size(); ++j) + { + int temp_value = 0; + + if (is_string_numeric(_value)) + { + temp_value = std::stoi(_value); } // Set to default first (if nothing else is set) - if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { - NUMBERS[j]->DecimalShift = _zwdc; - NUMBERS[j]->DecimalShiftInitial = _zwdc; + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) + { + NUMBERS[j]->DecimalShift = temp_value; + NUMBERS[j]->DecimalShiftInitial = temp_value; } NUMBERS[j]->Nachkomma = NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift; } } -void ClassFlowPostProcessing::handleAnalogToDigitTransitionStart(string _decsep, string _value) { - string _digit, _decpos; - int _pospunkt = _decsep.find_first_of("."); - // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); - - if (_pospunkt > -1) { - _digit = _decsep.substr(0, _pospunkt); - } - else { - _digit = "default"; - } - - for (int j = 0; j < NUMBERS.size(); ++j) { - float _zwdc = 9.2; - - if (isStringNumeric(_value)) { - _zwdc = std::stof(_value); - } - - // Set to default first (if nothing else is set) - if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { - NUMBERS[j]->AnalogToDigitTransitionStart = _zwdc; - - } - } -} - -void ClassFlowPostProcessing::handleAllowNegativeRate(string _decsep, string _value) { - string _digit, _decpos; - int _pospunkt = _decsep.find_first_of("."); - // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); - - if (_pospunkt > -1) { - _digit = _decsep.substr(0, _pospunkt); - } - else { - _digit = "default"; - } - - for (int j = 0; j < NUMBERS.size(); ++j) { - bool _zwdc = alphanumericToBoolean(_value); - - // Set to default first (if nothing else is set) - if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { - NUMBERS[j]->AllowNegativeRates = _zwdc; - } - } -} - -void ClassFlowPostProcessing::handleIgnoreLeadingNaN(string _decsep, string _value) { - string _digit, _decpos; - int _pospunkt = _decsep.find_first_of("."); - - if (_pospunkt > -1) { - _digit = _decsep.substr(0, _pospunkt); - } - else { - _digit = "default"; - } - - for (int j = 0; j < NUMBERS.size(); ++j) { - bool _zwdc = alphanumericToBoolean(_value); - - // Set to default first (if nothing else is set) - if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { - NUMBERS[j]->IgnoreLeadingNaN = _zwdc; - } - } -} - -void ClassFlowPostProcessing::handleMaxRateType(string _decsep, string _value) { - string _digit, _decpos; - int _pospunkt = _decsep.find_first_of("."); - // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); - - if (_pospunkt > -1) { - _digit = _decsep.substr(0, _pospunkt); - } - else { - _digit = "default"; - } - - for (int j = 0; j < NUMBERS.size(); ++j) { - t_RateType _zwdc = AbsoluteChange; - - if (toUpper(_value) == "RATECHANGE") { - _zwdc = RateChange; - } - - // Set to default first (if nothing else is set) - if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { - NUMBERS[j]->MaxRateType = _zwdc; - } - } -} - -void ClassFlowPostProcessing::handleMaxRateValue(string _decsep, string _value) { - string _digit, _decpos; - int _pospunkt = _decsep.find_first_of("."); - // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); - - if (_pospunkt > -1) { - _digit = _decsep.substr(0, _pospunkt); - } - else { - _digit = "default"; - } - - for (int j = 0; j < NUMBERS.size(); ++j) { - float _zwdc = 1; - - if (isStringNumeric(_value)) { - _zwdc = std::stof(_value); - } - - // Set to default first (if nothing else is set) - if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { - NUMBERS[j]->useMaxRateValue = true; - NUMBERS[j]->MaxRateValue = _zwdc; - } - } -} - -void ClassFlowPostProcessing::handleChangeRateThreshold(string _decsep, string _value) { - string _digit, _decpos; - int _pospunkt = _decsep.find_first_of("."); - // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); - - if (_pospunkt > -1) { - _digit = _decsep.substr(0, _pospunkt); - } - else { - _digit = "default"; - } - - for (int j = 0; j < NUMBERS.size(); ++j) { - int _zwdc = 2; - - if (isStringNumeric(_value)) { - _zwdc = std::stof(_value); - } - - // Set to default first (if nothing else is set) - if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { - NUMBERS[j]->ChangeRateThreshold = _zwdc; - } - } -} - -void ClassFlowPostProcessing::handlecheckDigitIncreaseConsistency(std::string _decsep, std::string _value) +void ClassFlowPostProcessing::handleAnalogToDigitTransitionStart(std::string _decsep, std::string _value) { std::string _digit; int _pospunkt = _decsep.find_first_of("."); - // ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); - if (_pospunkt > -1) { + if (_pospunkt > -1) + { _digit = _decsep.substr(0, _pospunkt); } - else { + else + { _digit = "default"; } - for (int j = 0; j < NUMBERS.size(); ++j) { - bool _rt = alphanumericToBoolean(_value); + for (int j = 0; j < NUMBERS.size(); ++j) + { + float temp_value = 9.2; + + if (is_string_numeric(_value)) + { + temp_value = std::stof(_value); + } // Set to default first (if nothing else is set) - if ((_digit == "default") || (NUMBERS[j]->name == _digit)) { - NUMBERS[j]->checkDigitIncreaseConsistency = _rt; + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) + { + NUMBERS[j]->AnalogToDigitTransitionStart = temp_value; } } } -bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph) { - std::vector splitted; - int _n; +void ClassFlowPostProcessing::handleAllowNegativeRate(std::string _decsep, std::string _value) +{ + std::string _digit; + int _pospunkt = _decsep.find_first_of("."); - aktparamgraph = trim(aktparamgraph); + if (_pospunkt > -1) + { + _digit = _decsep.substr(0, _pospunkt); + } + else + { + _digit = "default"; + } - if (aktparamgraph.size() == 0) { - if (!this->GetNextParagraph(pfile, aktparamgraph)) { + for (int j = 0; j < NUMBERS.size(); ++j) + { + bool temp_value = alphanumeric_to_boolean(_value); + + // Set to default first (if nothing else is set) + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) + { + NUMBERS[j]->AllowNegativeRates = temp_value; + } + } +} + +void ClassFlowPostProcessing::handleIgnoreLeadingNaN(std::string _decsep, std::string _value) +{ + std::string _digit; + int _pospunkt = _decsep.find_first_of("."); + + if (_pospunkt > -1) + { + _digit = _decsep.substr(0, _pospunkt); + } + else + { + _digit = "default"; + } + + for (int j = 0; j < NUMBERS.size(); ++j) + { + bool temp_value = alphanumeric_to_boolean(_value); + + // Set to default first (if nothing else is set) + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) + { + NUMBERS[j]->IgnoreLeadingNaN = temp_value; + } + } +} + +void ClassFlowPostProcessing::handleMaxFlowRate(std::string _decsep, std::string _value) +{ + std::string _digit; + int _pospunkt = _decsep.find_first_of("."); + + if (_pospunkt > -1) + { + _digit = _decsep.substr(0, _pospunkt); + } + else + { + _digit = "default"; + } + + for (int j = 0; j < NUMBERS.size(); ++j) + { + float temp_value = 4.0; + + if (is_string_numeric(_value)) + { + temp_value = std::stof(_value); + } + + // Set to default first (if nothing else is set) + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) + { + NUMBERS[j]->useMaxFlowRate = true; + NUMBERS[j]->MaxFlowRate = temp_value; + } + } +} + +void ClassFlowPostProcessing::handleMaxRateType(std::string _decsep, std::string _value) +{ + std::string _digit, _decpos; + int _pospunkt = _decsep.find_first_of("."); + + if (_pospunkt > -1) + { + _digit = _decsep.substr(0, _pospunkt); + } + else + { + _digit = "default"; + } + + for (int j = 0; j < NUMBERS.size(); ++j) + { + t_RateType temp_value = AbsoluteChange; + + if (to_upper(_value) == "RATECHANGE") + { + temp_value = RateChange; + } + + // Set to default first (if nothing else is set) + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) + { + NUMBERS[j]->MaxRateType = temp_value; + } + } +} + +void ClassFlowPostProcessing::handleMaxRateValue(std::string _decsep, std::string _value) +{ + std::string _digit; + int _pospunkt = _decsep.find_first_of("."); + + if (_pospunkt > -1) + { + _digit = _decsep.substr(0, _pospunkt); + } + else + { + _digit = "default"; + } + + for (int j = 0; j < NUMBERS.size(); ++j) + { + float temp_value = 1; + + if (is_string_numeric(_value)) + { + temp_value = std::stof(_value); + } + + // Set to default first (if nothing else is set) + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) + { + NUMBERS[j]->useMaxRateValue = true; + NUMBERS[j]->MaxRateValue = temp_value; + } + } +} + +void ClassFlowPostProcessing::handleChangeRateThreshold(std::string _decsep, std::string _value) +{ + std::string _digit; + int _pospunkt = _decsep.find_first_of("."); + + if (_pospunkt > -1) + { + _digit = _decsep.substr(0, _pospunkt); + } + else + { + _digit = "default"; + } + + for (int j = 0; j < NUMBERS.size(); ++j) + { + int temp_value = 2; + + if (is_string_numeric(_value)) + { + temp_value = std::stof(_value); + } + + // Set to default first (if nothing else is set) + if ((_digit == "default") || (NUMBERS[j]->name == _digit)) + { + NUMBERS[j]->ChangeRateThreshold = temp_value; + } + } +} + +bool ClassFlowPostProcessing::ReadParameter(FILE *pFile, std::string &aktparamgraph) +{ + aktparamgraph = trim_string_left_right(aktparamgraph); + if (aktparamgraph.size() == 0) + { + if (!GetNextParagraph(pFile, aktparamgraph)) + { return false; } } // Paragraph does not fit PostProcessing - if (aktparamgraph.compare("[PostProcessing]") != 0) { + if ((to_upper(aktparamgraph).compare("[POSTPROCESSING]") != 0) && (to_upper(aktparamgraph).compare(";[POSTPROCESSING]") != 0)) + { return false; } InitNUMBERS(); + std::vector splitted; - while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) { - splitted = ZerlegeZeile(aktparamgraph); - std::string _param = GetParameterName(splitted[0]); + while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph)) + { + splitted = split_line(aktparamgraph); - if ((toUpper(_param) == "EXTENDEDRESOLUTION") && (splitted.size() > 1)) { - handleDecimalExtendedResolution(splitted[0], splitted[1]); - } + if (splitted.size() > 1) + { + std::string _param = to_upper(GetParameterName(splitted[0])); - if ((toUpper(_param) == "DECIMALSHIFT") && (splitted.size() > 1)) { - handleDecimalSeparator(splitted[0], splitted[1]); - } - - if ((toUpper(_param) == "ANALOGTODIGITTRANSITIONSTART") && (splitted.size() > 1)) { - handleAnalogToDigitTransitionStart(splitted[0], splitted[1]); - } - - if ((toUpper(_param) == "MAXRATEVALUE") && (splitted.size() > 1)) { - handleMaxRateValue(splitted[0], splitted[1]); - } - - if ((toUpper(_param) == "MAXRATETYPE") && (splitted.size() > 1)) { - handleMaxRateType(splitted[0], splitted[1]); - } - - if ((toUpper(_param) == "PREVALUEUSE") && (splitted.size() > 1)) { - PreValueUse = alphanumericToBoolean(splitted[1]); - } - - if ((toUpper(_param) == "CHANGERATETHRESHOLD") && (splitted.size() > 1)) { - handleChangeRateThreshold(splitted[0], splitted[1]); - } - - if ((toUpper(_param) == "CHECKDIGITINCREASECONSISTENCY") && (splitted.size() > 1)) { - handlecheckDigitIncreaseConsistency(splitted[0], splitted[1]); - } - - if ((toUpper(_param) == "ALLOWNEGATIVERATES") && (splitted.size() > 1)) { - handleAllowNegativeRate(splitted[0], splitted[1]); - } - - if ((toUpper(_param) == "ERRORMESSAGE") && (splitted.size() > 1)) { - ErrorMessage = alphanumericToBoolean(splitted[1]); - } - - if ((toUpper(_param) == "IGNORELEADINGNAN") && (splitted.size() > 1)) { - handleIgnoreLeadingNaN(splitted[0], splitted[1]); - } - - if ((toUpper(_param) == "PREVALUEAGESTARTUP") && (splitted.size() > 1)) { - if (isStringNumeric(splitted[1])) { - PreValueAgeStartup = std::stoi(splitted[1]); + if (_param == "PREVALUEUSE") + { + PreValueUse = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "PREVALUEAGESTARTUP") + { + if (is_string_numeric(splitted[1])) + { + PreValueAgeStartup = std::stoi(splitted[1]); + } + } + else if (_param == "ERRORMESSAGE") + { + ErrorMessage = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "ALLOWNEGATIVERATES") + { + handleAllowNegativeRate(splitted[0], splitted[1]); + } + else if (_param == "DECIMALSHIFT") + { + handleDecimalSeparator(splitted[0], splitted[1]); + } + else if (_param == "ANALOGTODIGITTRANSITIONSTART") + { + handleAnalogToDigitTransitionStart(splitted[0], splitted[1]); + } + else if (_param == "MAXFLOWRATE") + { + handleMaxFlowRate(splitted[0], splitted[1]); + } + else if (_param == "MAXRATEVALUE") + { + handleMaxRateValue(splitted[0], splitted[1]); + } + else if (_param == "MAXRATETYPE") + { + handleMaxRateType(splitted[0], splitted[1]); + } + else if (_param == "CHANGERATETHRESHOLD") + { + handleChangeRateThreshold(splitted[0], splitted[1]); + } + else if (_param == "EXTENDEDRESOLUTION") + { + handleDecimalExtendedResolution(splitted[0], splitted[1]); + } + else if (_param == "IGNORELEADINGNAN") + { + handleIgnoreLeadingNaN(splitted[0], splitted[1]); } } } - if (PreValueUse) { + if (PreValueUse) + { return LoadPreValue(); } return true; } -void ClassFlowPostProcessing::InitNUMBERS() { - int anzDIGIT = 0; - int anzANALOG = 0; +void ClassFlowPostProcessing::InitNUMBERS() +{ std::vector name_numbers; - if (flowDigit) { - anzDIGIT = flowDigit->getNumberGENERAL(); + if (flowDigit) + { + int anzDIGIT = flowDigit->getNumberGENERAL(); flowDigit->UpdateNameNumbers(&name_numbers); + ESP_LOGD(TAG, "Anzahl NUMBERS: %d - DIGITS: %d", name_numbers.size(), anzDIGIT); } - if (flowAnalog) { - anzANALOG = flowAnalog->getNumberGENERAL(); + if (flowAnalog) + { + int anzANALOG = flowAnalog->getNumberGENERAL(); flowAnalog->UpdateNameNumbers(&name_numbers); + ESP_LOGD(TAG, "Anzahl NUMBERS: %d - ANALOG: %d", name_numbers.size(), anzANALOG); } - ESP_LOGD(TAG, "Anzahl NUMBERS: %d - DIGITS: %d, ANALOG: %d", name_numbers.size(), anzDIGIT, anzANALOG); - - for (int _num = 0; _num < name_numbers.size(); ++_num) { + for (int _num = 0; _num < name_numbers.size(); ++_num) + { NumberPost *_number = new NumberPost; - _number->name = name_numbers[_num]; - + _number->digit_roi = NULL; - - if (flowDigit) { + + if (flowDigit) + { _number->digit_roi = flowDigit->FindGENERAL(name_numbers[_num]); } - - if (_number->digit_roi) { + + if (_number->digit_roi) + { _number->AnzahlDigit = _number->digit_roi->ROI.size(); } - else { + else + { _number->AnzahlDigit = 0; } _number->analog_roi = NULL; - - if (flowAnalog) { + + if (flowAnalog) + { _number->analog_roi = flowAnalog->FindGENERAL(name_numbers[_num]); } - if (_number->analog_roi) { + if (_number->analog_roi) + { _number->AnzahlAnalog = _number->analog_roi->ROI.size(); } - else { + else + { _number->AnzahlAnalog = 0; } - _number->FlowRateAct = 0; // m3 / min + _number->PreValue = 0; // last value read out well + _number->ReturnPreValue = ""; _number->PreValueOkay = false; + _number->ErrorMessageText = ""; // Error message for consistency check _number->AllowNegativeRates = false; - _number->IgnoreLeadingNaN = false; + _number->DecimalShift = 0; + _number->DecimalShiftInitial = 0; + _number->AnalogToDigitTransitionStart = 9.2; + _number->MaxFlowRate = 4.0; + _number->useMaxFlowRate = false; _number->MaxRateValue = 0.1; _number->MaxRateType = AbsoluteChange; _number->useMaxRateValue = false; - _number->checkDigitIncreaseConsistency = false; - _number->DecimalShift = 0; - _number->DecimalShiftInitial = 0; - _number->isExtendedResolution = false; - _number->AnalogToDigitTransitionStart=9.2; _number->ChangeRateThreshold = 2; + _number->isExtendedResolution = false; + _number->IgnoreLeadingNaN = false; - _number->Value = 0; // last value read out, incl. corrections - _number->ReturnValue = ""; // corrected return value, possibly with error message - _number->ReturnRawValue = ""; // raw value (with N & leading 0) - _number->PreValue = 0; // last value read out well - _number->ReturnPreValue = ""; - _number->ErrorMessageText = ""; // Error message for consistency check + _number->Value = 0; // last value read out, incl. corrections + _number->ReturnValue = ""; // corrected return value, possibly with error message + _number->ReturnRawValue = ""; // raw value (with N & leading 0) + _number->FlowRateAct = 0; // m3 / min _number->Nachkomma = _number->AnzahlAnalog; NUMBERS.push_back(_number); } - for (int i = 0; i < NUMBERS.size(); ++i) { + for (int i = 0; i < NUMBERS.size(); ++i) + { ESP_LOGD(TAG, "Number %s, Anz DIG: %d, Anz ANA %d", NUMBERS[i]->name.c_str(), NUMBERS[i]->AnzahlDigit, NUMBERS[i]->AnzahlAnalog); } } -string ClassFlowPostProcessing::ShiftDecimal(string in, int _decShift) { - if (_decShift == 0) { +std::string ClassFlowPostProcessing::ShiftDecimal(std::string in, int _decShift) +{ + if (_decShift == 0) + { return in; } - int _pos_dec_org, _pos_dec_neu; - - _pos_dec_org = findDelimiterPos(in, "."); - - if (_pos_dec_org == std::string::npos) { + int _pos_dec_org = find_delimiter_pos(in, "."); + if (_pos_dec_org == std::string::npos) + { _pos_dec_org = in.length(); } - else { + else + { in = in.erase(_pos_dec_org, 1); } - - _pos_dec_neu = _pos_dec_org + _decShift; + int _pos_dec_neu = _pos_dec_org + _decShift; // comma is before the first digit - if (_pos_dec_neu <= 0) { - for (int i = 0; i > _pos_dec_neu; --i) { + if (_pos_dec_neu <= 0) + { + for (int i = 0; i > _pos_dec_neu; --i) + { in = in.insert(0, "0"); } - + in = "0." + in; return in; } // Comma should be after string (123 --> 1230) - if (_pos_dec_neu > in.length()) { - for (int i = in.length(); i < _pos_dec_neu; ++i) { + if (_pos_dec_neu > in.length()) + { + for (int i = in.length(); i < _pos_dec_neu; ++i) + { in = in.insert(in.length(), "0"); - } - return in; + } + return in; } - string zw; - zw = in.substr(0, _pos_dec_neu); - zw = zw + "."; - zw = zw + in.substr(_pos_dec_neu, in.length() - _pos_dec_neu); + std::string temp_string = in.substr(0, _pos_dec_neu); + temp_string = temp_string + "."; + temp_string = temp_string + in.substr(_pos_dec_neu, in.length() - _pos_dec_neu); - return zw; + return temp_string; } -bool ClassFlowPostProcessing::doFlow(string zwtime) { - string zwvalue; +bool ClassFlowPostProcessing::doFlow(std::string temp_time) +{ time_t imagetime = flowTakeImage->getTimeImageTaken(); - - if (imagetime == 0) { + if (imagetime == 0) + { time(&imagetime); } - struct tm* timeinfo; - timeinfo = localtime(&imagetime); + struct tm *timeinfo = localtime(&imagetime); char strftime_buf[64]; strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%dT%H:%M:%S", timeinfo); - zwtime = std::string(strftime_buf); + temp_time = std::string(strftime_buf); - ESP_LOGD(TAG, "Quantity NUMBERS: %d", NUMBERS.size()); - - for (int j = 0; j < NUMBERS.size(); ++j) { + for (int j = 0; j < NUMBERS.size(); ++j) + { NUMBERS[j]->ReturnRawValue = ""; NUMBERS[j]->ReturnRateValue = ""; NUMBERS[j]->ReturnValue = ""; - NUMBERS[j]->ReturnChangeAbsolute = RundeOutput(0.0, NUMBERS[j]->Nachkomma); // always reset change absolute + NUMBERS[j]->ReturnChangeAbsolute = round_output(0.0, NUMBERS[j]->Nachkomma); // always reset change absolute NUMBERS[j]->ErrorMessageText = ""; NUMBERS[j]->Value = -1; // calculate time difference - // double LastValueTimeDifference = difftime(imagetime, NUMBERS[j]->timeStampLastValue); // in seconds - double LastPreValueTimeDifference = difftime(imagetime, NUMBERS[j]->timeStampLastPreValue); // in seconds + double LastValueTimeDifference = difftime(imagetime, NUMBERS[j]->timeStampLastValue) / 60; // in minutes + double LastPreValueTimeDifference = difftime(imagetime, NUMBERS[j]->timeStampLastPreValue) / 60; // in minutes + + if (!flowctrl.AlignmentOk) + { + NUMBERS[j]->ErrorMessageText = "alignment failed"; + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, NUMBERS[j]->ErrorMessageText); + WriteDataLog(j); + continue; + } - // Update decimal point, as the decimal places can also change when changing from CNNType Auto --> xyz: UpdateNachkommaDecimalShift(); int previous_value = -1; - - if (NUMBERS[j]->analog_roi) { + if (NUMBERS[j]->analog_roi) + { NUMBERS[j]->ReturnRawValue = flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution); - - if (NUMBERS[j]->ReturnRawValue.length() > 0) { - char zw = NUMBERS[j]->ReturnRawValue[0]; - if (zw >= 48 && zw <=57) { - previous_value = zw - 48; + if (NUMBERS[j]->ReturnRawValue.length() > 0) + { + char temp_char = NUMBERS[j]->ReturnRawValue[0]; + if (temp_char >= 48 && temp_char <= 57) + { + previous_value = temp_char - 48; } } } - - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "After analog->getReadout: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str()); - #endif - if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) { + if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) + { NUMBERS[j]->ReturnRawValue = "." + NUMBERS[j]->ReturnRawValue; } - if (NUMBERS[j]->digit_roi) { - if (NUMBERS[j]->analog_roi) { + if (NUMBERS[j]->digit_roi) + { + if (NUMBERS[j]->analog_roi) + { NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, false, previous_value, NUMBERS[j]->analog_roi->ROI[0]->result_float, NUMBERS[j]->AnalogToDigitTransitionStart) + NUMBERS[j]->ReturnRawValue; } - else { - NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution, previous_value); // Extended Resolution only if there are no analogue digits + else + { + NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution, previous_value); // Extended Resolution only if there are no analogue digits } } - - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "After digit->getReadout: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str()); - #endif - + NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift); - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "After ShiftDecimal: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str()); - #endif - - if (NUMBERS[j]->IgnoreLeadingNaN) { - while ((NUMBERS[j]->ReturnRawValue.length() > 1) && (NUMBERS[j]->ReturnRawValue[0] == 'N')) { + if (NUMBERS[j]->IgnoreLeadingNaN) + { + while ((NUMBERS[j]->ReturnRawValue.length() > 1) && (NUMBERS[j]->ReturnRawValue[0] == 'N')) + { NUMBERS[j]->ReturnRawValue.erase(0, 1); } } - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "After IgnoreLeadingNaN: ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str()); - #endif - NUMBERS[j]->ReturnValue = NUMBERS[j]->ReturnRawValue; - if (findDelimiterPos(NUMBERS[j]->ReturnValue, "N") != std::string::npos) { - if (PreValueUse && NUMBERS[j]->PreValueOkay) { - NUMBERS[j]->ReturnValue = ErsetzteN(NUMBERS[j]->ReturnValue, NUMBERS[j]->PreValue); + if (find_delimiter_pos(NUMBERS[j]->ReturnValue, "N") != std::string::npos) + { + if (PreValueUse && NUMBERS[j]->PreValueOkay) + { + NUMBERS[j]->ReturnValue = ErsetzteN(NUMBERS[j]->ReturnValue, NUMBERS[j]->PreValue); } - else { - string _zw = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText; - LogFile.WriteToFile(ESP_LOG_INFO, TAG, _zw); + else + { + std::string temp_string = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, temp_string); NUMBERS[j]->ReturnValue = ""; NUMBERS[j]->timeStampLastValue = imagetime; WriteDataLog(j); continue; // there is no number because there is still an N. } } - - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "After findDelimiterPos: ReturnValue %s", NUMBERS[j]->ReturnRawValue.c_str()); - #endif - + // Delete leading zeros (unless there is only one 0 left) - while ((NUMBERS[j]->ReturnValue.length() > 1) && (NUMBERS[j]->ReturnValue[0] == '0')) { + while ((NUMBERS[j]->ReturnValue.length() > 1) && (NUMBERS[j]->ReturnValue[0] == '0')) + { NUMBERS[j]->ReturnValue.erase(0, 1); } - - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "After removeLeadingZeros: ReturnValue %s", NUMBERS[j]->ReturnRawValue.c_str()); - #endif - + NUMBERS[j]->Value = std::stod(NUMBERS[j]->ReturnValue); - - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "After setting the Value: Value %f and as double is %f", NUMBERS[j]->Value, std::stod(NUMBERS[j]->ReturnValue)); - #endif - if (NUMBERS[j]->checkDigitIncreaseConsistency) { - if (flowDigit) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Before checkDigitConsistency: value=" + std::to_string(NUMBERS[j]->Value)); - NUMBERS[j]->Value = checkDigitConsistency(NUMBERS[j]->Value, NUMBERS[j]->DecimalShift, NUMBERS[j]->analog_roi != NULL, NUMBERS[j]->PreValue); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "After checkDigitConsistency: value=" + std::to_string(NUMBERS[j]->Value)); - } - else { - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "checkDigitIncreaseConsistency = true - no digit numbers defined!"); - #endif - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "checkDigitIncreaseConsistency = true - no digit numbers defined!"); - } - } - - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "After checkDigitIncreaseConsistency: Value %f", NUMBERS[j]->Value); - #endif - - if (PreValueUse && NUMBERS[j]->PreValueOkay) { - if ((NUMBERS[j]->Nachkomma > 0) && (NUMBERS[j]->ChangeRateThreshold > 0)) { + if (PreValueUse && NUMBERS[j]->PreValueOkay) + { + if ((NUMBERS[j]->Nachkomma > 0) && (NUMBERS[j]->ChangeRateThreshold > 0)) + { double _difference1 = (NUMBERS[j]->PreValue - (NUMBERS[j]->ChangeRateThreshold / pow(10, NUMBERS[j]->Nachkomma))); double _difference2 = (NUMBERS[j]->PreValue + (NUMBERS[j]->ChangeRateThreshold / pow(10, NUMBERS[j]->Nachkomma))); - if ((NUMBERS[j]->Value >= _difference1) && (NUMBERS[j]->Value <= _difference2)) { + if ((NUMBERS[j]->Value >= _difference1) && (NUMBERS[j]->Value <= _difference2)) + { NUMBERS[j]->Value = NUMBERS[j]->PreValue; NUMBERS[j]->ReturnValue = std::to_string(NUMBERS[j]->PreValue); } } - if ((!NUMBERS[j]->AllowNegativeRates) && (NUMBERS[j]->Value < NUMBERS[j]->PreValue)) { + if ((!NUMBERS[j]->AllowNegativeRates) && (NUMBERS[j]->Value < NUMBERS[j]->PreValue)) + { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handleAllowNegativeRate for device: " + NUMBERS[j]->name); - - if ((NUMBERS[j]->Value < NUMBERS[j]->PreValue)) { - // more debug if extended resolution is on, see #2447 - if (NUMBERS[j]->isExtendedResolution) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Neg: value=" + std::to_string(NUMBERS[j]->Value) - + ", preValue=" + std::to_string(NUMBERS[j]->PreValue) - + ", preToll=" + std::to_string(NUMBERS[j]->PreValue-(2/pow(10, NUMBERS[j]->Nachkomma)))); - } - NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Neg. Rate - Read: " + zwvalue + " - Raw: " + NUMBERS[j]->ReturnRawValue + " - Pre: " + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + " "; + if ((NUMBERS[j]->Value < NUMBERS[j]->PreValue)) + { + NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Neg. Rate - Read: " + round_output(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma) + " - Raw: " + NUMBERS[j]->ReturnRawValue + " - Pre: " + round_output(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + " "; NUMBERS[j]->Value = NUMBERS[j]->PreValue; NUMBERS[j]->ReturnValue = ""; NUMBERS[j]->timeStampLastValue = imagetime; - string _zw = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText; - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, _zw); + std::string temp_string = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText; + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, temp_string); WriteDataLog(j); continue; } } - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "After AllowNegativeRates: Value %f", NUMBERS[j]->Value); - #endif - - // LastValueTimeDifference = LastValueTimeDifference / 60; // in minutes - LastPreValueTimeDifference = LastPreValueTimeDifference / 60; // in minutes NUMBERS[j]->FlowRateAct = (NUMBERS[j]->Value - NUMBERS[j]->PreValue) / LastPreValueTimeDifference; - NUMBERS[j]->ReturnRateValue = to_string(NUMBERS[j]->FlowRateAct); + NUMBERS[j]->ReturnRateValue = std::to_string(NUMBERS[j]->FlowRateAct); - if ((NUMBERS[j]->useMaxRateValue) && (NUMBERS[j]->Value != NUMBERS[j]->PreValue)) { + if ((NUMBERS[j]->useMaxRateValue) && (NUMBERS[j]->Value != NUMBERS[j]->PreValue)) + { double _ratedifference; - - if (NUMBERS[j]->MaxRateType == RateChange) { + + if (NUMBERS[j]->MaxRateType == RateChange) + { _ratedifference = NUMBERS[j]->FlowRateAct; } - else { - // TODO: - // Since I don't know if this is desired, I'll comment it out first. - // int roundDifference = (int)(round(LastPreValueTimeDifference / LastValueTimeDifference)); // calculate how many rounds have passed since NUMBERS[j]->timeLastPreValue was set - // _ratedifference = ((NUMBERS[j]->Value - NUMBERS[j]->PreValue) / ((int)(round(LastPreValueTimeDifference / LastValueTimeDifference)))); // Difference per round, as a safeguard in case a reading error(Neg. Rate - Read: or Rate too high - Read:) occurs in the meantime - _ratedifference = (NUMBERS[j]->Value - NUMBERS[j]->PreValue); + else + { + // Difference per round, as a safeguard in case a reading error(Neg. Rate - Read: or Rate too high - Read:) occurs in the meantime + _ratedifference = ((NUMBERS[j]->Value - NUMBERS[j]->PreValue) / ((int)(round(LastPreValueTimeDifference / LastValueTimeDifference)))); } - if (abs(_ratedifference) > abs(NUMBERS[j]->MaxRateValue)) { - NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Rate too high - Read: " + RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma) + " - Pre: " + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + " - Rate: " + RundeOutput(_ratedifference, NUMBERS[j]->Nachkomma); + if (abs(_ratedifference) > abs(NUMBERS[j]->MaxRateValue)) + { + NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Rate too high - Read: " + round_output(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma) + " - Pre: " + round_output(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + " - Rate: " + round_output(_ratedifference, NUMBERS[j]->Nachkomma); NUMBERS[j]->Value = NUMBERS[j]->PreValue; NUMBERS[j]->ReturnValue = ""; NUMBERS[j]->ReturnRateValue = ""; NUMBERS[j]->timeStampLastValue = imagetime; - string _zw = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText; - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, _zw); + std::string temp_string = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText; + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, temp_string); WriteDataLog(j); continue; } } - - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "After MaxRateCheck: Value %f", NUMBERS[j]->Value); - #endif } - - NUMBERS[j]->ReturnChangeAbsolute = RundeOutput(NUMBERS[j]->Value - NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma); + + NUMBERS[j]->ReturnChangeAbsolute = round_output(NUMBERS[j]->Value - NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma); NUMBERS[j]->PreValue = NUMBERS[j]->Value; NUMBERS[j]->PreValueOkay = true; - NUMBERS[j]->timeStampLastValue = imagetime; + NUMBERS[j]->timeStampLastValue = imagetime; NUMBERS[j]->timeStampLastPreValue = imagetime; - NUMBERS[j]->ReturnValue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma); - NUMBERS[j]->ReturnPreValue = RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma); + NUMBERS[j]->ReturnValue = round_output(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma); + NUMBERS[j]->ReturnPreValue = round_output(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma); NUMBERS[j]->ErrorMessageText = "no error"; UpdatePreValueINI = true; - string _zw = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText; - LogFile.WriteToFile(ESP_LOG_INFO, TAG, _zw); + std::string temp_string = NUMBERS[j]->name + ": Raw: " + NUMBERS[j]->ReturnRawValue + ", Value: " + NUMBERS[j]->ReturnValue + ", Status: " + NUMBERS[j]->ErrorMessageText; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, temp_string); WriteDataLog(j); } @@ -990,187 +1048,169 @@ bool ClassFlowPostProcessing::doFlow(string zwtime) { return true; } -void ClassFlowPostProcessing::WriteDataLog(int _index) { - if (!LogFile.GetDataLogToSD()) { +std::vector ClassFlowPostProcessing::addNumbersTogether(std::vector DigitValues, std::vector AnalogValues) +{ + std::vector meterValues; + + for (int i = 0; i < DigitValues.size(); ++i) + { + meterValues.push_back(DigitValues[i]); + } + + for (int i = 0; i < AnalogValues.size(); ++i) + { + meterValues.push_back(AnalogValues[i]); + } + + return meterValues; +} + +void ClassFlowPostProcessing::WriteDataLog(int _index) +{ + if (!LogFile.GetDataLogToSD()) + { return; } - - string analog = ""; - string digit = ""; - string timezw = ""; + + std::string analog = ""; + std::string digit = ""; + std::string temp_time = ""; char buffer[80]; - struct tm* timeinfo = localtime(&NUMBERS[_index]->timeStampLastValue); + struct tm *timeinfo = localtime(&NUMBERS[_index]->timeStampLastValue); strftime(buffer, 80, PREVALUE_TIME_FORMAT_OUTPUT, timeinfo); - timezw = std::string(buffer); - - if (flowAnalog) { + temp_time = std::string(buffer); + + if (flowAnalog) + { analog = flowAnalog->getReadoutRawString(_index); } - if (flowDigit) { + if (flowDigit) + { digit = flowDigit->getReadoutRawString(_index); } - - LogFile.WriteToData(timezw, NUMBERS[_index]->name, NUMBERS[_index]->ReturnRawValue, NUMBERS[_index]->ReturnValue, NUMBERS[_index]->ReturnPreValue, - NUMBERS[_index]->ReturnRateValue, NUMBERS[_index]->ReturnChangeAbsolute, NUMBERS[_index]->ErrorMessageText, digit, analog); + + LogFile.WriteToData(temp_time, NUMBERS[_index]->name, NUMBERS[_index]->ReturnRawValue, NUMBERS[_index]->ReturnValue, NUMBERS[_index]->ReturnPreValue, + NUMBERS[_index]->ReturnRateValue, NUMBERS[_index]->ReturnChangeAbsolute, NUMBERS[_index]->ErrorMessageText, digit, analog); ESP_LOGD(TAG, "WriteDataLog: %s, %s, %s, %s, %s", NUMBERS[_index]->ReturnRawValue.c_str(), NUMBERS[_index]->ReturnValue.c_str(), NUMBERS[_index]->ErrorMessageText.c_str(), digit.c_str(), analog.c_str()); } -void ClassFlowPostProcessing::UpdateNachkommaDecimalShift() { - for (int j = 0; j < NUMBERS.size(); ++j) { +void ClassFlowPostProcessing::UpdateNachkommaDecimalShift() +{ + for (int j = 0; j < NUMBERS.size(); ++j) + { // There are only digits - if (NUMBERS[j]->digit_roi && !NUMBERS[j]->analog_roi) { + if (NUMBERS[j]->digit_roi && !NUMBERS[j]->analog_roi) + { // ESP_LOGD(TAG, "Nurdigit"); NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShiftInitial; // Extended resolution is on and should also be used for this digit. - if (NUMBERS[j]->isExtendedResolution && flowDigit->isExtendedResolution()) { - NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShift-1; + if (NUMBERS[j]->isExtendedResolution && flowDigit->isExtendedResolution()) + { + NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShift - 1; } NUMBERS[j]->Nachkomma = -NUMBERS[j]->DecimalShift; } - if (!NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) { + if (!NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) + { // ESP_LOGD(TAG, "Nur analog"); NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShiftInitial; - - if (NUMBERS[j]->isExtendedResolution && flowAnalog->isExtendedResolution()) { - NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShift-1; + + if (NUMBERS[j]->isExtendedResolution && flowAnalog->isExtendedResolution()) + { + NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShift - 1; } NUMBERS[j]->Nachkomma = -NUMBERS[j]->DecimalShift; } // digit + analog - if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) { + if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) + { // ESP_LOGD(TAG, "Nur digit + analog"); NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShiftInitial; NUMBERS[j]->Nachkomma = NUMBERS[j]->analog_roi->ROI.size() - NUMBERS[j]->DecimalShift; // Extended resolution is on and should also be used for this digit. - if (NUMBERS[j]->isExtendedResolution && flowAnalog->isExtendedResolution()) { - NUMBERS[j]->Nachkomma = NUMBERS[j]->Nachkomma+1; + if (NUMBERS[j]->isExtendedResolution && flowAnalog->isExtendedResolution()) + { + NUMBERS[j]->Nachkomma = NUMBERS[j]->Nachkomma + 1; } } - ESP_LOGD(TAG, "UpdateNachkommaDecShift NUMBER%i: Nachkomma %i, DecShift %i", j, NUMBERS[j]->Nachkomma,NUMBERS[j]->DecimalShift); + ESP_LOGD(TAG, "UpdateNachkommaDecShift NUMBER%i: Nachkomma %i, DecShift %i", j, NUMBERS[j]->Nachkomma, NUMBERS[j]->DecimalShift); } } -string ClassFlowPostProcessing::getReadout(int _number) { +std::string ClassFlowPostProcessing::getReadout(int _number) +{ return NUMBERS[_number]->ReturnValue; } -string ClassFlowPostProcessing::getReadoutParam(bool _rawValue, bool _noerror, int _number) { - if (_rawValue) { +std::string ClassFlowPostProcessing::getReadoutParam(bool _rawValue, bool _noerror, int _number) +{ + if (_rawValue) + { return NUMBERS[_number]->ReturnRawValue; } - if (_noerror) { + if (_noerror) + { return NUMBERS[_number]->ReturnValue; } - + return NUMBERS[_number]->ReturnValue; } -string ClassFlowPostProcessing::ErsetzteN(string input, double _prevalue) { - int posN, posPunkt; +std::string ClassFlowPostProcessing::ErsetzteN(std::string input, double _prevalue) +{ int pot, ziffer; - float zw; - posN = findDelimiterPos(input, "N"); - posPunkt = findDelimiterPos(input, "."); - - if (posPunkt == std::string::npos) { + int posN = find_delimiter_pos(input, "N"); + int posPunkt = find_delimiter_pos(input, "."); + + if (posPunkt == std::string::npos) + { posPunkt = input.length(); } - while (posN != std::string::npos) { - if (posN < posPunkt) { + while (posN != std::string::npos) + { + if (posN < posPunkt) + { pot = posPunkt - posN - 1; } - else { + else + { pot = posPunkt - posN; } - zw =_prevalue / pow(10, pot); - ziffer = ((int) zw) % 10; + float temp_value = _prevalue / pow(10, pot); + ziffer = ((int)temp_value) % 10; input[posN] = ziffer + 48; - posN = findDelimiterPos(input, "N"); + posN = find_delimiter_pos(input, "N"); } return input; } -float ClassFlowPostProcessing::checkDigitConsistency(double input, int _decilamshift, bool _isanalog, double _preValue) { - int aktdigit, olddigit; - int aktdigit_before, olddigit_before; - int pot, pot_max; - float zw; - bool no_nulldurchgang = false; - - pot = _decilamshift; - - // if there are no analogue values, the last one cannot be evaluated - if (!_isanalog) { - pot++; - } - - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "checkDigitConsistency: pot=%d, decimalshift=%d", pot, _decilamshift); - #endif - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "checkDigitConsistency: pot=" + std::to_string(pot) + ", decimalshift=" + std::to_string(_decilamshift)); - - pot_max = ((int) log10(input)) + 1; - - while (pot <= pot_max) { - zw = input / pow(10, pot-1); - aktdigit_before = ((int) zw) % 10; - zw = _preValue / pow(10, pot-1); - olddigit_before = ((int) zw) % 10; - - zw = input / pow(10, pot); - aktdigit = ((int) zw) % 10; - zw = _preValue / pow(10, pot); - olddigit = ((int) zw) % 10; - - no_nulldurchgang = (olddigit_before <= aktdigit_before); - - if (no_nulldurchgang) { - if (aktdigit != olddigit) { - input = input + ((float) (olddigit - aktdigit)) * pow(10, pot); // New Digit is replaced by old Digit; - } - } - else { - // despite zero crossing, digit was not incremented --> add 1 - if (aktdigit == olddigit) { - input = input + ((float) (1)) * pow(10, pot); // add 1 at the point - } - } - - #ifdef SERIAL_DEBUG - ESP_LOGD(TAG, "checkDigitConsistency: input=%f", input); - #endif - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "checkDigitConsistency: input=" + std::to_string(input)); - - pot++; - } - - return input; -} - -string ClassFlowPostProcessing::getReadoutRate(int _number) { +std::string ClassFlowPostProcessing::getReadoutRate(int _number) +{ return std::to_string(NUMBERS[_number]->FlowRateAct); } -string ClassFlowPostProcessing::getReadoutTimeStamp(int _number) { - return NUMBERS[_number]->timeStamp; +std::string ClassFlowPostProcessing::getReadoutTimeStamp(int _number) +{ + return NUMBERS[_number]->timeStamp; } -string ClassFlowPostProcessing::getReadoutError(int _number) { +std::string ClassFlowPostProcessing::getReadoutError(int _number) +{ return NUMBERS[_number]->ErrorMessageText; } diff --git a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h index 24d58693..73dc8b2b 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h +++ b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h @@ -3,75 +3,75 @@ #ifndef CLASSFFLOWPOSTPROCESSING_H #define CLASSFFLOWPOSTPROCESSING_H +#include + #include "ClassFlow.h" #include "ClassFlowTakeImage.h" #include "ClassFlowCNNGeneral.h" #include "ClassFlowDefineTypes.h" -#include - -class ClassFlowPostProcessing : - public ClassFlow +class ClassFlowPostProcessing : public ClassFlow { protected: bool UpdatePreValueINI; - int PreValueAgeStartup; + int PreValueAgeStartup; bool ErrorMessage; - - ClassFlowCNNGeneral* flowAnalog; - ClassFlowCNNGeneral* flowDigit; - string FilePreValue; + ClassFlowCNNGeneral *flowAnalog; + ClassFlowCNNGeneral *flowDigit; + + std::string FilePreValue; ClassFlowTakeImage *flowTakeImage; bool LoadPreValue(void); - string ShiftDecimal(string in, int _decShift); + std::string ShiftDecimal(std::string in, int _decShift); - string ErsetzteN(string, double _prevalue); - float checkDigitConsistency(double input, int _decilamshift, bool _isanalog, double _preValue); + std::string ErsetzteN(std::string, double _prevalue); void InitNUMBERS(); - - void handleDecimalSeparator(string _decsep, string _value); - void handleMaxRateValue(string _decsep, string _value); - void handleDecimalExtendedResolution(string _decsep, string _value); - void handleMaxRateType(string _decsep, string _value); - void handleAnalogToDigitTransitionStart(string _decsep, string _value); - void handleAllowNegativeRate(string _decsep, string _value); - void handleIgnoreLeadingNaN(string _decsep, string _value); - void handleChangeRateThreshold(string _decsep, string _value); - void handlecheckDigitIncreaseConsistency(std::string _decsep, std::string _value); + + void handleDecimalSeparator(std::string _decsep, std::string _value); + void handleMaxRateValue(std::string _decsep, std::string _value); + void handleDecimalExtendedResolution(std::string _decsep, std::string _value); + void handleMaxRateType(std::string _decsep, std::string _value); + void handleAnalogToDigitTransitionStart(std::string _decsep, std::string _value); + void handleAllowNegativeRate(std::string _decsep, std::string _value); + void handleIgnoreLeadingNaN(std::string _decsep, std::string _value); + void handleChangeRateThreshold(std::string _decsep, std::string _value); + void handleMaxFlowRate(std::string _decsep, std::string _value); + + std::vector addNumbersTogether(std::vector DigitValues, std::vector AnalogValues); void WriteDataLog(int _index); public: bool PreValueUse; - std::vector NUMBERS; + std::vector NUMBERS; - ClassFlowPostProcessing(std::vector* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit); - virtual ~ClassFlowPostProcessing(){}; - bool ReadParameter(FILE* pfile, string& aktparamgraph); - bool doFlow(string time); - string getReadout(int _number); - string getReadoutParam(bool _rawValue, bool _noerror, int _number = 0); - string getReadoutError(int _number = 0); - string getReadoutRate(int _number = 0); - string getReadoutTimeStamp(int _number = 0); + ClassFlowPostProcessing(std::vector *lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit); + virtual ~ClassFlowPostProcessing() {}; + bool ReadParameter(FILE *pFile, std::string &aktparamgraph); + bool doFlow(std::string time); + std::string getReadout(int _number); + std::string getReadoutParam(bool _rawValue, bool _noerror, int _number = 0); + std::string getReadoutError(int _number = 0); + std::string getReadoutRate(int _number = 0); + std::string getReadoutTimeStamp(int _number = 0); void SavePreValue(); - string getJsonFromNumber(int i, std::string _lineend); - string GetPreValue(std::string _number = ""); - bool SetPreValue(double zw, string _numbers, bool _extern = false); + std::string getJsonFromNumber(int i, std::string _lineend); + std::string GetPreValue(std::string _number = ""); + bool SetPreValue(double _newvalue, string _numbers, bool _extern = false); std::string GetJSON(std::string _lineend = "\n"); std::string getNumbersName(); void UpdateNachkommaDecimalShift(); - std::vector* GetNumbers(){return &NUMBERS;}; + std::vector *GetNumbers() { return &NUMBERS; }; - string name(){return "ClassFlowPostProcessing";}; + std::string name() { return "ClassFlowPostProcessing"; }; }; -#endif //CLASSFFLOWPOSTPROCESSING_H +#endif // CLASSFFLOWPOSTPROCESSING_H diff --git a/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp b/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp index 380c0233..c3e98f6e 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowTakeImage.cpp @@ -1,3 +1,5 @@ +#include "defines.h" + #include #include #include @@ -13,20 +15,16 @@ #include "esp_wifi.h" #include "esp_log.h" -#include "../../include/defines.h" #include "psram.h" #include -// #define DEBUG_DETAIL_ON -// #define WIFITURNOFF - static const char *TAG = "TAKEIMAGE"; esp_err_t ClassFlowTakeImage::camera_capture(void) { - string nm = namerawimage; - Camera.CaptureToFile(nm); + std::string file_name = NameRawImage; + Camera.capture_to_file(file_name); time(&TimeImageTaken); localtime(&TimeImageTaken); @@ -38,17 +36,22 @@ void ClassFlowTakeImage::takePictureWithFlash(int flash_duration) // in case the image is flipped, it must be reset here // rawImage->width = CCstatus.ImageWidth; rawImage->height = CCstatus.ImageHeight; + if (Camera.CamTempImage) + { + rawImage->width = CFstatus.ImageWidth; + rawImage->height = CFstatus.ImageHeight; + } ESP_LOGD(TAG, "flash_duration: %d", flash_duration); - Camera.CaptureToBasisImage(rawImage, flash_duration); + Camera.capture_to_basis_image(rawImage, flash_duration); time(&TimeImageTaken); localtime(&TimeImageTaken); - if (CCstatus.SaveAllFiles) + if (Camera.SaveAllFiles) { - rawImage->SaveToFile(namerawimage); + rawImage->SaveToFile(NameRawImage); } } @@ -56,466 +59,447 @@ void ClassFlowTakeImage::SetInitialParameter(void) { TimeImageTaken = 0; rawImage = NULL; + NameRawImage = "/sdcard/img_tmp/raw.jpg"; + disabled = false; - namerawimage = "/sdcard/img_tmp/raw.jpg"; } // auslesen der Kameraeinstellungen aus der config.ini // wird beim Start aufgerufen -bool ClassFlowTakeImage::ReadParameter(FILE *pfile, string &aktparamgraph) +bool ClassFlowTakeImage::ReadParameter(FILE *pFile, std::string &aktparamgraph) { - Camera.getSensorDatenToCCstatus(); // Kamera >>> CCstatus - - std::vector splitted; - - aktparamgraph = trim(aktparamgraph); - + aktparamgraph = trim_string_left_right(aktparamgraph); if (aktparamgraph.size() == 0) { - if (!this->GetNextParagraph(pfile, aktparamgraph)) + if (!GetNextParagraph(pFile, aktparamgraph)) { return false; } } - if (aktparamgraph.compare("[TakeImage]") != 0) + if ((to_upper(aktparamgraph).compare("[TAKEIMAGE]") != 0) && (to_upper(aktparamgraph).compare(";[TAKEIMAGE]") != 0)) { // Paragraph does not fit TakeImage return false; } - while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) + Camera.get_sensor_controll_config(&CCstatus); // Kamera >>> CCstatus + std::vector splitted; + + while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph)) { - splitted = ZerlegeZeile(aktparamgraph); + splitted = split_line(aktparamgraph); - if ((toUpper(splitted[0]) == "RAWIMAGESLOCATION") && (splitted.size() > 1)) + if (splitted.size() > 1) { - imagesLocation = "/sdcard" + splitted[1]; - isLogImage = true; - } + std::string _param = to_upper(splitted[0]); - else if ((toUpper(splitted[0]) == "RAWIMAGESRETENTION") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) + if (_param == "RAWIMAGESLOCATION") { - this->imagesRetention = std::stod(splitted[1]); - } - } - - else if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1)) - { - CCstatus.SaveAllFiles = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "WAITBEFORETAKINGPICTURE") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) + imagesLocation = "/sdcard" + splitted[1]; + isLogImage = true; + } + else if (_param == "RAWIMAGESRETENTION") { - int _WaitBeforePicture = std::stoi(splitted[1]); - if (_WaitBeforePicture != 0) + if (is_string_numeric(splitted[1])) { - CCstatus.WaitBeforePicture = _WaitBeforePicture; + imagesRetention = std::stod(splitted[1]); + } + } + else if (_param == "SAVEALLFILES") + { + Camera.SaveAllFiles = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "WAITBEFORETAKINGPICTURE") + { + if (is_string_numeric(splitted[1])) + { + int _WaitBeforePicture = std::stoi(splitted[1]); + if (_WaitBeforePicture != 0) + { + CCstatus.WaitBeforePicture = _WaitBeforePicture; + } + else + { + CCstatus.WaitBeforePicture = 2; + } + } + } + else if (_param == "CAMXCLKFREQMHZ") + { + if (is_string_numeric(splitted[1])) + { + int _CamXclkFreqMhz = std::stoi(splitted[1]); + CCstatus.CamXclkFreqMhz = clip_int(_CamXclkFreqMhz, 20, 1); + } + } + else if (_param == "CAMGAINCEILING") + { + std::string _ImageGainceiling = to_upper(splitted[1]); + + if (is_string_numeric(_ImageGainceiling)) + { + int _ImageGainceiling_ = std::stoi(_ImageGainceiling); + switch (_ImageGainceiling_) + { + case 1: + CCstatus.ImageGainceiling = GAINCEILING_4X; + break; + case 2: + CCstatus.ImageGainceiling = GAINCEILING_8X; + break; + case 3: + CCstatus.ImageGainceiling = GAINCEILING_16X; + break; + case 4: + CCstatus.ImageGainceiling = GAINCEILING_32X; + break; + case 5: + CCstatus.ImageGainceiling = GAINCEILING_64X; + break; + case 6: + CCstatus.ImageGainceiling = GAINCEILING_128X; + break; + default: + CCstatus.ImageGainceiling = GAINCEILING_2X; + } } else { - CCstatus.WaitBeforePicture = 2; + if (_ImageGainceiling == "X4") + { + CCstatus.ImageGainceiling = GAINCEILING_4X; + } + else if (_ImageGainceiling == "X8") + { + CCstatus.ImageGainceiling = GAINCEILING_8X; + } + else if (_ImageGainceiling == "X16") + { + CCstatus.ImageGainceiling = GAINCEILING_16X; + } + else if (_ImageGainceiling == "X32") + { + CCstatus.ImageGainceiling = GAINCEILING_32X; + } + else if (_ImageGainceiling == "X64") + { + CCstatus.ImageGainceiling = GAINCEILING_64X; + } + else if (_ImageGainceiling == "X128") + { + CCstatus.ImageGainceiling = GAINCEILING_128X; + } + else + { + CCstatus.ImageGainceiling = GAINCEILING_2X; + } } } - } - - else if ((toUpper(splitted[0]) == "CAMGAINCEILING") && (splitted.size() > 1)) - { - std::string _ImageGainceiling = toUpper(splitted[1]); - - if (isStringNumeric(_ImageGainceiling)) + else if (_param == "CAMQUALITY") { - int _ImageGainceiling_ = std::stoi(_ImageGainceiling); - switch (_ImageGainceiling_) + if (is_string_numeric(splitted[1])) { - case 1: - CCstatus.ImageGainceiling = GAINCEILING_4X; - break; - case 2: - CCstatus.ImageGainceiling = GAINCEILING_8X; - break; - case 3: - CCstatus.ImageGainceiling = GAINCEILING_16X; - break; - case 4: - CCstatus.ImageGainceiling = GAINCEILING_32X; - break; - case 5: - CCstatus.ImageGainceiling = GAINCEILING_64X; - break; - case 6: - CCstatus.ImageGainceiling = GAINCEILING_128X; - break; - default: - CCstatus.ImageGainceiling = GAINCEILING_2X; + int _ImageQuality = std::stoi(splitted[1]); + CCstatus.ImageQuality = clip_int(_ImageQuality, 63, 6); } } - else + else if (_param == "CAMBRIGHTNESS") { - if (_ImageGainceiling == "X4") + if (is_string_numeric(splitted[1])) { - CCstatus.ImageGainceiling = GAINCEILING_4X; + int _ImageBrightness = std::stoi(splitted[1]); + CCstatus.ImageBrightness = clip_int(_ImageBrightness, 2, -2); } - else if (_ImageGainceiling == "X8") + } + else if (_param == "CAMCONTRAST") + { + if (is_string_numeric(splitted[1])) { - CCstatus.ImageGainceiling = GAINCEILING_8X; + int _ImageContrast = std::stoi(splitted[1]); + CCstatus.ImageContrast = clip_int(_ImageContrast, 2, -2); } - else if (_ImageGainceiling == "X16") + } + else if (_param == "CAMSATURATION") + { + if (is_string_numeric(splitted[1])) { - CCstatus.ImageGainceiling = GAINCEILING_16X; + int _ImageSaturation = std::stoi(splitted[1]); + CCstatus.ImageSaturation = clip_int(_ImageSaturation, 2, -2); } - else if (_ImageGainceiling == "X32") + } + else if (_param == "CAMSHARPNESS") + { + if (is_string_numeric(splitted[1])) { - CCstatus.ImageGainceiling = GAINCEILING_32X; + int _ImageSharpness = std::stoi(splitted[1]); + if (Camera.CamSensorId == OV2640_PID) + { + CCstatus.ImageSharpness = clip_int(_ImageSharpness, 2, -2); + } + else + { + CCstatus.ImageSharpness = clip_int(_ImageSharpness, 3, -3); + } } - else if (_ImageGainceiling == "X64") + } + else if (_param == "CAMAUTOSHARPNESS") + { + CCstatus.ImageAutoSharpness = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "CAMSPECIALEFFECT") + { + std::string _ImageSpecialEffect = to_upper(splitted[1]); + + if (is_string_numeric(_ImageSpecialEffect)) { - CCstatus.ImageGainceiling = GAINCEILING_64X; - } - else if (_ImageGainceiling == "X128") - { - CCstatus.ImageGainceiling = GAINCEILING_128X; + int _ImageSpecialEffect_ = std::stoi(_ImageSpecialEffect); + CCstatus.ImageSpecialEffect = clip_int(_ImageSpecialEffect_, 6, 0); } else { - CCstatus.ImageGainceiling = GAINCEILING_2X; + if (_ImageSpecialEffect == "NEGATIVE") + { + CCstatus.ImageSpecialEffect = 1; + } + else if (_ImageSpecialEffect == "GRAYSCALE") + { + CCstatus.ImageSpecialEffect = 2; + } + else if (_ImageSpecialEffect == "RED") + { + CCstatus.ImageSpecialEffect = 3; + } + else if (_ImageSpecialEffect == "GREEN") + { + CCstatus.ImageSpecialEffect = 4; + } + else if (_ImageSpecialEffect == "BLUE") + { + CCstatus.ImageSpecialEffect = 5; + } + else if (_ImageSpecialEffect == "RETRO") + { + CCstatus.ImageSpecialEffect = 6; + } + else + { + CCstatus.ImageSpecialEffect = 0; + } } } - } - - else if ((toUpper(splitted[0]) == "CAMQUALITY") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) + else if (_param == "CAMWBMODE") { - int _ImageQuality = std::stoi(splitted[1]); - CCstatus.ImageQuality = clipInt(_ImageQuality, 63, 6); - } - } + std::string _ImageWbMode = to_upper(splitted[1]); - else if ((toUpper(splitted[0]) == "CAMBRIGHTNESS") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) - { - int _ImageBrightness = std::stoi(splitted[1]); - CCstatus.ImageBrightness = clipInt(_ImageBrightness, 2, -2); - } - } - - else if ((toUpper(splitted[0]) == "CAMCONTRAST") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) - { - int _ImageContrast = std::stoi(splitted[1]); - CCstatus.ImageContrast = clipInt(_ImageContrast, 2, -2); - } - } - - else if ((toUpper(splitted[0]) == "CAMSATURATION") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) - { - int _ImageSaturation = std::stoi(splitted[1]); - CCstatus.ImageSaturation = clipInt(_ImageSaturation, 2, -2); - } - } - - else if ((toUpper(splitted[0]) == "CAMSHARPNESS") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) - { - int _ImageSharpness = std::stoi(splitted[1]); - if (CCstatus.CamSensor_id == OV2640_PID) + if (is_string_numeric(_ImageWbMode)) { - CCstatus.ImageSharpness = clipInt(_ImageSharpness, 2, -2); + int _ImageWbMode_ = std::stoi(_ImageWbMode); + CCstatus.ImageWbMode = clip_int(_ImageWbMode_, 4, 0); } else { - CCstatus.ImageSharpness = clipInt(_ImageSharpness, 3, -3); + if (_ImageWbMode == "SUNNY") + { + CCstatus.ImageWbMode = 1; + } + else if (_ImageWbMode == "CLOUDY") + { + CCstatus.ImageWbMode = 2; + } + else if (_ImageWbMode == "OFFICE") + { + CCstatus.ImageWbMode = 3; + } + else if (_ImageWbMode == "HOME") + { + CCstatus.ImageWbMode = 4; + } + else + { + CCstatus.ImageWbMode = 0; + } } } - } - - else if ((toUpper(splitted[0]) == "CAMAUTOSHARPNESS") && (splitted.size() > 1)) - { - CCstatus.ImageAutoSharpness = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMSPECIALEFFECT") && (splitted.size() > 1)) - { - std::string _ImageSpecialEffect = toUpper(splitted[1]); - - if (isStringNumeric(_ImageSpecialEffect)) + else if (_param == "CAMAWB") { - int _ImageSpecialEffect_ = std::stoi(_ImageSpecialEffect); - CCstatus.ImageSpecialEffect = clipInt(_ImageSpecialEffect_, 6, 0); + CCstatus.ImageAwb = alphanumeric_to_boolean(splitted[1]); } - else + else if (_param == "CAMAWBGAIN") { - if (_ImageSpecialEffect == "NEGATIVE") + CCstatus.ImageAwbGain = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "CAMAEC") + { + CCstatus.ImageAec = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "CAMAEC2") + { + CCstatus.ImageAec2 = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "CAMAELEVEL") + { + if (is_string_numeric(splitted[1])) { - CCstatus.ImageSpecialEffect = 1; - } - else if (_ImageSpecialEffect == "GRAYSCALE") - { - CCstatus.ImageSpecialEffect = 2; - } - else if (_ImageSpecialEffect == "RED") - { - CCstatus.ImageSpecialEffect = 3; - } - else if (_ImageSpecialEffect == "GREEN") - { - CCstatus.ImageSpecialEffect = 4; - } - else if (_ImageSpecialEffect == "BLUE") - { - CCstatus.ImageSpecialEffect = 5; - } - else if (_ImageSpecialEffect == "RETRO") - { - CCstatus.ImageSpecialEffect = 6; - } - else - { - CCstatus.ImageSpecialEffect = 0; + int _ImageAeLevel = std::stoi(splitted[1]); + if (Camera.CamSensorId == OV2640_PID) + { + CCstatus.ImageAeLevel = clip_int(_ImageAeLevel, 2, -2); + } + else + { + CCstatus.ImageAeLevel = clip_int(_ImageAeLevel, 5, -5); + } } } - } - - else if ((toUpper(splitted[0]) == "CAMWBMODE") && (splitted.size() > 1)) - { - std::string _ImageWbMode = toUpper(splitted[1]); - - if (isStringNumeric(_ImageWbMode)) + else if (_param == "CAMAECVALUE") { - int _ImageWbMode_ = std::stoi(_ImageWbMode); - CCstatus.ImageWbMode = clipInt(_ImageWbMode_, 4, 0); - } - else - { - if (_ImageWbMode == "SUNNY") + if (is_string_numeric(splitted[1])) { - CCstatus.ImageWbMode = 1; - } - else if (_ImageWbMode == "CLOUDY") - { - CCstatus.ImageWbMode = 2; - } - else if (_ImageWbMode == "OFFICE") - { - CCstatus.ImageWbMode = 3; - } - else if (_ImageWbMode == "HOME") - { - CCstatus.ImageWbMode = 4; - } - else - { - CCstatus.ImageWbMode = 0; + int _ImageAecValue = std::stoi(splitted[1]); + CCstatus.ImageAecValue = clip_int(_ImageAecValue, 1200, 0); } } - } - - else if ((toUpper(splitted[0]) == "CAMAWB") && (splitted.size() > 1)) - { - CCstatus.ImageAwb = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMAWBGAIN") && (splitted.size() > 1)) - { - CCstatus.ImageAwbGain = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMAEC") && (splitted.size() > 1)) - { - CCstatus.ImageAec = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMAEC2") && (splitted.size() > 1)) - { - CCstatus.ImageAec2 = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMAELEVEL") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) + else if (_param == "CAMAGC") { - int _ImageAeLevel = std::stoi(splitted[1]); - if (CCstatus.CamSensor_id == OV2640_PID) + CCstatus.ImageAgc = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "CAMAGCGAIN") + { + if (is_string_numeric(splitted[1])) { - CCstatus.ImageAeLevel = clipInt(_ImageAeLevel, 2, -2); - } - else - { - CCstatus.ImageAeLevel = clipInt(_ImageAeLevel, 5, -5); + int _ImageAgcGain = std::stoi(splitted[1]); + CCstatus.ImageAgcGain = clip_int(_ImageAgcGain, 30, 0); } } - } - - else if ((toUpper(splitted[0]) == "CAMAECVALUE") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) + else if (_param == "CAMBPC") { - int _ImageAecValue = std::stoi(splitted[1]); - CCstatus.ImageAecValue = clipInt(_ImageAecValue, 1200, 0); + CCstatus.ImageBpc = alphanumeric_to_boolean(splitted[1]); } - } - - else if ((toUpper(splitted[0]) == "CAMAGC") && (splitted.size() > 1)) - { - CCstatus.ImageAgc = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMAGCGAIN") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) + else if (_param == "CAMWPC") { - int _ImageAgcGain = std::stoi(splitted[1]); - CCstatus.ImageAgcGain = clipInt(_ImageAgcGain, 30, 0); + CCstatus.ImageWpc = alphanumeric_to_boolean(splitted[1]); } - } - - else if ((toUpper(splitted[0]) == "CAMBPC") && (splitted.size() > 1)) - { - CCstatus.ImageBpc = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMWPC") && (splitted.size() > 1)) - { - CCstatus.ImageWpc = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMRAWGMA") && (splitted.size() > 1)) - { - CCstatus.ImageRawGma = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMLENC") && (splitted.size() > 1)) - { - CCstatus.ImageLenc = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMHMIRROR") && (splitted.size() > 1)) - { - CCstatus.ImageHmirror = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMVFLIP") && (splitted.size() > 1)) - { - CCstatus.ImageVflip = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMDCW") && (splitted.size() > 1)) - { - CCstatus.ImageDcw = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMDENOISE") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) + else if (_param == "CAMRAWGMA") { - int _ImageDenoiseLevel = std::stoi(splitted[1]); - if (CCstatus.CamSensor_id == OV2640_PID) + CCstatus.ImageRawGma = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "CAMLENC") + { + CCstatus.ImageLenc = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "CAMHMIRROR") + { + CCstatus.ImageHmirror = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "CAMVFLIP") + { + CCstatus.ImageVflip = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "CAMDCW") + { + CCstatus.ImageDcw = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "CAMDENOISE") + { + if (is_string_numeric(splitted[1])) { - CCstatus.ImageDenoiseLevel = 0; - } - else - { - CCstatus.ImageDenoiseLevel = clipInt(_ImageDenoiseLevel, 8, 0); + int _ImageDenoiseLevel = std::stoi(splitted[1]); + if (Camera.CamSensorId == OV2640_PID) + { + CCstatus.ImageDenoiseLevel = 0; + } + else + { + CCstatus.ImageDenoiseLevel = clip_int(_ImageDenoiseLevel, 8, 0); + } } } - } - - else if ((toUpper(splitted[0]) == "CAMZOOM") && (splitted.size() > 1)) - { - CCstatus.ImageZoomEnabled = alphanumericToBoolean(splitted[1]); - } - - else if ((toUpper(splitted[0]) == "CAMZOOMOFFSETX") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) + else if (_param == "CAMZOOM") { - int _ImageZoomOffsetX = std::stoi(splitted[1]); - if (CCstatus.CamSensor_id == OV2640_PID) + CCstatus.ImageZoomEnabled = alphanumeric_to_boolean(splitted[1]); + } + else if (_param == "CAMZOOMOFFSETX") + { + if (is_string_numeric(splitted[1])) { - CCstatus.ImageZoomOffsetX = clipInt(_ImageZoomOffsetX, 480, -480); - } - else if (CCstatus.CamSensor_id == OV3660_PID) - { - CCstatus.ImageZoomOffsetX = clipInt(_ImageZoomOffsetX, 704, -704); - } - else if (CCstatus.CamSensor_id == OV5640_PID) - { - CCstatus.ImageZoomOffsetX = clipInt(_ImageZoomOffsetX, 960, -960); + int _ImageZoomOffsetX = std::stoi(splitted[1]); + if (Camera.CamSensorId == OV2640_PID) + { + CCstatus.ImageZoomOffsetX = clip_int(_ImageZoomOffsetX, 480, -480); + } + else if (Camera.CamSensorId == OV3660_PID) + { + CCstatus.ImageZoomOffsetX = clip_int(_ImageZoomOffsetX, 704, -704); + } + else if (Camera.CamSensorId == OV5640_PID) + { + CCstatus.ImageZoomOffsetX = clip_int(_ImageZoomOffsetX, 960, -960); + } } } - } - - else if ((toUpper(splitted[0]) == "CAMZOOMOFFSETY") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) + else if (_param == "CAMZOOMOFFSETY") { - int _ImageZoomOffsetY = std::stoi(splitted[1]); - if (CCstatus.CamSensor_id == OV2640_PID) + if (is_string_numeric(splitted[1])) { - CCstatus.ImageZoomOffsetY = clipInt(_ImageZoomOffsetY, 360, -360); - } - else if (CCstatus.CamSensor_id == OV3660_PID) - { - CCstatus.ImageZoomOffsetY = clipInt(_ImageZoomOffsetY, 528, -528); - } - else if (CCstatus.CamSensor_id == OV5640_PID) - { - CCstatus.ImageZoomOffsetY = clipInt(_ImageZoomOffsetY, 720, -720); + int _ImageZoomOffsetY = std::stoi(splitted[1]); + if (Camera.CamSensorId == OV2640_PID) + { + CCstatus.ImageZoomOffsetY = clip_int(_ImageZoomOffsetY, 360, -360); + } + else if (Camera.CamSensorId == OV3660_PID) + { + CCstatus.ImageZoomOffsetY = clip_int(_ImageZoomOffsetY, 528, -528); + } + else if (Camera.CamSensorId == OV5640_PID) + { + CCstatus.ImageZoomOffsetY = clip_int(_ImageZoomOffsetY, 720, -720); + } } } - } - - else if ((toUpper(splitted[0]) == "CAMZOOMSIZE") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) + else if (_param == "CAMZOOMSIZE") { - int _ImageZoomSize = std::stoi(splitted[1]); - if (CCstatus.CamSensor_id == OV2640_PID) + if (is_string_numeric(splitted[1])) { - CCstatus.ImageZoomSize = clipInt(_ImageZoomSize, 29, 0); - } - else if (CCstatus.CamSensor_id == OV3660_PID) - { - CCstatus.ImageZoomSize = clipInt(_ImageZoomSize, 43, 0); - } - else if (CCstatus.CamSensor_id == OV5640_PID) - { - CCstatus.ImageZoomSize = clipInt(_ImageZoomSize, 59, 0); + int _ImageZoomSize = std::stoi(splitted[1]); + if (Camera.CamSensorId == OV2640_PID) + { + CCstatus.ImageZoomSize = clip_int(_ImageZoomSize, 29, 0); + } + else if (Camera.CamSensorId == OV3660_PID) + { + CCstatus.ImageZoomSize = clip_int(_ImageZoomSize, 43, 0); + } + else if (Camera.CamSensorId == OV5640_PID) + { + CCstatus.ImageZoomSize = clip_int(_ImageZoomSize, 59, 0); + } } } - } - - else if ((toUpper(splitted[0]) == "LEDINTENSITY") && (splitted.size() > 1)) - { - if (isStringNumeric(splitted[1])) + else if (_param == "LEDINTENSITY") { - int ledintensity = std::stoi(splitted[1]); - CCstatus.ImageLedIntensity = Camera.SetLEDIntensity(ledintensity); + if (is_string_numeric(splitted[1])) + { + int ledintensity = std::stoi(splitted[1]); + CCstatus.ImageLedIntensity = Camera.set_led_intensity(ledintensity); + } } - } - - else if ((toUpper(splitted[0]) == "DEMO") && (splitted.size() > 1)) - { - CCstatus.DemoMode = alphanumericToBoolean(splitted[1]); - if (CCstatus.DemoMode == true) + else if (_param == "DEMO") { - Camera.useDemoMode(); + Camera.DemoMode = alphanumeric_to_boolean(splitted[1]); + if (Camera.DemoMode == true) + { + Camera.use_demo_mode(); + } } } } - Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera - Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip); + Camera.set_sensor_controll_config(&CCstatus); // CCstatus >>> Kamera + Camera.set_quality_zoom_size(&CCstatus); + + Camera.changedCameraSettings = false; + Camera.CamTempImage = false; rawImage = new CImageBasis("rawImage"); rawImage->CreateEmptyImage(CCstatus.ImageWidth, CCstatus.ImageHeight, 3); @@ -530,57 +514,39 @@ ClassFlowTakeImage::ClassFlowTakeImage(std::vector *lfc) : ClassFlo SetInitialParameter(); } -string ClassFlowTakeImage::getHTMLSingleStep(string host) +std::string ClassFlowTakeImage::getHTMLSingleStep(std::string host) { - string result; + std::string result; result = "Raw Image:
\n\n"; return result; } // wird bei jeder Auswertrunde aufgerufen -bool ClassFlowTakeImage::doFlow(string zwtime) +bool ClassFlowTakeImage::doFlow(std::string zwtime) { psram_init_shared_memory_for_take_image_step(); - string logPath = CreateLogFolder(zwtime); + std::string logPath = CreateLogFolder(zwtime); int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000); - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - Before takePictureWithFlash"); -#endif + if (Camera.CamTempImage) + { + flash_duration = (int)(CFstatus.WaitBeforePicture * 1000); + } #ifdef WIFITURNOFF esp_wifi_stop(); // to save power usage and #endif - // wenn die Kameraeinstellungen durch Erstellen eines neuen Referenzbildes verändert wurden, müssen sie neu gesetzt werden - if (CFstatus.changedCameraSettings) - { - Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera - Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip); - Camera.LedIntensity = CCstatus.ImageLedIntensity; - CFstatus.changedCameraSettings = false; - } - takePictureWithFlash(flash_duration); #ifdef WIFITURNOFF esp_wifi_start(); #endif -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After takePictureWithFlash"); -#endif - LogImage(logPath, "raw", NULL, NULL, zwtime, rawImage); RemoveOldLogs(); - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After RemoveOldLogs"); -#endif - psram_deinit_shared_memory_for_take_image_step(); return true; @@ -589,18 +555,29 @@ bool ClassFlowTakeImage::doFlow(string zwtime) esp_err_t ClassFlowTakeImage::SendRawJPG(httpd_req_t *req) { int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000); + if (Camera.CamTempImage) + { + flash_duration = (int)(CFstatus.WaitBeforePicture * 1000); + } + time(&TimeImageTaken); localtime(&TimeImageTaken); - return Camera.CaptureToHTTP(req, flash_duration); + return Camera.capture_to_http(req, flash_duration); } ImageData *ClassFlowTakeImage::SendRawImage(void) { CImageBasis *zw = new CImageBasis("SendRawImage", rawImage); ImageData *id; + int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000); - Camera.CaptureToBasisImage(zw, flash_duration); + if (Camera.CamTempImage) + { + flash_duration = (int)(CFstatus.WaitBeforePicture * 1000); + } + + Camera.capture_to_basis_image(zw, flash_duration); time(&TimeImageTaken); localtime(&TimeImageTaken); diff --git a/code/components/jomjol_flowcontroll/ClassFlowTakeImage.h b/code/components/jomjol_flowcontroll/ClassFlowTakeImage.h index 0be4f3af..9b2de5c8 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowTakeImage.h +++ b/code/components/jomjol_flowcontroll/ClassFlowTakeImage.h @@ -3,17 +3,17 @@ #ifndef CLASSFFLOWTAKEIMAGE_H #define CLASSFFLOWTAKEIMAGE_H +#include +#include "defines.h" + #include "ClassFlowImage.h" #include "ClassControllCamera.h" -#include "../../include/defines.h" - -#include class ClassFlowTakeImage : public ClassFlowImage { protected: time_t TimeImageTaken; - string namerawimage; + std::string NameRawImage; esp_err_t camera_capture(void); void takePictureWithFlash(int flash_duration); @@ -25,11 +25,11 @@ public: ClassFlowTakeImage(std::vector *lfc); - bool ReadParameter(FILE *pfile, string &aktparamgraph); - bool doFlow(string time); - string getHTMLSingleStep(string host); + bool ReadParameter(FILE *pFile, std::string &aktparamgraph); + bool doFlow(std::string time); + std::string getHTMLSingleStep(std::string host); time_t getTimeImageTaken(void); - string name() { return "ClassFlowTakeImage"; }; + std::string name() { return "ClassFlowTakeImage"; }; ImageData *SendRawImage(void); esp_err_t SendRawJPG(httpd_req_t *req); @@ -37,4 +37,4 @@ public: ~ClassFlowTakeImage(void); }; -#endif // CLASSFFLOWTAKEIMAGE_H \ No newline at end of file +#endif // CLASSFFLOWTAKEIMAGE_H diff --git a/code/components/jomjol_flowcontroll/ClassFlowWebhook.cpp b/code/components/jomjol_flowcontroll/ClassFlowWebhook.cpp index 5194c82b..573d4f6f 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowWebhook.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowWebhook.cpp @@ -1,8 +1,11 @@ -#ifdef ENABLE_WEBHOOK -#include -#include "ClassFlowWebhook.h" +#include "defines.h" #include "Helper.h" -#include "connect_wlan.h" + +#include +#include + +#include "ClassFlowWebhook.h" +#include "connect_wifi_sta.h" #include "time_sntp.h" #include "interface_webhook.h" @@ -10,32 +13,35 @@ #include "ClassFlowPostProcessing.h" #include "ClassFlowAlignment.h" #include "esp_log.h" -#include "../../include/defines.h" #include "ClassLogFile.h" -#include +static const char *TAG = "WEBHOOK"; -static const char* TAG = "WEBHOOK"; +Webhook_controll_config_t Webhook_controll_config; void ClassFlowWebhook::SetInitialParameter(void) { - uri = ""; + Webhook_controll_config.enabled = false; + Webhook_controll_config.uri = ""; + Webhook_controll_config.apikey = ""; + Webhook_controll_config.uploadImg = 0; + Webhook_controll_config.oldValue = ""; + flowpostprocessing = NULL; flowAlignment = NULL; previousElement = NULL; - ListFlowControll = NULL; + ListFlowControll = NULL; + disabled = false; - WebhookEnable = false; - WebhookUploadImg = 0; -} +} ClassFlowWebhook::ClassFlowWebhook() { SetInitialParameter(); } -ClassFlowWebhook::ClassFlowWebhook(std::vector* lfc) +ClassFlowWebhook::ClassFlowWebhook(std::vector *lfc) { SetInitialParameter(); @@ -44,17 +50,17 @@ ClassFlowWebhook::ClassFlowWebhook(std::vector* lfc) { if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0) { - flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i]; - } - if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0) - { - flowAlignment = (ClassFlowAlignment*) (*ListFlowControll)[i]; + flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i]; } + if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0) + { + flowAlignment = (ClassFlowAlignment *)(*ListFlowControll)[i]; + } } } -ClassFlowWebhook::ClassFlowWebhook(std::vector* lfc, ClassFlow *_prev) +ClassFlowWebhook::ClassFlowWebhook(std::vector *lfc, ClassFlow *_prev) { SetInitialParameter(); @@ -65,107 +71,131 @@ ClassFlowWebhook::ClassFlowWebhook(std::vector* lfc, ClassFlow *_pre { if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0) { - flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i]; + flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i]; } + if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0) { - flowAlignment = (ClassFlowAlignment*) (*ListFlowControll)[i]; + flowAlignment = (ClassFlowAlignment *)(*ListFlowControll)[i]; } } } - -bool ClassFlowWebhook::ReadParameter(FILE* pfile, string& aktparamgraph) +bool ClassFlowWebhook::ReadParameter(FILE *pFile, std::string &aktparamgraph) { - std::vector splitted; - - aktparamgraph = trim(aktparamgraph); - printf("akt param: %s\n", aktparamgraph.c_str()); - + aktparamgraph = trim_string_left_right(aktparamgraph); if (aktparamgraph.size() == 0) - if (!this->GetNextParagraph(pfile, aktparamgraph)) - return false; - - if (toUpper(aktparamgraph).compare("[WEBHOOK]") != 0) - return false; - - - - while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph)) { - ESP_LOGD(TAG, "while loop reading line: %s", aktparamgraph.c_str()); - splitted = ZerlegeZeile(aktparamgraph); - std::string _param = GetParameterName(splitted[0]); - - if ((toUpper(_param) == "URI") && (splitted.size() > 1)) + if (!GetNextParagraph(pFile, aktparamgraph)) { - this->uri = splitted[1]; + return false; } - if (((toUpper(_param) == "APIKEY")) && (splitted.size() > 1)) + } + + if ((to_upper(aktparamgraph).compare("[WEBHOOK]") != 0) && (to_upper(aktparamgraph).compare(";[WEBHOOK]") != 0)) + { + return false; + } + + if (aktparamgraph[0] == ';') + { + Webhook_controll_config.enabled = false; + while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph)); + ESP_LOGD(TAG, "Webhook is disabled!"); + + return true; + } + + std::vector splitted; + + while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph)) + { + splitted = split_line(aktparamgraph); + + if (splitted.size() > 1) { - this->apikey = splitted[1]; - } - if (((toUpper(_param) == "UPLOADIMG")) && (splitted.size() > 1)) - { - if (toUpper(splitted[1]) == "1") + std::string _param = to_upper(GetParameterName(splitted[0])); + + if (_param == "URI") { - this->WebhookUploadImg = 1; - } else if (toUpper(splitted[1]) == "2") + Webhook_controll_config.uri = splitted[1]; + } + else if (_param == "APIKEY") { - this->WebhookUploadImg = 2; + Webhook_controll_config.apikey = splitted[1]; + } + else if (_param == "UPLOADIMG") + { + if (to_upper(splitted[1]) == "1") + { + Webhook_controll_config.uploadImg = 1; + } + else if (to_upper(splitted[1]) == "2") + { + Webhook_controll_config.uploadImg = 2; + } } } } - - WebhookInit(uri,apikey); - WebhookEnable = true; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Webhook Enabled for Uri " + uri); - printf("uri: %s\n", uri.c_str()); + if ((Webhook_controll_config.uri.length() > 0) && (Webhook_controll_config.apikey.length() > 0)) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init Webhook with uri: " + Webhook_controll_config.uri + ", apikey: *****"); + WebhookInit(Webhook_controll_config.uri, Webhook_controll_config.apikey); + Webhook_controll_config.enabled = true; + } + else + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Webhook init skipped as we are missing some parameters"); + Webhook_controll_config.enabled = false; + } + return true; } - -void ClassFlowWebhook::handleMeasurement(string _decsep, string _value) +void ClassFlowWebhook::handleMeasurement(std::string _decsep, std::string _value) { - string _digit, _decpos; + std::string _digit; int _pospunkt = _decsep.find_first_of("."); -// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt); + if (_pospunkt > -1) + { _digit = _decsep.substr(0, _pospunkt); + } else + { _digit = "default"; + } + for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j) { - if (_digit == "default") // Set to default first (if nothing else is set) - { - flowpostprocessing->NUMBERS[j]->MeasurementV2 = _value; - } - if (flowpostprocessing->NUMBERS[j]->name == _digit) + // Set to default first (if nothing else is set) + if ((_digit == "default") || (flowpostprocessing->NUMBERS[j]->name == _digit)) { flowpostprocessing->NUMBERS[j]->MeasurementV2 = _value; } } } - -bool ClassFlowWebhook::doFlow(string zwtime) +bool ClassFlowWebhook::doFlow(std::string temp_time) { - if (!WebhookEnable) + if (!Webhook_controll_config.enabled) + { return true; + } if (flowpostprocessing) { printf("vor sende WebHook"); bool numbersWithError = WebhookPublish(flowpostprocessing->GetNumbers()); - #ifdef ALGROI_LOAD_FROM_MEM_AS_JPG - if ((WebhookUploadImg == 1 || (WebhookUploadImg != 0 && numbersWithError)) && flowAlignment && flowAlignment->AlgROI) { - WebhookUploadPic(flowAlignment->AlgROI); - } - #endif +#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG + if ((Webhook_controll_config.uploadImg == 1 || (Webhook_controll_config.uploadImg != 0 && numbersWithError)) && flowAlignment && flowAlignment->AlgROI) + { + WebhookUploadPic(flowAlignment->AlgROI); + } +#endif } - + return true; } -#endif //ENABLE_WEBHOOK \ No newline at end of file diff --git a/code/components/jomjol_flowcontroll/ClassFlowWebhook.h b/code/components/jomjol_flowcontroll/ClassFlowWebhook.h index 8f679d42..4e13df8f 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowWebhook.h +++ b/code/components/jomjol_flowcontroll/ClassFlowWebhook.h @@ -1,43 +1,42 @@ -#ifdef ENABLE_WEBHOOK - #pragma once #ifndef CLASSFWEBHOOK_H #define CLASSFWEBHOOK_H -#include "ClassFlow.h" +#include +#include "ClassFlow.h" #include "ClassFlowPostProcessing.h" #include "ClassFlowAlignment.h" -#include +typedef struct +{ + bool enabled; + std::string uri; + std::string apikey; + int uploadImg; + std::string oldValue; +} Webhook_controll_config_t; -class ClassFlowWebhook : - public ClassFlow +extern Webhook_controll_config_t Webhook_controll_config; + +class ClassFlowWebhook : public ClassFlow { protected: - std::string uri, apikey; - ClassFlowPostProcessing* flowpostprocessing; - ClassFlowAlignment* flowAlignment; - - bool WebhookEnable; - int WebhookUploadImg; - - void SetInitialParameter(void); - - void handleFieldname(string _decsep, string _value); - void handleMeasurement(string _decsep, string _value); + ClassFlowPostProcessing *flowpostprocessing; + ClassFlowAlignment *flowAlignment; + void SetInitialParameter(void); + void handleMeasurement(std::string _decsep, std::string _value); public: ClassFlowWebhook(); - ClassFlowWebhook(std::vector* lfc); - ClassFlowWebhook(std::vector* lfc, ClassFlow *_prev); + ClassFlowWebhook(std::vector *lfc); + ClassFlowWebhook(std::vector *lfc, ClassFlow *_prev); - bool ReadParameter(FILE* pfile, string& aktparamgraph); - bool doFlow(string time); - string name(){return "ClassFlowWebhook";}; + bool ReadParameter(FILE *pFile, std::string &aktparamgraph); + bool doFlow(std::string temp_time); + std::string name() { return "ClassFlowWebhook"; }; }; -#endif //CLASSFWEBHOOK_H -#endif //ENABLE_WEBHOOK \ No newline at end of file +#endif // CLASSFWEBHOOK_H diff --git a/code/components/jomjol_flowcontroll/MainFlowControl.cpp b/code/components/jomjol_flowcontroll/MainFlowControl.cpp index 14dab641..ee3174f1 100644 --- a/code/components/jomjol_flowcontroll/MainFlowControl.cpp +++ b/code/components/jomjol_flowcontroll/MainFlowControl.cpp @@ -1,15 +1,16 @@ +#include "defines.h" + #include "MainFlowControl.h" #include #include -#include "string.h" -#include "esp_log.h" +#include +#include #include #include #include -#include "../../include/defines.h" #include "Helper.h" #include "statusled.h" @@ -24,8 +25,9 @@ #include "server_file.h" -#include "read_wlanini.h" -#include "connect_wlan.h" +#include "read_network_config.h" +#include "connect_wifi_sta.h" +#include "connect_roaming.h" #include "psram.h" #include "basic_auth.h" @@ -35,7 +37,6 @@ #endif ClassFlowControll flowctrl; -camera_flow_config_temp_t CFstatus; TaskHandle_t xHandletask_autodoFlow = NULL; @@ -50,12 +51,9 @@ bool isPlannedReboot = false; static const char *TAG = "MAINCTRL"; -// #define DEBUG_DETAIL_ON - void CheckIsPlannedReboot(void) { FILE *pfile; - if ((pfile = fopen("/sdcard/reboot.txt", "r")) == NULL) { // LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Initial boot or not a planned reboot"); @@ -64,7 +62,7 @@ void CheckIsPlannedReboot(void) else { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Planned reboot"); - DeleteFile("/sdcard/reboot.txt"); // Prevent Boot Loop!!! + delete_file("/sdcard/reboot.txt"); // Prevent Boot Loop!!! isPlannedReboot = true; } } @@ -96,37 +94,21 @@ bool isSetupModusActive(void) void DeleteMainFlowTask(void) { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "DeleteMainFlowTask: xHandletask_autodoFlow: %ld", (long)xHandletask_autodoFlow); -#endif - if (xHandletask_autodoFlow != NULL) { vTaskDelete(xHandletask_autodoFlow); xHandletask_autodoFlow = NULL; } - -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Killed: xHandletask_autodoFlow"); -#endif } void doInit(void) { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Start flowctrl.InitFlow(config);"); -#endif flowctrl.InitFlow(CONFIG_FILE); -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Finished flowctrl.InitFlow(config);"); -#endif /* GPIO handler has to be initialized before MQTT init to ensure proper topic subscription */ gpio_handler_init(); -#ifdef ENABLE_MQTT flowctrl.StartMQTTService(); -#endif // ENABLE_MQTT } bool doflow(void) @@ -136,182 +118,12 @@ bool doflow(void) flowisrunning = true; flowctrl.doFlow(zw_time); flowisrunning = false; - -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "doflow - end %s", zw_time.c_str()); -#endif - return true; } -esp_err_t setCCstatusToCFstatus(void) -{ - CFstatus.CamSensor_id = CCstatus.CamSensor_id; - - CFstatus.ImageFrameSize = CCstatus.ImageFrameSize; - - CFstatus.ImageContrast = CCstatus.ImageContrast; - CFstatus.ImageBrightness = CCstatus.ImageBrightness; - CFstatus.ImageSaturation = CCstatus.ImageSaturation; - - CFstatus.ImageQuality = CCstatus.ImageQuality; - - CFstatus.ImageGainceiling = CCstatus.ImageGainceiling; - - CFstatus.ImageAgc = CCstatus.ImageAgc; - CFstatus.ImageAec = CCstatus.ImageAec; - CFstatus.ImageHmirror = CCstatus.ImageHmirror; - CFstatus.ImageVflip = CCstatus.ImageVflip; - - CFstatus.ImageAwb = CCstatus.ImageAwb; - CFstatus.ImageAec2 = CCstatus.ImageAec2; - CFstatus.ImageAecValue = CCstatus.ImageAecValue; - CFstatus.ImageSpecialEffect = CCstatus.ImageSpecialEffect; - CFstatus.ImageWbMode = CCstatus.ImageWbMode; - CFstatus.ImageAeLevel = CCstatus.ImageAeLevel; - - CFstatus.ImageDcw = CCstatus.ImageDcw; - CFstatus.ImageBpc = CCstatus.ImageBpc; - CFstatus.ImageWpc = CCstatus.ImageWpc; - CFstatus.ImageAwbGain = CCstatus.ImageAwbGain; - CFstatus.ImageAgcGain = CCstatus.ImageAgcGain; - - CFstatus.ImageRawGma = CCstatus.ImageRawGma; - CFstatus.ImageLenc = CCstatus.ImageLenc; - - CFstatus.ImageSharpness = CCstatus.ImageSharpness; - CFstatus.ImageAutoSharpness = CCstatus.ImageAutoSharpness; - - CFstatus.ImageDenoiseLevel = CCstatus.ImageDenoiseLevel; - - CFstatus.ImageLedIntensity = CCstatus.ImageLedIntensity; - - CFstatus.ImageZoomEnabled = CCstatus.ImageZoomEnabled; - CFstatus.ImageZoomOffsetX = CCstatus.ImageZoomOffsetX; - CFstatus.ImageZoomOffsetY = CCstatus.ImageZoomOffsetY; - CFstatus.ImageZoomSize = CCstatus.ImageZoomSize; - - CFstatus.WaitBeforePicture = CCstatus.WaitBeforePicture; - - return ESP_OK; -} - -esp_err_t setCFstatusToCCstatus(void) -{ - // CCstatus.CamSensor_id = CFstatus.CamSensor_id; - - CCstatus.ImageFrameSize = CFstatus.ImageFrameSize; - - CCstatus.ImageContrast = CFstatus.ImageContrast; - CCstatus.ImageBrightness = CFstatus.ImageBrightness; - CCstatus.ImageSaturation = CFstatus.ImageSaturation; - - CCstatus.ImageQuality = CFstatus.ImageQuality; - - CCstatus.ImageGainceiling = CFstatus.ImageGainceiling; - - CCstatus.ImageAgc = CFstatus.ImageAgc; - CCstatus.ImageAec = CFstatus.ImageAec; - CCstatus.ImageHmirror = CFstatus.ImageHmirror; - CCstatus.ImageVflip = CFstatus.ImageVflip; - - CCstatus.ImageAwb = CFstatus.ImageAwb; - CCstatus.ImageAec2 = CFstatus.ImageAec2; - CCstatus.ImageAecValue = CFstatus.ImageAecValue; - CCstatus.ImageSpecialEffect = CFstatus.ImageSpecialEffect; - CCstatus.ImageWbMode = CFstatus.ImageWbMode; - CCstatus.ImageAeLevel = CFstatus.ImageAeLevel; - - CCstatus.ImageDcw = CFstatus.ImageDcw; - CCstatus.ImageBpc = CFstatus.ImageBpc; - CCstatus.ImageWpc = CFstatus.ImageWpc; - CCstatus.ImageAwbGain = CFstatus.ImageAwbGain; - CCstatus.ImageAgcGain = CFstatus.ImageAgcGain; - - CCstatus.ImageRawGma = CFstatus.ImageRawGma; - CCstatus.ImageLenc = CFstatus.ImageLenc; - - CCstatus.ImageSharpness = CFstatus.ImageSharpness; - CCstatus.ImageAutoSharpness = CFstatus.ImageAutoSharpness; - - CCstatus.ImageDenoiseLevel = CFstatus.ImageDenoiseLevel; - - CCstatus.ImageLedIntensity = CFstatus.ImageLedIntensity; - - CCstatus.ImageZoomEnabled = CFstatus.ImageZoomEnabled; - CCstatus.ImageZoomOffsetX = CFstatus.ImageZoomOffsetX; - CCstatus.ImageZoomOffsetY = CFstatus.ImageZoomOffsetY; - CCstatus.ImageZoomSize = CFstatus.ImageZoomSize; - - CCstatus.WaitBeforePicture = CFstatus.WaitBeforePicture; - - return ESP_OK; -} - -esp_err_t setCFstatusToCam(void) -{ - sensor_t *s = esp_camera_sensor_get(); - - if (s != NULL) - { - s->set_framesize(s, CFstatus.ImageFrameSize); - - // s->set_contrast(s, CFstatus.ImageContrast); // -2 to 2 - // s->set_brightness(s, CFstatus.ImageBrightness); // -2 to 2 - Camera.SetCamContrastBrightness(s, CFstatus.ImageContrast, CFstatus.ImageBrightness); - - s->set_saturation(s, CFstatus.ImageSaturation); // -2 to 2 - - s->set_quality(s, CFstatus.ImageQuality); // 0 - 63 - - // s->set_gainceiling(s, CFstatus.ImageGainceiling); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128) - Camera.SetCamGainceiling(s, CFstatus.ImageGainceiling); - - s->set_gain_ctrl(s, CFstatus.ImageAgc); // 0 = disable , 1 = enable - s->set_exposure_ctrl(s, CFstatus.ImageAec); // 0 = disable , 1 = enable - s->set_hmirror(s, CFstatus.ImageHmirror); // 0 = disable , 1 = enable - s->set_vflip(s, CFstatus.ImageVflip); // 0 = disable , 1 = enable - - s->set_whitebal(s, CFstatus.ImageAwb); // 0 = disable , 1 = enable - s->set_aec2(s, CFstatus.ImageAec2); // 0 = disable , 1 = enable - s->set_aec_value(s, CFstatus.ImageAecValue); // 0 to 1200 - // s->set_special_effect(s, CFstatus.ImageSpecialEffect); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia) - Camera.SetCamSpecialEffect(s, CFstatus.ImageSpecialEffect); - s->set_wb_mode(s, CFstatus.ImageWbMode); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home) - s->set_ae_level(s, CFstatus.ImageAeLevel); // -2 to 2 - - s->set_dcw(s, CFstatus.ImageDcw); // 0 = disable , 1 = enable - s->set_bpc(s, CFstatus.ImageBpc); // 0 = disable , 1 = enable - s->set_wpc(s, CFstatus.ImageWpc); // 0 = disable , 1 = enable - s->set_awb_gain(s, CFstatus.ImageAwbGain); // 0 = disable , 1 = enable - s->set_agc_gain(s, CFstatus.ImageAgcGain); // 0 to 30 - - s->set_raw_gma(s, CFstatus.ImageRawGma); // 0 = disable , 1 = enable - s->set_lenc(s, CFstatus.ImageLenc); // 0 = disable , 1 = enable - - // s->set_sharpness(s, CFstatus.ImageSharpness); // auto-sharpness is not officially supported, default to 0 - Camera.SetCamSharpness(CFstatus.ImageAutoSharpness, CFstatus.ImageSharpness); - s->set_denoise(s, CFstatus.ImageDenoiseLevel); // The OV2640 does not support it, OV3660 and OV5640 (0 to 8) - - TickType_t xDelay2 = 100 / portTICK_PERIOD_MS; - vTaskDelay(xDelay2); - - return ESP_OK; - } - else - { - return ESP_FAIL; - } -} - esp_err_t handler_get_heap(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_get_heap - Start"); - ESP_LOGD(TAG, "handler_get_heap uri: %s", req->uri); -#endif - - std::string zw = "Heap info:
" + getESPHeapInfo(); + std::string zw = "Heap info:
" + get_heapinfo(); #ifdef TASK_ANALYSIS_ON char *pcTaskList = (char *)calloc_psram_heap(std::string(TAG) + "->pcTaskList", 1, sizeof(char) * 768, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); @@ -338,20 +150,11 @@ esp_err_t handler_get_heap(httpd_req_t *req) httpd_resp_send(req, NULL, 0); } -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_get_heap - Done"); -#endif - return ESP_OK; } esp_err_t handler_init(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_init - Start"); - ESP_LOGD(TAG, "handler_doinit uri: %s", req->uri); -#endif - httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); const char *resp_str = "Init started
"; httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); @@ -361,32 +164,20 @@ esp_err_t handler_init(httpd_req_t *req) resp_str = "Init done
"; httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_init - Done"); -#endif - return ESP_OK; } esp_err_t handler_stream(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_stream - Start"); - ESP_LOGD(TAG, "handler_stream uri: %s", req->uri); -#endif - char _query[50]; char _value[10]; bool flashlightOn = false; if (httpd_req_get_url_query_str(req, _query, 50) == ESP_OK) { - // ESP_LOGD(TAG, "Query: %s", _query); + // ESP_LOGD(TAG, "Query: %s", _query); if (httpd_query_key_value(_query, "flashlight", _value, 10) == ESP_OK) { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "flashlight is found%s", _value); -#endif if (strlen(_value) > 0) { flashlightOn = true; @@ -394,21 +185,13 @@ esp_err_t handler_stream(httpd_req_t *req) } } - Camera.CaptureToStream(req, flashlightOn); - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_stream - Done"); -#endif + Camera.capture_to_stream(req, flashlightOn); return ESP_OK; } esp_err_t handler_flow_start(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_flow_start - Start"); -#endif - ESP_LOGD(TAG, "handler_flow_start uri: %s", req->uri); httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); @@ -426,20 +209,11 @@ esp_err_t handler_flow_start(httpd_req_t *req) httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Flow start triggered by REST API, but flow is not active"); } -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_flow_start - Done"); -#endif - return ESP_OK; } -#ifdef ENABLE_MQTT esp_err_t MQTTCtrlFlowStart(std::string _topic) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("MQTTCtrlFlowStart - Start"); -#endif - ESP_LOGD(TAG, "MQTTCtrlFlowStart: topic %s", _topic.c_str()); if (autostartIsEnabled) @@ -452,20 +226,11 @@ esp_err_t MQTTCtrlFlowStart(std::string _topic) LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Flow start triggered by MQTT topic " + _topic + ", but flow is not active!"); } -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("MQTTCtrlFlowStart - Done"); -#endif - return ESP_OK; } -#endif // ENABLE_MQTT esp_err_t handler_json(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_json - Start"); -#endif - ESP_LOGD(TAG, "handler_JSON uri: %s", req->uri); if (bTaskAutoFlowCreated) @@ -489,35 +254,27 @@ esp_err_t handler_json(httpd_req_t *req) return ESP_ERR_NOT_FOUND; } -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_JSON - Done"); -#endif - return ESP_OK; } /** - * Generates a http response containing the OpenMetrics (https://openmetrics.io/) text wire format + * Generates a http response containing the OpenMetrics (https://openmetrics.io/) text wire format * according to https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#text-format. - * + * * A MetricFamily with a Metric for each Sequence is provided. If no valid value is available, the metric is not provided. * MetricPoints are provided without a timestamp. Additional metrics with some device information is also provided. - * + * * The metric name prefix is 'ai_on_the_edge_device_'. - * + * * example configuration for Prometheus (`prometheus.yml`): - * + * * - job_name: watermeter * static_configs: * - targets: ['watermeter.fritz.box'] - * -*/ + * + */ esp_err_t handler_openmetrics(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_openmetrics - Start"); -#endif - ESP_LOGD(TAG, "handler_openmetrics uri: %s", req->uri); if (bTaskAutoFlowCreated) @@ -531,16 +288,16 @@ esp_err_t handler_openmetrics(httpd_req_t *req) string response = createSequenceMetrics(metricNamePrefix, flowctrl.getNumbers()); // CPU Temperature - response += createMetric(metricNamePrefix + "_cpu_temperature_celsius", "current cpu temperature in celsius", "gauge", std::to_string((int)temperatureRead())); + response += createMetric(metricNamePrefix + "_cpu_temperature_celsius", "current cpu temperature in celsius", "gauge", std::to_string((int)read_tempsensor())); // WiFi signal strength - response += createMetric(metricNamePrefix + "_rssi_dbm", "current WiFi signal strength in dBm", "gauge", std::to_string(get_WIFI_RSSI())); + response += createMetric(metricNamePrefix + "_rssi_dbm", "current WiFi signal strength in dBm", "gauge", std::to_string(get_wifi_rssi())); // memory info - response += createMetric(metricNamePrefix + "_memory_heap_free_bytes", "available heap memory", "gauge", std::to_string(getESPHeapSize())); + response += createMetric(metricNamePrefix + "_memory_heap_free_bytes", "available heap memory", "gauge", std::to_string(get_heapsize())); // device uptime - response += createMetric(metricNamePrefix + "_uptime_seconds", "device uptime in seconds", "gauge", std::to_string((long)getUpTime())); + response += createMetric(metricNamePrefix + "_uptime_seconds", "device uptime in seconds", "gauge", std::to_string((long)get_uptime())); // data aquisition round response += createMetric(metricNamePrefix + "_rounds_total", "data aquisition rounds since device startup", "counter", std::to_string(countRounds)); @@ -554,26 +311,17 @@ esp_err_t handler_openmetrics(httpd_req_t *req) return ESP_ERR_NOT_FOUND; } -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_openmetrics - Done"); -#endif - return ESP_OK; } -esp_err_t handler_wasserzaehler(httpd_req_t *req) +esp_err_t handler_value(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler water counter - Start"); -#endif - if (bTaskAutoFlowCreated) { bool _rawValue = false; bool _noerror = false; bool _all = false; std::string _type = "value"; - std::string zw; ESP_LOGD(TAG, "handler water counter uri: %s", req->uri); @@ -582,36 +330,24 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req) if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) { - // ESP_LOGD(TAG, "Query: %s", _query); + // ESP_LOGD(TAG, "Query: %s", _query); if (httpd_query_key_value(_query, "all", _size, 10) == ESP_OK) { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "all is found%s", _size); -#endif _all = true; } if (httpd_query_key_value(_query, "type", _size, 10) == ESP_OK) { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "all is found: %s", _size); -#endif _type = std::string(_size); } if (httpd_query_key_value(_query, "rawvalue", _size, 10) == ESP_OK) { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "rawvalue is found: %s", _size); -#endif _rawValue = true; } if (httpd_query_key_value(_query, "noerror", _size, 10) == ESP_OK) { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "noerror is found: %s", _size); -#endif _noerror = true; } } @@ -628,23 +364,21 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req) { _intype = READOUT_TYPE_PREVALUE; } - - if (_type == "raw") + else if (_type == "raw") { _intype = READOUT_TYPE_RAWVALUE; } - - if (_type == "error") + else if (_type == "error") { _intype = READOUT_TYPE_ERROR; } - zw = flowctrl.getReadoutAll(_intype); - ESP_LOGD(TAG, "ZW: %s", zw.c_str()); + std::string temp_value = flowctrl.getReadoutAll(_intype); + ESP_LOGD(TAG, "temp_value: %s", temp_value.c_str()); - if (zw.length() > 0) + if (temp_value.length() > 0) { - httpd_resp_send(req, zw.c_str(), zw.length()); + httpd_resp_send(req, temp_value.c_str(), temp_value.length()); } return ESP_OK; @@ -652,7 +386,7 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req) std::string *status = flowctrl.getActStatus(); std::string query = std::string(_query); - // ESP_LOGD(TAG, "Query: %s, query.c_str()); + // ESP_LOGD(TAG, "Query: %s, query.c_str()); if (query.find("full") != std::string::npos) { @@ -672,16 +406,16 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req) httpd_resp_sendstr_chunk(req, txt.c_str()); } - zw = flowctrl.getReadout(_rawValue, _noerror, 0); + std::string temp_value = flowctrl.getReadout(_rawValue, _noerror, 0); - if (zw.length() > 0) + if (temp_value.length() > 0) { - httpd_resp_sendstr_chunk(req, zw.c_str()); + httpd_resp_sendstr_chunk(req, temp_value.c_str()); } if (query.find("full") != std::string::npos) { - std::string txt, zw; + std::string output_string, temp_string; if ((countRounds <= 1) && (*status != std::string("Flow finished"))) { @@ -691,94 +425,93 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req) else { /* Digit ROIs */ - txt = ""; - txt += "


Recognized Digit ROIs (previous round)

\n"; - txt += "\n"; + output_string = ""; + output_string += "

Recognized Digit ROIs (previous round)

\n"; + output_string += "
\n"; - std::vector htmlinfodig; - htmlinfodig = flowctrl.GetAllDigit(); + std::vector htmlinfo_dig; + htmlinfo_dig = flowctrl.GetAllDigit(); - for (int i = 0; i < htmlinfodig.size(); ++i) + for (int i = 0; i < htmlinfo_dig.size(); ++i) { if (flowctrl.GetTypeDigit() == Digit) { // Numbers greater than 10 and less than 0 indicate NaN, since a Roi can only have values ​​from 0 to 9. - if ((htmlinfodig[i]->val >= 10) || (htmlinfodig[i]->val < 0)) + if ((htmlinfo_dig[i]->val >= 10.0) || (htmlinfo_dig[i]->val < 0.0)) { - zw = "NaN"; + temp_string = "NaN"; } else { - zw = std::to_string((int)htmlinfodig[i]->val); + temp_string = std::to_string((int)htmlinfo_dig[i]->val); } - txt += "\n"; + output_string += "\n"; } else { std::stringstream stream; - stream << std::fixed << std::setprecision(1) << htmlinfodig[i]->val; - zw = stream.str(); + stream << std::fixed << std::setprecision(2) << htmlinfo_dig[i]->val; + temp_string = stream.str(); // Numbers greater than 10 and less than 0 indicate NaN, since a Roi can only have values ​​from 0 to 9. - if ((std::stod(zw) >= 10) || (std::stod(zw) < 0)) + if ((std::stof(temp_string) >= 10.00) || (std::stof(temp_string) < 0.00)) { - zw = "NaN"; + temp_string = "NaN"; } - txt += "\n"; + output_string += "\n"; } - delete htmlinfodig[i]; + + delete htmlinfo_dig[i]; } - htmlinfodig.clear(); + htmlinfo_dig.clear(); - txt += "

" + zw + "

filename + "\">

" + temp_string + "

filename + "\">

" + zw + "

filename + "\">

" + temp_string + "

filename + "\">

\n"; - httpd_resp_sendstr_chunk(req, txt.c_str()); + output_string += "\n"; + httpd_resp_sendstr_chunk(req, output_string.c_str()); /* Analog ROIs */ - txt = "

Recognized Analog ROIs (previous round)

\n"; - txt += "\n"; + output_string = "

Recognized Analog ROIs (previous round)

\n"; + output_string += "
\n"; - std::vector htmlinfoana; - htmlinfoana = flowctrl.GetAllAnalog(); + std::vector htmlinfo_ana; + htmlinfo_ana = flowctrl.GetAllAnalog(); - for (int i = 0; i < htmlinfoana.size(); ++i) + for (int i = 0; i < htmlinfo_ana.size(); ++i) { std::stringstream stream; - stream << std::fixed << std::setprecision(1) << htmlinfoana[i]->val; - zw = stream.str(); - + stream << std::fixed << std::setprecision(2) << htmlinfo_ana[i]->val; + temp_string = stream.str(); + // Numbers greater than 10 and less than 0 indicate NaN, since a Roi can only have values ​​from 0 to 9. - if ((std::stod(zw) >= 10) || (std::stod(zw) < 0)) + if ((std::stof(temp_string) >= 10.00) || (std::stof(temp_string) < 0.00)) { - zw = "NaN"; + temp_string = "NaN"; } - txt += "\n"; - delete htmlinfoana[i]; + output_string += "\n"; + delete htmlinfo_ana[i]; } - htmlinfoana.clear(); + htmlinfo_ana.clear(); - txt += "\n

" + zw + "

filename + "\">

" + temp_string + "

filename + "\">

\n"; - httpd_resp_sendstr_chunk(req, txt.c_str()); + output_string += "\n\n"; + httpd_resp_sendstr_chunk(req, output_string.c_str()); /* Full Image * Only show it after the image got taken */ - txt = "

Full Image (current round)

\n"; + output_string = "

Full Image (current round)

\n"; - if ((*status == std::string("Initialization")) || - (*status == std::string("Initialization (delayed)")) || - (*status == std::string("Take Image"))) + if ((*status == std::string("Initialization")) || (*status == std::string("Initialization (delayed)")) || (*status == std::string("Take Image"))) { - txt += "

Current state: " + *status + "

\n"; + output_string += "

Current state: " + *status + "

\n"; } else { - txt += "\n"; + output_string += "\n"; } - httpd_resp_sendstr_chunk(req, txt.c_str()); + httpd_resp_sendstr_chunk(req, output_string.c_str()); } } @@ -791,19 +524,11 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req) return ESP_ERR_NOT_FOUND; } -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_wasserzaehler - Done"); -#endif - return ESP_OK; } esp_err_t handler_editflow(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_editflow - Start"); -#endif - ESP_LOGD(TAG, "handler_editflow uri: %s", req->uri); char _query[512]; @@ -814,9 +539,6 @@ esp_err_t handler_editflow(httpd_req_t *req) { if (httpd_query_key_value(_query, "task", _valuechar, 30) == ESP_OK) { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "task is found: %s", _valuechar); -#endif _task = std::string(_valuechar); } } @@ -827,13 +549,13 @@ esp_err_t handler_editflow(httpd_req_t *req) return get_numbers_file_handler(req); } - if (_task.compare("data") == 0) + else if (_task.compare("data") == 0) { ESP_LOGD(TAG, "Get data list"); return get_data_file_handler(req); } - if (_task.compare("tflite") == 0) + else if (_task.compare("tflite") == 0) { ESP_LOGD(TAG, "Get tflite list"); return get_tflite_file_handler(req); @@ -848,15 +570,10 @@ esp_err_t handler_editflow(httpd_req_t *req) httpd_query_key_value(_query, "out", _valuechar, 30); out = std::string(_valuechar); -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "in: %s", in.c_str()); - ESP_LOGD(TAG, "out: %s", out.c_str()); -#endif - in = "/sdcard" + in; out = "/sdcard" + out; - CopyFile(in, out); + copy_file(in, out); zw = "Copy Done"; httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); httpd_resp_send(req, zw.c_str(), zw.length()); @@ -876,41 +593,32 @@ esp_err_t handler_editflow(httpd_req_t *req) httpd_query_key_value(_query, "x", _valuechar, 30); std::string _x = std::string(_valuechar); - if (isStringNumeric(_x)) + if (is_string_numeric(_x)) { x = std::stoi(_x); } httpd_query_key_value(_query, "y", _valuechar, 30); std::string _y = std::string(_valuechar); - if (isStringNumeric(_y)) + if (is_string_numeric(_y)) { y = std::stoi(_y); } httpd_query_key_value(_query, "dx", _valuechar, 30); std::string _dx = std::string(_valuechar); - if (isStringNumeric(_dx)) + if (is_string_numeric(_dx)) { dx = std::stoi(_dx); } httpd_query_key_value(_query, "dy", _valuechar, 30); std::string _dy = std::string(_valuechar); - if (isStringNumeric(_dy)) + if (is_string_numeric(_dy)) { dy = std::stoi(_dy); } -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "in: %s", in.c_str()); - ESP_LOGD(TAG, "out: %s", out.c_str()); - ESP_LOGD(TAG, "x: %s", _x.c_str()); - ESP_LOGD(TAG, "y: %s", _y.c_str()); - ESP_LOGD(TAG, "dx: %s", _dx.c_str()); - ESP_LOGD(TAG, "dy: %s", _dy.c_str()); -#endif - if (httpd_query_key_value(_query, "enhance", _valuechar, 10) == ESP_OK) { string _enhance = std::string(_valuechar); @@ -926,7 +634,8 @@ esp_err_t handler_editflow(httpd_req_t *req) std::string out2 = out.substr(0, out.length() - 4) + "_org.jpg"; - if ((flowctrl.SetupModeActive || (*flowctrl.getActStatus() == std::string("Flow finished"))) && psram_init_shared_memory_for_take_image_step()) + // if ((flowctrl.SetupModeActive || (*flowctrl.getActStatus() == std::string("Flow finished"))) && psram_init_shared_memory_for_take_image_step()) + if ((flowctrl.SetupModeActive || (*flowctrl.getActStatus() != std::string("Take Image"))) && psram_init_shared_memory_for_take_image_step()) { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Taking image for Alignment Mark Update..."); @@ -960,14 +669,15 @@ esp_err_t handler_editflow(httpd_req_t *req) // wird beim Erstellen eines neuen Referenzbildes aufgerufen std::string *sys_status = flowctrl.getActStatus(); - if ((sys_status->c_str() != std::string("Take Image")) && (sys_status->c_str() != std::string("Aligning"))) + // if ((sys_status->c_str() != std::string("Take Image")) && (sys_status->c_str() != std::string("Aligning"))) + if (sys_status->c_str() != std::string("Take Image")) { if ((_task.compare("test_take") == 0) || (_task.compare("cam_settings") == 0)) { std::string _host = ""; // laden der aktuellen Kameraeinstellungen(CCstatus) in den Zwischenspeicher(CFstatus) - setCCstatusToCFstatus(); // CCstatus >>> CFstatus + Camera.set_camera_config_from_to(&CCstatus, &CFstatus); // CCstatus >>> CFstatus if (httpd_query_key_value(_query, "host", _valuechar, 30) == ESP_OK) { @@ -977,63 +687,80 @@ esp_err_t handler_editflow(httpd_req_t *req) if (httpd_query_key_value(_query, "waitb", _valuechar, 30) == ESP_OK) { std::string _waitb = std::string(_valuechar); - if (isStringNumeric(_waitb)) + if (is_string_numeric(_waitb)) { CFstatus.WaitBeforePicture = std::stoi(_valuechar); } } + if (httpd_query_key_value(_query, "xclk", _valuechar, sizeof(_valuechar)) == ESP_OK) + { + std::string temp_xclk = std::string(_valuechar); + if (is_string_numeric(temp_xclk)) + { + int temp_xclk_ = std::stoi(_valuechar); + CFstatus.CamXclkFreqMhz = clip_int(temp_xclk_, 20, 1); + } + } + if (httpd_query_key_value(_query, "aecgc", _valuechar, 30) == ESP_OK) { std::string _aecgc = std::string(_valuechar); - if (isStringNumeric(_aecgc)) + if (is_string_numeric(_aecgc)) { int _aecgc_ = std::stoi(_valuechar); switch (_aecgc_) { - case 1: - CFstatus.ImageGainceiling = GAINCEILING_4X; - break; - case 2: - CFstatus.ImageGainceiling = GAINCEILING_8X; - break; - case 3: - CFstatus.ImageGainceiling = GAINCEILING_16X; - break; - case 4: - CFstatus.ImageGainceiling = GAINCEILING_32X; - break; - case 5: - CFstatus.ImageGainceiling = GAINCEILING_64X; - break; - case 6: - CFstatus.ImageGainceiling = GAINCEILING_128X; - break; - default: - CFstatus.ImageGainceiling = GAINCEILING_2X; + case 1: + CFstatus.ImageGainceiling = GAINCEILING_4X; + break; + case 2: + CFstatus.ImageGainceiling = GAINCEILING_8X; + break; + case 3: + CFstatus.ImageGainceiling = GAINCEILING_16X; + break; + case 4: + CFstatus.ImageGainceiling = GAINCEILING_32X; + break; + case 5: + CFstatus.ImageGainceiling = GAINCEILING_64X; + break; + case 6: + CFstatus.ImageGainceiling = GAINCEILING_128X; + break; + default: + CFstatus.ImageGainceiling = GAINCEILING_2X; } } else { - if (_aecgc == "X4") { + if (_aecgc == "X4") + { CFstatus.ImageGainceiling = GAINCEILING_4X; } - else if (_aecgc == "X8") { + else if (_aecgc == "X8") + { CFstatus.ImageGainceiling = GAINCEILING_8X; } - else if (_aecgc == "X16") { + else if (_aecgc == "X16") + { CFstatus.ImageGainceiling = GAINCEILING_16X; } - else if (_aecgc == "X32") { + else if (_aecgc == "X32") + { CFstatus.ImageGainceiling = GAINCEILING_32X; } - else if (_aecgc == "X64") { + else if (_aecgc == "X64") + { CFstatus.ImageGainceiling = GAINCEILING_64X; } - else if (_aecgc == "X128") { + else if (_aecgc == "X128") + { CFstatus.ImageGainceiling = GAINCEILING_128X; } - else { + else + { CFstatus.ImageGainceiling = GAINCEILING_2X; } } @@ -1042,56 +769,56 @@ esp_err_t handler_editflow(httpd_req_t *req) if (httpd_query_key_value(_query, "qual", _valuechar, 30) == ESP_OK) { std::string _qual = std::string(_valuechar); - if (isStringNumeric(_qual)) + if (is_string_numeric(_qual)) { int _qual_ = std::stoi(_valuechar); - CFstatus.ImageQuality = clipInt(_qual_, 63, 6); + CFstatus.ImageQuality = clip_int(_qual_, 63, 6); } } if (httpd_query_key_value(_query, "bri", _valuechar, 30) == ESP_OK) { std::string _bri = std::string(_valuechar); - if (isStringNumeric(_bri)) + if (is_string_numeric(_bri)) { int _bri_ = std::stoi(_valuechar); - CFstatus.ImageBrightness = clipInt(_bri_, 2, -2); + CFstatus.ImageBrightness = clip_int(_bri_, 2, -2); } } if (httpd_query_key_value(_query, "con", _valuechar, 30) == ESP_OK) { std::string _con = std::string(_valuechar); - if (isStringNumeric(_con)) + if (is_string_numeric(_con)) { int _con_ = std::stoi(_valuechar); - CFstatus.ImageContrast = clipInt(_con_, 2, -2); + CFstatus.ImageContrast = clip_int(_con_, 2, -2); } } if (httpd_query_key_value(_query, "sat", _valuechar, 30) == ESP_OK) { std::string _sat = std::string(_valuechar); - if (isStringNumeric(_sat)) + if (is_string_numeric(_sat)) { int _sat_ = std::stoi(_valuechar); - CFstatus.ImageSaturation = clipInt(_sat_, 2, -2); + CFstatus.ImageSaturation = clip_int(_sat_, 2, -2); } } if (httpd_query_key_value(_query, "shp", _valuechar, 30) == ESP_OK) { std::string _shp = std::string(_valuechar); - if (isStringNumeric(_shp)) + if (is_string_numeric(_shp)) { int _shp_ = std::stoi(_valuechar); - if (CCstatus.CamSensor_id == OV2640_PID) + if (Camera.CamSensorId == OV2640_PID) { - CFstatus.ImageSharpness = clipInt(_shp_, 2, -2); + CFstatus.ImageSharpness = clip_int(_shp_, 2, -2); } else { - CFstatus.ImageSharpness = clipInt(_shp_, 3, -3); + CFstatus.ImageSharpness = clip_int(_shp_, 3, -3); } } } @@ -1099,38 +826,45 @@ esp_err_t handler_editflow(httpd_req_t *req) if (httpd_query_key_value(_query, "ashp", _valuechar, 30) == ESP_OK) { std::string _ashp = std::string(_valuechar); - CFstatus.ImageAutoSharpness = alphanumericToBoolean(_ashp); + CFstatus.ImageAutoSharpness = alphanumeric_to_boolean(_ashp); } if (httpd_query_key_value(_query, "spe", _valuechar, 30) == ESP_OK) { std::string _spe = std::string(_valuechar); - if (isStringNumeric(_spe)) + if (is_string_numeric(_spe)) { int _spe_ = std::stoi(_valuechar); - CFstatus.ImageSpecialEffect = clipInt(_spe_, 6, 0); + CFstatus.ImageSpecialEffect = clip_int(_spe_, 6, 0); } else { - if (_spe == "negative") { + if (_spe == "negative") + { CFstatus.ImageSpecialEffect = 1; } - else if (_spe == "grayscale") { + else if (_spe == "grayscale") + { CFstatus.ImageSpecialEffect = 2; } - else if (_spe == "red") { + else if (_spe == "red") + { CFstatus.ImageSpecialEffect = 3; } - else if (_spe == "green") { + else if (_spe == "green") + { CFstatus.ImageSpecialEffect = 4; } - else if (_spe == "blue") { + else if (_spe == "blue") + { CFstatus.ImageSpecialEffect = 5; } - else if (_spe == "retro") { + else if (_spe == "retro") + { CFstatus.ImageSpecialEffect = 6; } - else { + else + { CFstatus.ImageSpecialEffect = 0; } } @@ -1139,26 +873,31 @@ esp_err_t handler_editflow(httpd_req_t *req) if (httpd_query_key_value(_query, "wbm", _valuechar, 30) == ESP_OK) { std::string _wbm = std::string(_valuechar); - if (isStringNumeric(_wbm)) + if (is_string_numeric(_wbm)) { int _wbm_ = std::stoi(_valuechar); - CFstatus.ImageWbMode = clipInt(_wbm_, 4, 0); + CFstatus.ImageWbMode = clip_int(_wbm_, 4, 0); } else { - if (_wbm == "sunny") { + if (_wbm == "sunny") + { CFstatus.ImageWbMode = 1; } - else if (_wbm == "cloudy") { + else if (_wbm == "cloudy") + { CFstatus.ImageWbMode = 2; } - else if (_wbm == "office") { + else if (_wbm == "office") + { CFstatus.ImageWbMode = 3; } - else if (_wbm == "home") { + else if (_wbm == "home") + { CFstatus.ImageWbMode = 4; } - else { + else + { CFstatus.ImageWbMode = 0; } } @@ -1167,40 +906,40 @@ esp_err_t handler_editflow(httpd_req_t *req) if (httpd_query_key_value(_query, "awb", _valuechar, 30) == ESP_OK) { std::string _awb = std::string(_valuechar); - CFstatus.ImageAwb = alphanumericToBoolean(_awb); + CFstatus.ImageAwb = alphanumeric_to_boolean(_awb); } if (httpd_query_key_value(_query, "awbg", _valuechar, 30) == ESP_OK) { std::string _awbg = std::string(_valuechar); - CFstatus.ImageAwbGain = alphanumericToBoolean(_awbg); + CFstatus.ImageAwbGain = alphanumeric_to_boolean(_awbg); } if (httpd_query_key_value(_query, "aec", _valuechar, 30) == ESP_OK) { std::string _aec = std::string(_valuechar); - CFstatus.ImageAec = alphanumericToBoolean(_aec); + CFstatus.ImageAec = alphanumeric_to_boolean(_aec); } if (httpd_query_key_value(_query, "aec2", _valuechar, 30) == ESP_OK) { std::string _aec2 = std::string(_valuechar); - CFstatus.ImageAec2 = alphanumericToBoolean(_aec2); + CFstatus.ImageAec2 = alphanumeric_to_boolean(_aec2); } if (httpd_query_key_value(_query, "ael", _valuechar, 30) == ESP_OK) { std::string _ael = std::string(_valuechar); - if (isStringNumeric(_ael)) + if (is_string_numeric(_ael)) { int _ael_ = std::stoi(_valuechar); - if (CCstatus.CamSensor_id == OV2640_PID) + if (Camera.CamSensorId == OV2640_PID) { - CFstatus.ImageAeLevel = clipInt(_ael_, 2, -2); + CFstatus.ImageAeLevel = clip_int(_ael_, 2, -2); } else { - CFstatus.ImageAeLevel = clipInt(_ael_, 5, -5); + CFstatus.ImageAeLevel = clip_int(_ael_, 5, -5); } } } @@ -1208,84 +947,84 @@ esp_err_t handler_editflow(httpd_req_t *req) if (httpd_query_key_value(_query, "aecv", _valuechar, 30) == ESP_OK) { std::string _aecv = std::string(_valuechar); - if (isStringNumeric(_aecv)) + if (is_string_numeric(_aecv)) { int _aecv_ = std::stoi(_valuechar); - CFstatus.ImageAecValue = clipInt(_aecv_, 1200, 0); + CFstatus.ImageAecValue = clip_int(_aecv_, 1200, 0); } } if (httpd_query_key_value(_query, "agc", _valuechar, 30) == ESP_OK) { std::string _agc = std::string(_valuechar); - CFstatus.ImageAgc = alphanumericToBoolean(_agc); + CFstatus.ImageAgc = alphanumeric_to_boolean(_agc); } if (httpd_query_key_value(_query, "agcg", _valuechar, 30) == ESP_OK) { std::string _agcg = std::string(_valuechar); - if (isStringNumeric(_agcg)) + if (is_string_numeric(_agcg)) { int _agcg_ = std::stoi(_valuechar); - CFstatus.ImageAgcGain = clipInt(_agcg_, 30, 0); + CFstatus.ImageAgcGain = clip_int(_agcg_, 30, 0); } } if (httpd_query_key_value(_query, "bpc", _valuechar, 30) == ESP_OK) { std::string _bpc = std::string(_valuechar); - CFstatus.ImageBpc = alphanumericToBoolean(_bpc); + CFstatus.ImageBpc = alphanumeric_to_boolean(_bpc); } if (httpd_query_key_value(_query, "wpc", _valuechar, 30) == ESP_OK) { std::string _wpc = std::string(_valuechar); - CFstatus.ImageWpc = alphanumericToBoolean(_wpc); + CFstatus.ImageWpc = alphanumeric_to_boolean(_wpc); } if (httpd_query_key_value(_query, "rgma", _valuechar, 30) == ESP_OK) { std::string _rgma = std::string(_valuechar); - CFstatus.ImageRawGma = alphanumericToBoolean(_rgma); + CFstatus.ImageRawGma = alphanumeric_to_boolean(_rgma); } if (httpd_query_key_value(_query, "lenc", _valuechar, 30) == ESP_OK) { std::string _lenc = std::string(_valuechar); - CFstatus.ImageLenc = alphanumericToBoolean(_lenc); + CFstatus.ImageLenc = alphanumeric_to_boolean(_lenc); } if (httpd_query_key_value(_query, "mirror", _valuechar, 30) == ESP_OK) { std::string _mirror = std::string(_valuechar); - CFstatus.ImageHmirror = alphanumericToBoolean(_mirror); + CFstatus.ImageHmirror = alphanumeric_to_boolean(_mirror); } if (httpd_query_key_value(_query, "flip", _valuechar, 30) == ESP_OK) { std::string _flip = std::string(_valuechar); - CFstatus.ImageVflip = alphanumericToBoolean(_flip); + CFstatus.ImageVflip = alphanumeric_to_boolean(_flip); } if (httpd_query_key_value(_query, "dcw", _valuechar, 30) == ESP_OK) { std::string _dcw = std::string(_valuechar); - CFstatus.ImageDcw = alphanumericToBoolean(_dcw); + CFstatus.ImageDcw = alphanumeric_to_boolean(_dcw); } if (httpd_query_key_value(_query, "den", _valuechar, 30) == ESP_OK) { std::string _idlv = std::string(_valuechar); - if (isStringNumeric(_idlv)) + if (is_string_numeric(_idlv)) { int _ImageDenoiseLevel = std::stoi(_valuechar); - if (CCstatus.CamSensor_id == OV2640_PID) + if (Camera.CamSensorId == OV2640_PID) { CFstatus.ImageDenoiseLevel = 0; } else { - CFstatus.ImageDenoiseLevel = clipInt(_ImageDenoiseLevel, 8, 0); + CFstatus.ImageDenoiseLevel = clip_int(_ImageDenoiseLevel, 8, 0); } } } @@ -1293,26 +1032,26 @@ esp_err_t handler_editflow(httpd_req_t *req) if (httpd_query_key_value(_query, "zoom", _valuechar, 30) == ESP_OK) { std::string _zoom = std::string(_valuechar); - CFstatus.ImageZoomEnabled = alphanumericToBoolean(_zoom); + CFstatus.ImageZoomEnabled = alphanumeric_to_boolean(_zoom); } if (httpd_query_key_value(_query, "zoomx", _valuechar, 30) == ESP_OK) { std::string _zoomx = std::string(_valuechar); - if (isStringNumeric(_zoomx)) + if (is_string_numeric(_zoomx)) { int _ImageZoomOffsetX = std::stoi(_valuechar); - if (CCstatus.CamSensor_id == OV2640_PID) + if (Camera.CamSensorId == OV2640_PID) { - CFstatus.ImageZoomOffsetX = clipInt(_ImageZoomOffsetX, 480, -480); + CFstatus.ImageZoomOffsetX = clip_int(_ImageZoomOffsetX, 480, -480); } - else if (CCstatus.CamSensor_id == OV3660_PID) + else if (Camera.CamSensorId == OV3660_PID) { - CFstatus.ImageZoomOffsetX = clipInt(_ImageZoomOffsetX, 704, -704); + CFstatus.ImageZoomOffsetX = clip_int(_ImageZoomOffsetX, 704, -704); } - else if (CCstatus.CamSensor_id == OV5640_PID) + else if (Camera.CamSensorId == OV5640_PID) { - CFstatus.ImageZoomOffsetX = clipInt(_ImageZoomOffsetX, 960, -960); + CFstatus.ImageZoomOffsetX = clip_int(_ImageZoomOffsetX, 960, -960); } } } @@ -1320,20 +1059,20 @@ esp_err_t handler_editflow(httpd_req_t *req) if (httpd_query_key_value(_query, "zoomy", _valuechar, 30) == ESP_OK) { std::string _zoomy = std::string(_valuechar); - if (isStringNumeric(_zoomy)) + if (is_string_numeric(_zoomy)) { int _ImageZoomOffsetY = std::stoi(_valuechar); - if (CCstatus.CamSensor_id == OV2640_PID) + if (Camera.CamSensorId == OV2640_PID) { - CFstatus.ImageZoomOffsetY = clipInt(_ImageZoomOffsetY, 360, -360); + CFstatus.ImageZoomOffsetY = clip_int(_ImageZoomOffsetY, 360, -360); } - else if (CCstatus.CamSensor_id == OV3660_PID) + else if (Camera.CamSensorId == OV3660_PID) { - CFstatus.ImageZoomOffsetY = clipInt(_ImageZoomOffsetY, 528, -528); + CFstatus.ImageZoomOffsetY = clip_int(_ImageZoomOffsetY, 528, -528); } - else if (CCstatus.CamSensor_id == OV5640_PID) + else if (Camera.CamSensorId == OV5640_PID) { - CFstatus.ImageZoomOffsetY = clipInt(_ImageZoomOffsetY, 720, -720); + CFstatus.ImageZoomOffsetY = clip_int(_ImageZoomOffsetY, 720, -720); } } } @@ -1341,20 +1080,20 @@ esp_err_t handler_editflow(httpd_req_t *req) if (httpd_query_key_value(_query, "zooms", _valuechar, 30) == ESP_OK) { std::string _zooms = std::string(_valuechar); - if (isStringNumeric(_zooms)) + if (is_string_numeric(_zooms)) { int _ImageZoomSize = std::stoi(_valuechar); - if (CCstatus.CamSensor_id == OV2640_PID) + if (Camera.CamSensorId == OV2640_PID) { - CFstatus.ImageZoomSize = clipInt(_ImageZoomSize, 29, 0); + CFstatus.ImageZoomSize = clip_int(_ImageZoomSize, 29, 0); } - else if (CCstatus.CamSensor_id == OV3660_PID) + else if (Camera.CamSensorId == OV3660_PID) { - CFstatus.ImageZoomSize = clipInt(_ImageZoomSize, 43, 0); + CFstatus.ImageZoomSize = clip_int(_ImageZoomSize, 43, 0); } - else if (CCstatus.CamSensor_id == OV5640_PID) + else if (Camera.CamSensorId == OV5640_PID) { - CFstatus.ImageZoomSize = clipInt(_ImageZoomSize, 59, 0); + CFstatus.ImageZoomSize = clip_int(_ImageZoomSize, 59, 0); } } } @@ -1362,20 +1101,21 @@ esp_err_t handler_editflow(httpd_req_t *req) if (httpd_query_key_value(_query, "ledi", _valuechar, 30) == ESP_OK) { std::string _ledi = std::string(_valuechar); - if (isStringNumeric(_ledi)) + if (is_string_numeric(_ledi)) { int _ImageLedIntensity = std::stoi(_valuechar); - CFstatus.ImageLedIntensity = Camera.SetLEDIntensity(_ImageLedIntensity); + CFstatus.ImageLedIntensity = Camera.set_led_intensity(_ImageLedIntensity); } } if (_task.compare("cam_settings") == 0) { // wird aufgerufen, wenn das Referenzbild + Kameraeinstellungen gespeichert wurden - setCFstatusToCCstatus(); // CFstatus >>> CCstatus + Camera.set_camera_config_from_to(&CFstatus, &CCstatus); // CFstatus >>> CCstatus // Kameraeinstellungen wurden verädert - CFstatus.changedCameraSettings = true; + Camera.changedCameraSettings = true; + Camera.CamTempImage = false; ESP_LOGD(TAG, "Cam Settings set"); std::string _zw = "CamSettingsSet"; @@ -1385,14 +1125,9 @@ esp_err_t handler_editflow(httpd_req_t *req) else { // wird aufgerufen, wenn ein neues Referenzbild erstellt oder aktualisiert wurde - // CFstatus >>> Kamera - setCFstatusToCam(); - - Camera.SetQualityZoomSize(CFstatus.ImageQuality, CFstatus.ImageFrameSize, CFstatus.ImageZoomEnabled, CFstatus.ImageZoomOffsetX, CFstatus.ImageZoomOffsetY, CFstatus.ImageZoomSize, CFstatus.ImageVflip); - // Camera.SetZoomSize(CFstatus.ImageZoomEnabled, CFstatus.ImageZoomOffsetX, CFstatus.ImageZoomOffsetY, CFstatus.ImageZoomSize, CFstatus.ImageVflip); - // Kameraeinstellungen wurden verädert - CFstatus.changedCameraSettings = true; + Camera.changedCameraSettings = true; + Camera.CamTempImage = true; ESP_LOGD(TAG, "test_take - vor TakeImage"); std::string image_temp = flowctrl.doSingleStep("[TakeImage]", _host); @@ -1422,28 +1157,16 @@ esp_err_t handler_editflow(httpd_req_t *req) httpd_resp_send(req, _zw.c_str(), _zw.length()); } -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_editflow - Done"); -#endif - return ESP_OK; } esp_err_t handler_statusflow(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_statusflow - Start"); -#endif - const char *resp_str; httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); if (bTaskAutoFlowCreated) { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "handler_statusflow: %s", req->uri); -#endif - string *zw = flowctrl.getActStatusWithTime(); resp_str = zw->c_str(); @@ -1455,39 +1178,23 @@ esp_err_t handler_statusflow(httpd_req_t *req) httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); } -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_statusflow - Done"); -#endif - return ESP_OK; } esp_err_t handler_cputemp(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_cputemp - Start"); -#endif - httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - httpd_resp_send(req, std::to_string((int)temperatureRead()).c_str(), HTTPD_RESP_USE_STRLEN); - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_cputemp - End"); -#endif + httpd_resp_send(req, std::to_string((int)read_tempsensor()).c_str(), HTTPD_RESP_USE_STRLEN); return ESP_OK; } esp_err_t handler_rssi(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_rssi - Start"); -#endif - - if (getWIFIisConnected()) + if (get_wifi_sta_is_connected()) { httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - httpd_resp_send(req, std::to_string(get_WIFI_RSSI()).c_str(), HTTPD_RESP_USE_STRLEN); + httpd_resp_send(req, std::to_string(get_wifi_rssi()).c_str(), HTTPD_RESP_USE_STRLEN); } else { @@ -1495,19 +1202,11 @@ esp_err_t handler_rssi(httpd_req_t *req) return ESP_ERR_NOT_FOUND; } -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_rssi - End"); -#endif - return ESP_OK; } esp_err_t handler_current_date(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_uptime - Start"); -#endif - std::string formatedDateAndTime = getCurrentTimeString("%Y-%m-%d %H:%M:%S"); // std::string formatedDate = getCurrentTimeString("%Y-%m-%d"); @@ -1517,38 +1216,21 @@ esp_err_t handler_current_date(httpd_req_t *req) /* Respond with an empty chunk to signal HTTP response completion */ httpd_resp_sendstr_chunk(req, NULL); -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_uptime - End"); -#endif - return ESP_OK; } esp_err_t handler_uptime(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_uptime - Start"); -#endif - - std::string formatedUptime = getFormatedUptime(false); + std::string formatedUptime = get_formated_uptime(false); httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); httpd_resp_send(req, formatedUptime.c_str(), formatedUptime.length()); -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_uptime - End"); -#endif - return ESP_OK; } esp_err_t handler_prevalue(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_prevalue - Start"); - ESP_LOGD(TAG, "handler_prevalue: %s", req->uri); -#endif - // Default usage message when handler gets called without any parameter const std::string RESTUsageInfo = "00: Handler usage:
" @@ -1569,10 +1251,6 @@ esp_err_t handler_prevalue(httpd_req_t *req) if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Query: %s", _query); -#endif - if (httpd_query_key_value(_query, "numbers", _numbersname, 50) != ESP_OK) { // If request is incomplete @@ -1584,9 +1262,7 @@ esp_err_t handler_prevalue(httpd_req_t *req) if (httpd_query_key_value(_query, "value", _value, 20) == ESP_OK) { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Value: %s", _value); -#endif + ESP_LOGD(TAG, "handler_prevalue() - Value: %s", _value); } } else @@ -1633,10 +1309,6 @@ esp_err_t handler_prevalue(httpd_req_t *req) httpd_resp_send(req, sReturnMessage.c_str(), sReturnMessage.length()); -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_prevalue - End"); -#endif - return ESP_OK; } @@ -1649,13 +1321,10 @@ void task_autodoFlow(void *pvParameter) if (!isPlannedReboot && (esp_reset_reason() == ESP_RST_PANIC)) { flowctrl.setActStatus("Initialization (delayed)"); - // #ifdef ENABLE_MQTT - // MQTTPublish(mqttServer_getMainTopic() + "/" + "status", "Initialization (delayed)", false); // Right now, not possible -> MQTT Service is going to be started later - // #endif //ENABLE_MQTT vTaskDelay(60 * 5000 / portTICK_PERIOD_MS); // Wait 5 minutes to give time to do an OTA update or fetch the log } - ESP_LOGD(TAG, "task_autodoFlow: start"); + ESP_LOGD(TAG, "task_autodoFlow(): start"); doInit(); flowctrl.setAutoStartInterval(auto_interval); @@ -1663,26 +1332,23 @@ void task_autodoFlow(void *pvParameter) if (isSetupModusActive()) { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "We are in Setup Mode -> Not starting Auto Flow!"); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "task_autodoFlow(): We are in Setup Mode -> Not starting Auto Flow!"); autostartIsEnabled = false; - // 15.7.0 Setup Wizard cannot take a Reference Picture #2953 - // std::string zw_time = getCurrentTimeString(LOGFILE_TIME_FORMAT); - // flowctrl.doFlowTakeImageOnly(zw_time); } if (autostartIsEnabled) { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Starting Flow..."); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "task_autodoFlow(): Starting Flow..."); } else { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Autostart is not enabled -> Not starting Flow"); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "task_autodoFlow(): Autostart is not enabled -> Not starting Flow"); } while (autostartIsEnabled) { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "----------------------------------------------------------------"); // Clear separation between runs - time_t roundStartTime = getUpTime(); + time_t roundStartTime = get_uptime(); std::string _zw = "Round #" + std::to_string(++countRounds) + " started"; LogFile.WriteToFile(ESP_LOG_INFO, TAG, _zw); @@ -1691,56 +1357,54 @@ void task_autodoFlow(void *pvParameter) if (flowisrunning) { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Autoflow: doFlow is already running!"); -#endif + ESP_LOGD(TAG, "task_autodoFlow(): doFlow is already running!"); } else { -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Autoflow: doFlow is started"); -#endif + ESP_LOGD(TAG, "task_autodoFlow(): doFlow is started"); flowisrunning = true; doflow(); -#ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Remove older log files"); -#endif + + ESP_LOGD(TAG, "task_autodoFlow(): Remove older log files"); LogFile.RemoveOldLogFile(); LogFile.RemoveOldDataLog(); } // Round finished -> Logfile - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Round #" + std::to_string(countRounds) + " completed (" + std::to_string(getUpTime() - roundStartTime) + " seconds)"); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Round #" + std::to_string(countRounds) + " completed (" + std::to_string(get_uptime() - roundStartTime) + " seconds)"); // CPU Temp -> Logfile - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CPU Temperature: " + std::to_string((int)temperatureRead()) + "°C"); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CPU Temperature: " + std::to_string((int)read_tempsensor()) + "°C"); - // WIFI Signal Strength (RSSI) -> Logfile - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "WIFI Signal (RSSI): " + std::to_string(get_WIFI_RSSI()) + "dBm"); - - // Check if time is synchronized (if NTP is configured) - if (getUseNtp() && !getTimeIsSet()) + if (network_config.connection_type != NETWORK_CONNECTION_DISCONNECT) { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Time server is configured, but time is not yet set!"); - StatusLED(TIME_CHECK, 1, false); - } + // WIFI Signal Strength (RSSI) -> Logfile + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "WIFI Signal (RSSI): " + std::to_string(get_wifi_rssi()) + "dBm"); + + // Check if time is synchronized (if NTP is configured) + if (getUseNtp() && !getTimeIsSet()) + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "task_autodoFlow(): Time server is configured, but time is not yet set!"); + set_status_led(TIME_CHECK, 1, false); + } #if (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES) - wifiRoamingQuery(); + wifiRoamingQuery(); #endif // Scan channels and check if an AP with better RSSI is available, then disconnect and try to reconnect to AP with better RSSI // NOTE: Keep this direct before the following task delay, because scan is done in blocking mode and this takes ca. 1,5 - 2s. #ifdef WLAN_USE_ROAMING_BY_SCANNING - wifiRoamByScanning(); + wifi_roaming_by_scanning(); #endif + } fr_delta_ms = (esp_timer_get_time() - fr_start) / 1000; if (auto_interval > fr_delta_ms) { const TickType_t xDelay = (auto_interval - fr_delta_ms) / portTICK_PERIOD_MS; - ESP_LOGD(TAG, "Autoflow: sleep for: %ldms", (long)xDelay); + ESP_LOGD(TAG, "task_autodoFlow(): sleep for: %ldms", (long)xDelay); vTaskDelay(xDelay); } } @@ -1754,14 +1418,14 @@ void task_autodoFlow(void *pvParameter) vTaskDelete(NULL); // Delete this task if it exits from the loop above xHandletask_autodoFlow = NULL; - ESP_LOGD(TAG, "task_autodoFlow: end"); + ESP_LOGD(TAG, "task_autodoFlow(): end"); } void InitializeFlowTask(void) { BaseType_t xReturned; - ESP_LOGD(TAG, "getESPHeapInfo: %s", getESPHeapInfo().c_str()); + ESP_LOGD(TAG, "getESPHeapInfo: %s", get_heapinfo().c_str()); uint32_t stackSize = 16 * 1024; xReturned = xTaskCreatePinnedToCore(&task_autodoFlow, "task_autodoFlow", stackSize, NULL, tskIDLE_PRIORITY + 2, &xHandletask_autodoFlow, 0); @@ -1772,10 +1436,10 @@ void InitializeFlowTask(void) LogFile.WriteHeapInfo("Creation task_autodoFlow failed"); } - ESP_LOGD(TAG, "getESPHeapInfo: %s", getESPHeapInfo().c_str()); + ESP_LOGD(TAG, "getESPHeapInfo: %s", get_heapinfo().c_str()); } -void register_server_main_flow_task_uri(httpd_handle_t server) +void main_flow_register_uri(httpd_handle_t server) { ESP_LOGI(TAG, "server_main_flow_task - Registering URI handlers"); @@ -1852,18 +1516,18 @@ void register_server_main_flow_task_uri(httpd_handle_t server) // Legacy API => New: "/value" camuri.uri = "/value.html"; - camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_wasserzaehler); + camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_value); camuri.user_ctx = (void *)"Value"; httpd_register_uri_handler(server, &camuri); camuri.uri = "/value"; - camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_wasserzaehler); + camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_value); camuri.user_ctx = (void *)"Value"; httpd_register_uri_handler(server, &camuri); // Legacy API => New: "/value" camuri.uri = "/wasserzaehler.html"; - camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_wasserzaehler); + camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_value); camuri.user_ctx = (void *)"Wasserzaehler"; httpd_register_uri_handler(server, &camuri); diff --git a/code/components/jomjol_flowcontroll/MainFlowControl.h b/code/components/jomjol_flowcontroll/MainFlowControl.h index a2c2047e..5931212c 100644 --- a/code/components/jomjol_flowcontroll/MainFlowControl.h +++ b/code/components/jomjol_flowcontroll/MainFlowControl.h @@ -3,75 +3,16 @@ #ifndef MAINFLOWCONTROL_H #define MAINFLOWCONTROL_H -#include #include - +#include #include + #include "CImageBasis.h" #include "ClassFlowControll.h" #include "openmetrics.h" -typedef struct -{ - uint16_t CamSensor_id; - - framesize_t ImageFrameSize = FRAMESIZE_VGA; // 0 - 10 - gainceiling_t ImageGainceiling; // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128) - - int ImageQuality; // 0 - 63 - int ImageBrightness; // (-2 to 2) - set brightness - int ImageContrast; //-2 - 2 - int ImageSaturation; //-2 - 2 - int ImageSharpness; //-2 - 2 - bool ImageAutoSharpness; - int ImageSpecialEffect; // 0 - 6 - int ImageWbMode; // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home) - int ImageAwb; // white balance enable (0 or 1) - int ImageAwbGain; // Auto White Balance enable (0 or 1) - int ImageAec; // auto exposure off (1 or 0) - int ImageAec2; // automatic exposure sensor (0 or 1) - int ImageAeLevel; // auto exposure levels (-2 to 2) - int ImageAecValue; // set exposure manually (0-1200) - int ImageAgc; // auto gain off (1 or 0) - int ImageAgcGain; // set gain manually (0 - 30) - int ImageBpc; // black pixel correction - int ImageWpc; // white pixel correction - int ImageRawGma; // (1 or 0) - int ImageLenc; // lens correction (1 or 0) - int ImageHmirror; // (0 or 1) flip horizontally - int ImageVflip; // Invert image (0 or 1) - int ImageDcw; // downsize enable (1 or 0) - - int ImageDenoiseLevel; // The OV2640 does not support it, OV3660 and OV5640 (0 to 8) - - int ImageWidth; - int ImageHeight; - - int ImageLedIntensity; - - bool ImageZoomEnabled; - int ImageZoomOffsetX; - int ImageZoomOffsetY; - int ImageZoomSize; - - int WaitBeforePicture; - bool isImageSize; - - bool CameraInitSuccessful; - bool changedCameraSettings; - bool DemoMode; - bool SaveAllFiles; -} camera_flow_config_temp_t; - -extern camera_flow_config_temp_t CFstatus; extern ClassFlowControll flowctrl; -esp_err_t setCCstatusToCFstatus(void); // CCstatus >>> CFstatus -esp_err_t setCFstatusToCCstatus(void); // CFstatus >>> CCstatus -esp_err_t setCFstatusToCam(void); // CFstatus >>> Kamera - -void register_server_main_flow_task_uri(httpd_handle_t server); - void CheckIsPlannedReboot(void); bool getIsPlannedReboot(void); @@ -81,11 +22,11 @@ bool isSetupModusActive(void); int getCountFlowRounds(void); -#ifdef ENABLE_MQTT esp_err_t MQTTCtrlFlowStart(std::string _topic); -#endif // ENABLE_MQTT esp_err_t GetRawJPG(httpd_req_t *req); esp_err_t GetJPG(std::string _filename, httpd_req_t *req); +void main_flow_register_uri(httpd_handle_t server); + #endif // MAINFLOWCONTROL_H diff --git a/code/components/jomjol_helper/CMakeLists.txt b/code/components/jomjol_helper/CMakeLists.txt index ba60b641..e9e413b2 100644 --- a/code/components/jomjol_helper/CMakeLists.txt +++ b/code/components/jomjol_helper/CMakeLists.txt @@ -1,7 +1,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." - REQUIRES esp_timer esp-tflite-micro jomjol_logfile fatfs sdmmc vfs) + INCLUDE_DIRS "." "../../include" + REQUIRES driver esp_driver_uart log esp_timer esp-tflite-micro jomjol_logfile fatfs sdmmc vfs) diff --git a/code/components/jomjol_helper/Helper.cpp b/code/components/jomjol_helper/Helper.cpp index c2315d97..d19b72c1 100644 --- a/code/components/jomjol_helper/Helper.cpp +++ b/code/components/jomjol_helper/Helper.cpp @@ -1,4 +1,4 @@ -// #pragma warning(disable : 4996) +#include "defines.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -12,41 +12,522 @@ #include #include #include - -#ifdef __cplusplus -extern "C" -{ -#endif #include -#ifdef __cplusplus -} -#endif - #include #include #include #include -#include "../../include/defines.h" #include "ClassLogFile.h" -#include "esp_vfs_fat.h" -#include "../sdmmc_common.h" +#include + +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)) +#include +#else +#include <../sdmmc_common.h> +#endif static const char *TAG = "HELPER"; using namespace std; unsigned int systemStatus = 0; +bool all_pw_were_encrypted = false; -sdmmc_cid_t SDCardCid; -sdmmc_csd_t SDCardCsd; -bool SDCardIsMMC; +sdmmc_cid_t sd_card_cid; +sdmmc_csd_t sd_card_csd; +bool is_sd_card_mmc; -// #define DEBUG_DETAIL_ON +#if CONFIG_SOC_TEMP_SENSOR_SUPPORTED +// The ESP32-S2/C3/S3/C2 has a built-in temperature sensor. +// The temperature sensor module contains an 8-bit Sigma-Delta ADC and a temperature offset DAC. +// https://github.com/espressif/esp-idf/blob/master/examples/peripherals/temperature_sensor/ +temperature_sensor_handle_t temp_handle = NULL; +temperature_sensor_config_t temp_sensor = { + .range_min = -10, + .range_max = 80, + .clk_src = TEMPERATURE_SENSOR_CLK_SRC_DEFAULT, +}; -///////////////////////////////////////////////////////////////////////////////////////////// -string getESPHeapInfo() +void init_tempsensor(void) +{ + ESP_ERROR_CHECK(temperature_sensor_install(&temp_sensor, &temp_handle)); + + xTaskCreate( + [](void *pvParameters) + { + while (1) + { + // Get converted sensor data + float tsens_out; + + // Enable temperature sensor + ESP_ERROR_CHECK(temperature_sensor_enable(temp_handle)); + ESP_ERROR_CHECK(temperature_sensor_get_celsius(temp_handle, &tsens_out)); + temp_sens_value = tsens_out; + // Disable the temperature sensor if it is not needed and save the power + ESP_ERROR_CHECK(temperature_sensor_disable(temp_handle)); + + vTaskDelay(pdMS_TO_TICKS(5000)); + } + }, + "tempsensor_task", 2048, NULL, 5, NULL); +} + +float read_tempsensor(void) +{ + return temp_sens_value; +} +#elif CONFIG_IDF_TARGET_ESP32 +extern "C" uint8_t temprature_sens_read(void); +float read_tempsensor(void) +{ + // convert Fahrenheit to Celsius (F-32) * (5/9) = degree Celsius + temp_sens_value = (temprature_sens_read() - 32) / 1.8; + return temp_sens_value; +} +#endif + +void string_to_ip4(const char *ip, int &a, int &b, int &c, int &d) +{ + std::string zw = std::string(ip); + std::stringstream s(zw); + char ch; // to temporarily store the '.' + s >> a >> ch >> b >> ch >> c >> ch >> d; +} + +std::string bssid_to_string(const char *c) +{ + char cBssid[25]; + sprintf(cBssid, "%02x:%02x:%02x:%02x:%02x:%02x", c[0], c[1], c[2], c[3], c[4], c[5]); + return std::string(cBssid); +} + +string to_upper(string in) +{ + for (int i = 0; i < in.length(); ++i) + { + in[i] = toupper(in[i]); + } + + return in; +} + +string to_lower(string in) +{ + for (int i = 0; i < in.length(); ++i) + { + in[i] = tolower(in[i]); + } + + return in; +} + +std::vector split_string(const std::string &str) +{ + std::vector tokens; + + std::stringstream ss(str); + std::string token; + + while (std::getline(ss, token, '\n')) + { + tokens.push_back(token); + } + + return tokens; +} + +std::vector split_line(std::string input, std::string _delimiter) +{ + std::vector Output; + + // wenn input nicht leer ist + if (input.length() > 1) + { + if ((to_upper(input).find("PASSWORD") != std::string::npos) || (input.find("SSID") != std::string::npos) || (to_upper(input).find("TOKEN") != std::string::npos) || (to_upper(input).find("APIKEY") != std::string::npos) || + (input.find("**##**") != std::string::npos)) + { + size_t pos1 = input.find(_delimiter); + size_t pos2 = input.find(" "); + + // wenn _delimiter im string gefunden wird + if (pos1 != std::string::npos) + { + Output.push_back(trim_string_left_right(input.substr(0, pos1), "")); + + // wenn der string einen Wert enthält + if ((input.size() - 1) > pos1) + { + // überprüfe die erste Stelle + std::string value = input.substr(pos1, std::string::npos); + value.erase(0, 1); + value = trim_string_left_right(value, ""); + + if ((value.substr(0, 1) == "\"") && (value.substr(value.size() - 1, std::string::npos) == "\"")) + { + value = value.substr(1, value.size() - 2); + } + + std::string is_pw_encrypted = value.substr(0, 6); + + if (is_pw_encrypted == "**##**") + { + Output.push_back(encrypt_decrypt_string(value.substr(6, std::string::npos))); + } + else + { + Output.push_back(value.substr(0, std::string::npos)); + } + } + else + { + Output.push_back(""); + } + } + // wenn Leerzeichen im string gefunden wird + else if (pos2 != std::string::npos) + { + Output.push_back(trim_string_left_right(input.substr(0, pos2), "")); + + // wenn der string einen Wert enthält + if ((input.size() - 1) > pos2) + { + // überprüfe die erste Stelle + std::string value = input.substr(pos2, std::string::npos); + value.erase(0, 1); + value = trim_string_left_right(value, ""); + + if ((value.substr(0, 1) == "\"") && (value.substr(value.size() - 1, std::string::npos) == "\"")) + { + value = value.substr(1, value.size() - 2); + } + + std::string is_pw_encrypted = value.substr(0, 6); + + if (is_pw_encrypted == "**##**") + { + Output.push_back(encrypt_decrypt_string(value.substr(6, std::string::npos))); + } + else + { + Output.push_back(value.substr(0, std::string::npos)); + } + } + else + { + Output.push_back(""); + } + } + else + { + Output.push_back(input); + } + } + else + { + // Legacy Mode + std::string token; + size_t pos1 = std::string::npos; + + if (find_delimiter_pos(input, _delimiter) != std::string::npos) + { + pos1 = find_delimiter_pos(input, _delimiter); + } + else + { + pos1 = find_delimiter_pos(input, " "); + } + + if (pos1 != std::string::npos) + { + Output.push_back(trim_string_left_right(input.substr(0, pos1), " ")); + + if ((input.size() - 1) > pos1) + { + // überprüfe die erste Stelle + std::string value = input.substr(pos1, std::string::npos); + value.erase(0, 1); + value = trim_string_left_right(value, " "); + + if (find_delimiter_pos(value, _delimiter) != std::string::npos) + { + pos1 = find_delimiter_pos(value, _delimiter); + } + else + { + pos1 = find_delimiter_pos(value, " "); + } + + if ((value.substr(0, 1) == "\"") && (value.substr(value.size() - 1, std::string::npos) == "\"")) + { + value = value.substr(1, value.size() - 2); + } + + while (pos1 != std::string::npos) + { + token = value.substr(0, pos1); + token = trim_string_left_right(token, " "); + if ((token.substr(0, 1) == "\"") && (token.substr(token.size() - 1, std::string::npos) == "\"")) + { + token = token.substr(1, token.size() - 2); + } + Output.push_back(token); + + value.erase(0, pos1 + 1); + value = trim_string_left_right(value, " "); + + if (find_delimiter_pos(value, _delimiter) != std::string::npos) + { + pos1 = find_delimiter_pos(value, _delimiter); + } + else + { + pos1 = find_delimiter_pos(value, " "); + } + } + + if ((value.substr(0, 1) == "\"") && (value.substr(value.size() - 1, std::string::npos) == "\"")) + { + value = value.substr(1, value.size() - 2); + } + Output.push_back(value); + } + else + { + Output.push_back(""); + } + } + else + { + Output.push_back(input); + } + } + } + else + { + Output.push_back(input); + } + + return Output; +} + +// Encrypt/Decrypt a string +std::string encrypt_decrypt_string(std::string toEncrypt) +{ + char key[3] = {'K', 'C', 'Q'}; // Any chars will work, in an array of any size + std::string output = toEncrypt; + + for (int i = 0; i < toEncrypt.size(); i++) + { + output[i] = toEncrypt[i] ^ key[i % (sizeof(key) / sizeof(char))]; + } + + return output; +} + +// Checks whether a password is decrypted +std::string encrypt_pw_string(std::string toEncrypt) +{ + std::string string_result = ""; + + if (is_in_string(toEncrypt, (std::string)STRING_ENCRYPTED_LABEL)) + { + string_result = toEncrypt; + all_pw_were_encrypted = true; + } + else + { + string_result = (std::string)STRING_ENCRYPTED_LABEL + encrypt_decrypt_string(toEncrypt); + all_pw_were_encrypted = false; + } + + return string_result; +} + +std::string decrypt_pw_string(std::string toDecrypt) +{ + std::string string_result = ""; + + if (is_in_string(toDecrypt, (std::string)STRING_ENCRYPTED_LABEL)) + { + replace_string(toDecrypt, (std::string)STRING_ENCRYPTED_LABEL, "", false); + string_result = encrypt_decrypt_string(toDecrypt); + all_pw_were_encrypted = true; + } + else + { + string_result = toDecrypt; + all_pw_were_encrypted = false; + } + + return string_result; +} + +// Checks if all passwords on the SD are encrypted and if they are not encrypted, it encrypts them. +esp_err_t encrypt_decrypt_pw_on_sd(bool _encrypt, std::string filename) +{ + std::string line = ""; + + std::vector splitted; + std::vector temp_file; + + all_pw_were_encrypted = false; + + std::string fn = format_filename(filename); + FILE *pFile = fopen(fn.c_str(), "r"); + + if (pFile == NULL) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "EncryptDecryptConfigPwOnSD: Unable to open file config.ini (read)"); + fclose(pFile); + return ESP_FAIL; + } + + ESP_LOGD(TAG, "EncryptDecryptConfigPwOnSD: config.ini opened"); + + char temp_line[256]; + + if (fgets(temp_line, sizeof(temp_line), pFile) == NULL) + { + line = ""; + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "EncryptDecryptConfigPwOnSD: File opened, but empty or content not readable"); + fclose(pFile); + return ESP_FAIL; + } + else + { + line = std::string(temp_line); + } + + if (_encrypt) + { + while ((line.size() > 0) || !(feof(pFile))) + { + splitted = split_line(line); + std::string _param = to_upper(splitted[0]); + + if (splitted.size() > 1) + { + if (filename == CONFIG_FILE) + { + if (_param == "PASSWORD") + { + line = "password = " + encrypt_pw_string(splitted[1]) + "\n"; + } + else if (_param == "TOKEN") + { + line = "Token = " + encrypt_pw_string(splitted[1]) + "\n"; + } + else if (_param == "APIKEY") + { + line = "apikey = " + encrypt_pw_string(splitted[1]) + "\n"; + } + } + else if (filename == NETWORK_CONFIG_FILE) + { + if (_param == "PASSWORD") + { + line = "password = \"" + encrypt_pw_string(splitted[1]) + "\"\n"; + } + else if (_param == "HTTP_PASSWORD") + { + line = "http_password = \"" + encrypt_pw_string(splitted[1]) + "\"\n"; + } + } + } + + temp_file.push_back(line); + + if (fgets(temp_line, sizeof(temp_line), pFile) == NULL) + { + line = ""; + } + else + { + line = std::string(temp_line); + } + } + } + else + { + while ((line.size() > 0) || !(feof(pFile))) + { + splitted = split_line(line); + std::string _param = to_upper(splitted[0]); + + if (splitted.size() > 1) + { + if (filename == CONFIG_FILE) + { + if (_param == "PASSWORD") + { + line = "password = " + decrypt_pw_string(splitted[1]) + "\n"; + } + else if (_param == "TOKEN") + { + line = "Token = " + decrypt_pw_string(splitted[1]) + "\n"; + } + else if (_param == "APIKEY") + { + line = "apikey = " + decrypt_pw_string(splitted[1]) + "\n"; + } + } + else if (filename == NETWORK_CONFIG_FILE) + { + if (_param == "PASSWORD") + { + line = "password = \"" + decrypt_pw_string(splitted[1]) + "\"\n"; + } + else if (_param == "HTTP_PASSWORD") + { + line = "http_password = \"" + decrypt_pw_string(splitted[1]) + "\"\n"; + } + } + } + + temp_file.push_back(line); + + if (fgets(temp_line, sizeof(temp_line), pFile) == NULL) + { + line = ""; + } + else + { + line = std::string(temp_line); + } + } + } + + fclose(pFile); + + // Only write to the SD if not all passwords are encrypted + if ((all_pw_were_encrypted == false && _encrypt == true) || (all_pw_were_encrypted == true && _encrypt == false)) + { + pFile = fopen(fn.c_str(), "w+"); + + if (pFile == NULL) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "EncryptDecryptConfigPwOnSD: Unable to open file config.ini (write)"); + fclose(pFile); + return ESP_FAIL; + } + + for (int i = 0; i < temp_file.size(); ++i) + { + fputs(temp_file[i].c_str(), pFile); + } + + fclose(pFile); + } + + ESP_LOGD(TAG, "EncryptDecryptConfigPwOnSD done"); + + return ESP_OK; +} + +string get_heapinfo() { string espInfoResultStr = ""; char aMsgBuf[80]; @@ -82,703 +563,18 @@ string getESPHeapInfo() return espInfoResultStr; } -size_t getESPHeapSize() +size_t get_heapsize() { return heap_caps_get_free_size(MALLOC_CAP_8BIT); } -size_t getInternalESPHeapSize() +size_t get_internal_heapsize() { return heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); } -string getSDCardPartitionSize() -{ - FATFS *fs; - uint32_t fre_clust, tot_sect; - - /* Get volume information and free clusters of drive 0 */ - f_getfree("0:", (DWORD *)&fre_clust, &fs); - tot_sect = ((fs->n_fatent - 2) * fs->csize) / 1024 / (1024 / SDCardCsd.sector_size); // corrected by SD Card sector size (usually 512 bytes) and convert to MB - - // ESP_LOGD(TAG, "%d MB total drive space (Sector size [bytes]: %d)", (int)tot_sect, (int)fs->ssize); - - return std::to_string(tot_sect); -} - -string getSDCardFreePartitionSpace() -{ - FATFS *fs; - uint32_t fre_clust, fre_sect; - - /* Get volume information and free clusters of drive 0 */ - f_getfree("0:", (DWORD *)&fre_clust, &fs); - fre_sect = (fre_clust * fs->csize) / 1024 / (1024 / SDCardCsd.sector_size); // corrected by SD Card sector size (usually 512 bytes) and convert to MB - - // ESP_LOGD(TAG, "%d MB free drive space (Sector size [bytes]: %d)", (int)fre_sect, (int)fs->ssize); - - return std::to_string(fre_sect); -} - -string getSDCardPartitionAllocationSize() -{ - FATFS *fs; - uint32_t fre_clust, allocation_size; - - /* Get volume information and free clusters of drive 0 */ - f_getfree("0:", (DWORD *)&fre_clust, &fs); - allocation_size = fs->ssize; - - // ESP_LOGD(TAG, "SD Card Partition Allocation Size: %d bytes", allocation_size); - - return std::to_string(allocation_size); -} - -void SaveSDCardInfo(sdmmc_card_t *card) -{ - SDCardCid = card->cid; - SDCardCsd = card->csd; - SDCardIsMMC = card->is_mmc; -} - -string getSDCardManufacturer() -{ - string SDCardManufacturer = SDCardParseManufacturerIDs(SDCardCid.mfg_id); - // ESP_LOGD(TAG, "SD Card Manufacturer: %s", SDCardManufacturer.c_str()); - - return (SDCardManufacturer + " (ID: " + std::to_string(SDCardCid.mfg_id) + ")"); -} - -string getSDCardName() -{ - char *SDCardName = SDCardCid.name; - // ESP_LOGD(TAG, "SD Card Name: %s", SDCardName); - - return std::string(SDCardName); -} - -string getSDCardCapacity() -{ - int SDCardCapacity = SDCardCsd.capacity / (1024 / SDCardCsd.sector_size) / 1024; // total sectors * sector size --> Byte to MB (1024*1024) - // ESP_LOGD(TAG, "SD Card Capacity: %s", std::to_string(SDCardCapacity).c_str()); - - return std::to_string(SDCardCapacity); -} - -string getSDCardSectorSize() -{ - int SDCardSectorSize = SDCardCsd.sector_size; - // ESP_LOGD(TAG, "SD Card Sector Size: %s bytes", std::to_string(SDCardSectorSize).c_str()); - - return std::to_string(SDCardSectorSize); -} - /////////////////////////////////////////////////////////////////////////////////////////////// -void memCopyGen(uint8_t *_source, uint8_t *_target, int _size) -{ - for (int i = 0; i < _size; ++i) - { - *(_target + i) = *(_source + i); - } -} - -std::string FormatFileName(std::string input) -{ -#ifdef ISWINDOWS_TRUE - input.erase(0, 1); - std::string os = "/"; - std::string ns = "\\"; - FindReplace(input, os, ns); -#endif - return input; -} - -std::size_t file_size(const std::string &file_name) -{ - std::ifstream file(file_name.c_str(), std::ios::in | std::ios::binary); - - if (!file) - { - return 0; - } - - file.seekg(0, std::ios::end); - return static_cast(file.tellg()); -} - -void FindReplace(std::string &line, std::string &oldString, std::string &newString) -{ - const size_t oldSize = oldString.length(); - - // do nothing if line is shorter than the string to find - if (oldSize > line.length()) - { - return; - } - - const size_t newSize = newString.length(); - - for (size_t pos = 0;; pos += newSize) - { - // Locate the substring to replace - pos = line.find(oldString, pos); - - if (pos == std::string::npos) - { - return; - } - - if (oldSize == newSize) - { - // if they're same size, use std::string::replace - line.replace(pos, oldSize, newString); - } - else - { - // if not same size, replace by erasing and inserting - line.erase(pos, oldSize); - line.insert(pos, newString); - } - } -} - -/** - * Create a folder and its parent folders as needed - */ -bool MakeDir(std::string path) -{ - std::string parent; - - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Creating folder " + path + "..."); - - bool bSuccess = false; - int nRC = ::mkdir(path.c_str(), 0775); - - if (nRC == -1) - { - switch (errno) - { - case ENOENT: - // parent didn't exist, try to create it - parent = path.substr(0, path.find_last_of('/')); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Need to create parent folder first: " + parent); - - if (MakeDir(parent)) - { - // Now, try to create again. - bSuccess = 0 == ::mkdir(path.c_str(), 0775); - } - else - { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create parent folder: " + parent); - bSuccess = false; - } - break; - - case EEXIST: - // Done! - bSuccess = true; - break; - - default: - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create folder: " + path + " (errno: " + std::to_string(errno) + ")"); - bSuccess = false; - break; - } - } - else - { - bSuccess = true; - } - - return bSuccess; -} - -bool ctype_space(const char c, string adddelimiter) -{ - if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 11) - { - return true; - } - - if (adddelimiter.find(c) != string::npos) - { - return true; - } - - return false; -} - -string trim(string istring, string adddelimiter) -{ - bool trimmed = false; - - if (ctype_space(istring[istring.length() - 1], adddelimiter)) - { - istring.erase(istring.length() - 1); - trimmed = true; - } - - if (ctype_space(istring[0], adddelimiter)) - { - istring.erase(0, 1); - trimmed = true; - } - - if ((trimmed == false) || (istring.size() == 0)) - { - return istring; - } - else - { - return trim(istring, adddelimiter); - } -} - -size_t findDelimiterPos(string input, string delimiter) -{ - size_t pos = std::string::npos; - // size_t zw; - string akt_del; - - for (int anz = 0; anz < delimiter.length(); ++anz) - { - akt_del = delimiter[anz]; - size_t zw = input.find(akt_del); - - if (zw != std::string::npos) - { - if ((pos != std::string::npos) && (zw < pos)) - { - pos = zw; - } - else - { - pos = zw; - } - } - } - - return pos; -} - -bool RenameFile(string from, string to) -{ - // ESP_LOGI(logTag, "Renaming File: %s", from.c_str()); - FILE *fpSourceFile = fopen(from.c_str(), "rb"); - - // Sourcefile does not exist otherwise there is a mistake when renaming! - if (!fpSourceFile) - { - ESP_LOGE(TAG, "RenameFile: File %s does not exist!", from.c_str()); - return false; - } - - fclose(fpSourceFile); - rename(from.c_str(), to.c_str()); - - return true; -} - -bool RenameFolder(string from, string to) -{ - // ESP_LOGI(logTag, "Renaming Folder: %s", from.c_str()); - DIR *fpSourceFolder = opendir(from.c_str()); - - // Sourcefolder does not exist otherwise there is a mistake when renaming! - if (!fpSourceFolder) - { - ESP_LOGE(TAG, "RenameFolder: Folder %s does not exist!", from.c_str()); - return false; - } - - closedir(fpSourceFolder); - rename(from.c_str(), to.c_str()); - - return true; -} - -bool FileExists(string filename) -{ - FILE *fpSourceFile = fopen(filename.c_str(), "rb"); - - // Sourcefile does not exist - if (!fpSourceFile) - { - return false; - } - - fclose(fpSourceFile); - - return true; -} - -bool FolderExists(string foldername) -{ - DIR *fpSourceFolder = opendir(foldername.c_str()); - - // Sourcefolder does not exist - if (!fpSourceFolder) - { - return false; - } - - closedir(fpSourceFolder); - - return true; -} - -bool DeleteFile(string filename) -{ - // ESP_LOGI(logTag, "Deleting file: %s", filename.c_str()); - /* Delete file */ - FILE *fpSourceFile = fopen(filename.c_str(), "rb"); - - // Sourcefile does not exist otherwise there is a mistake in copying! - if (!fpSourceFile) - { - ESP_LOGD(TAG, "DeleteFile: File %s existiert nicht!", filename.c_str()); - return false; - } - - fclose(fpSourceFile); - unlink(filename.c_str()); - - return true; -} - -bool CopyFile(string input, string output) -{ - input = FormatFileName(input); - output = FormatFileName(output); - - if (toUpper(input).compare(WLAN_CONFIG_FILE) == 0) - { - ESP_LOGD(TAG, "wlan.ini kann nicht kopiert werden!"); - return false; - } - - char cTemp; - FILE *fpSourceFile = fopen(input.c_str(), "rb"); - - // Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch! - if (!fpSourceFile) - { - ESP_LOGD(TAG, "File %s existiert nicht!", input.c_str()); - return false; - } - - FILE *fpTargetFile = fopen(output.c_str(), "wb"); - - // Code Section - - // Read From The Source File - "Copy" - while (fread(&cTemp, 1, 1, fpSourceFile) == 1) - { - // Write To The Target File - "Paste" - fwrite(&cTemp, 1, 1, fpTargetFile); - } - - // Close The Files - fclose(fpSourceFile); - fclose(fpTargetFile); - ESP_LOGD(TAG, "File copied: %s to %s", input.c_str(), output.c_str()); - - return true; -} - -string getFileFullFileName(string filename) -{ - size_t lastpos = filename.find_last_of('/'); - - if (lastpos == string::npos) - { - return ""; - } - - // ESP_LOGD(TAG, "Last position: %d", lastpos); - - string zw = filename.substr(lastpos + 1, filename.size() - lastpos); - - return zw; -} - -string getDirectory(string filename) -{ - size_t lastpos = filename.find('/'); - - if (lastpos == string::npos) - { - lastpos = filename.find('\\'); - } - - if (lastpos == string::npos) - { - return ""; - } - - // ESP_LOGD(TAG, "Directory: %d", lastpos); - - string zw = filename.substr(0, lastpos - 1); - return zw; -} - -string getFileType(string filename) -{ - size_t lastpos = filename.rfind(".", filename.length()); - size_t neu_pos; - - while ((neu_pos = filename.find(".", lastpos + 1)) > -1) - { - lastpos = neu_pos; - } - - if (lastpos == string::npos) - { - return ""; - } - - string zw = filename.substr(lastpos + 1, filename.size() - lastpos); - zw = toUpper(zw); - - return zw; -} - -/* recursive mkdir */ -int mkdir_r(const char *dir, const mode_t mode) -{ - char tmp[FILE_PATH_MAX]; - char *p = NULL; - struct stat sb; - size_t len; - - /* copy path */ - len = strnlen(dir, FILE_PATH_MAX); - - if (len == 0 || len == FILE_PATH_MAX) - { - return -1; - } - - memcpy(tmp, dir, len); - tmp[len] = '\0'; - - /* remove trailing slash */ - if (tmp[len - 1] == '/') - { - tmp[len - 1] = '\0'; - } - - /* check if path exists and is a directory */ - if (stat(tmp, &sb) == 0) - { - if (S_ISDIR(sb.st_mode)) - { - return 0; - } - } - - /* recursive mkdir */ - for (p = tmp + 1; *p; p++) - { - if (*p == '/') - { - *p = 0; - - /* test path */ - if (stat(tmp, &sb) != 0) - { - /* path does not exist - create directory */ - if (mkdir(tmp, mode) < 0) - { - return -1; - } - } - else if (!S_ISDIR(sb.st_mode)) - { - /* not a directory */ - return -1; - } - - *p = '/'; - } - } - - /* test path */ - if (stat(tmp, &sb) != 0) - { - /* path does not exist - create directory */ - if (mkdir(tmp, mode) < 0) - { - return -1; - } - } - else if (!S_ISDIR(sb.st_mode)) - { - /* not a directory */ - return -1; - } - - return 0; -} - -string toUpper(string in) -{ - for (int i = 0; i < in.length(); ++i) - { - in[i] = toupper(in[i]); - } - - return in; -} - -string toLower(string in) -{ - for (int i = 0; i < in.length(); ++i) - { - in[i] = tolower(in[i]); - } - - return in; -} - -// CPU Temp -extern "C" uint8_t temprature_sens_read(); -float temperatureRead() -{ - return (temprature_sens_read() - 32) / 1.8; -} - -time_t addDays(time_t startTime, int days) -{ - struct tm *tm = localtime(&startTime); - tm->tm_mday += days; - return mktime(tm); -} - -int removeFolder(const char *folderPath, const char *logTag) -{ - // ESP_LOGD(logTag, "Delete content in path %s", folderPath); - - DIR *dir = opendir(folderPath); - - if (!dir) - { - ESP_LOGE(logTag, "Failed to stat dir: %s", folderPath); - return -1; - } - - struct dirent *entry; - int deleted = 0; - - while ((entry = readdir(dir)) != NULL) - { - std::string path = string(folderPath) + "/" + entry->d_name; - - if (entry->d_type == DT_REG) - { - // ESP_LOGD(logTag, "Delete file %s", path.c_str()); - if (unlink(path.c_str()) == 0) - { - deleted++; - } - else - { - ESP_LOGE(logTag, "can't delete file: %s", path.c_str()); - } - } - else if (entry->d_type == DT_DIR) - { - deleted += removeFolder(path.c_str(), logTag); - } - } - - closedir(dir); - - if (rmdir(folderPath) != 0) - { - ESP_LOGE(logTag, "can't delete folder: %s", folderPath); - } - - ESP_LOGD(logTag, "%d files in folder %s deleted.", deleted, folderPath); - - return deleted; -} - -std::vector HelperZerlegeZeile(std::string input, std::string _delimiter = "") -{ - std::vector Output; - std::string delimiter = " =,"; - - if (_delimiter.length() > 0) - { - delimiter = _delimiter; - } - - return ZerlegeZeile(input, delimiter); -} - -std::vector ZerlegeZeile(std::string input, std::string delimiter) -{ - std::vector Output; - /* The input can have multiple formats: - * - key = value - * - key = value1 value2 value3 ... - * - key value1 value2 value3 ... - * - * Examples: - * - ImageSize = VGA - * - IO0 = input disabled 10 false false - * - main.dig1 28 144 55 100 false - * - * This causes issues eg. if a password key has a whitespace or equal sign in its value. - * As a workaround and to not break any legacy usage, we enforce to only use the - * equal sign, if the key is "password" - */ - if ((input.find("password") != string::npos) || (input.find("Token") != string::npos)) - { - // Line contains a password, use the equal sign as the only delimiter and only split on first occurrence - size_t pos = input.find("="); - Output.push_back(trim(input.substr(0, pos), "")); - Output.push_back(trim(input.substr(pos + 1, string::npos), "")); - } - else - { - // Legacy Mode - input = trim(input, delimiter); // sonst werden delimiter am Ende (z.B. == im Token) gelöscht) - size_t pos = findDelimiterPos(input, delimiter); - std::string token; - - while (pos != std::string::npos) - { - token = input.substr(0, pos); - token = trim(token, delimiter); - Output.push_back(token); - input.erase(0, pos + 1); - input = trim(input, delimiter); - pos = findDelimiterPos(input, delimiter); - } - - Output.push_back(input); - } - - return Output; -} - -std::string ReplaceString(std::string subject, const std::string &search, const std::string &replace) -{ - size_t pos = 0; - - while ((pos = subject.find(search, pos)) != std::string::npos) - { - subject.replace(pos, search.length(), replace); - pos += replace.length(); - } - - return subject; -} - /* Source: https://git.kernel.org/pub/scm/utils/mmc/mmc-utils.git/tree/lsmmc.c */ /* SD Card Manufacturer Database */ struct SDCard_Manufacturer_database @@ -1002,9 +798,9 @@ struct SDCard_Manufacturer_database mmc_database[] = { }; /* Parse SD Card Manufacturer Database */ -string SDCardParseManufacturerIDs(int id) +string sd_card_parse_manufacturer_ids(int id) { - if (SDCardIsMMC) + if (is_sd_card_mmc) { unsigned int id_cnt = sizeof(mmc_database) / sizeof(struct SDCard_Manufacturer_database); string ret_val = ""; @@ -1045,10 +841,631 @@ string SDCardParseManufacturerIDs(int id) } } -string RundeOutput(double _in, int _anzNachkomma) +string get_sd_card_partition_size() +{ + FATFS *fs; + uint32_t fre_clust, tot_sect; + + /* Get volume information and free clusters of drive 0 */ + f_getfree("0:", (DWORD *)&fre_clust, &fs); + tot_sect = ((fs->n_fatent - 2) * fs->csize) / 1024 / (1024 / sd_card_csd.sector_size); // corrected by SD Card sector size (usually 512 bytes) and convert to MB + + return std::to_string(tot_sect); +} + +string get_sd_card_free_partition_space() +{ + FATFS *fs; + uint32_t fre_clust, fre_sect; + + /* Get volume information and free clusters of drive 0 */ + f_getfree("0:", (DWORD *)&fre_clust, &fs); + fre_sect = (fre_clust * fs->csize) / 1024 / (1024 / sd_card_csd.sector_size); // corrected by SD Card sector size (usually 512 bytes) and convert to MB + + return std::to_string(fre_sect); +} + +string get_sd_card_partition_allocation_size() +{ + FATFS *fs; + uint32_t fre_clust, allocation_size; + + /* Get volume information and free clusters of drive 0 */ + f_getfree("0:", (DWORD *)&fre_clust, &fs); + allocation_size = fs->ssize; + + return std::to_string(allocation_size); +} + +void save_sd_card_info(sdmmc_card_t *card) +{ + sd_card_cid = card->cid; + sd_card_csd = card->csd; + is_sd_card_mmc = card->is_mmc; +} + +string get_sd_card_manufacturer() +{ + string SDCardManufacturer = sd_card_parse_manufacturer_ids(sd_card_cid.mfg_id); + return (SDCardManufacturer + " (ID: " + std::to_string(sd_card_cid.mfg_id) + ")"); +} + +string get_sd_card_name() +{ + char *SDCardName = sd_card_cid.name; + return std::string(SDCardName); +} + +string get_sd_card_capacity() +{ + int SDCardCapacity = sd_card_csd.capacity / (1024 / sd_card_csd.sector_size) / 1024; // total sectors * sector size --> Byte to MB (1024*1024) + return std::to_string(SDCardCapacity); +} + +string get_sd_card_sector_size() +{ + int SDCardSectorSize = sd_card_csd.sector_size; + return std::to_string(SDCardSectorSize); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void mem_copy_gen(uint8_t *_source, uint8_t *_target, int _size) +{ + for (int i = 0; i < _size; ++i) + { + *(_target + i) = *(_source + i); + } +} + +std::string format_filename(std::string input) +{ +#ifdef ISWINDOWS_TRUE + input.erase(0, 1); + std::string os = "/"; + std::string ns = "\\"; + find_replace(input, os, ns); +#endif + return input; +} + +std::size_t file_size(const std::string &file_name) +{ + std::ifstream file(file_name.c_str(), std::ios::in | std::ios::binary); + + if (!file) + { + return 0; + } + + file.seekg(0, std::ios::end); + return static_cast(file.tellg()); +} + +void find_replace(std::string &line, std::string &oldString, std::string &newString) +{ + const size_t oldSize = oldString.length(); + + // do nothing if line is shorter than the string to find + if (oldSize > line.length()) + { + return; + } + + const size_t newSize = newString.length(); + + for (size_t pos = 0;; pos += newSize) + { + // Locate the substring to replace + pos = line.find(oldString, pos); + + if (pos == std::string::npos) + { + return; + } + + if (oldSize == newSize) + { + // if they're same size, use std::string::replace + line.replace(pos, oldSize, newString); + } + else + { + // if not same size, replace by erasing and inserting + line.erase(pos, oldSize); + line.insert(pos, newString); + } + } +} + +/** + * Create a folder and its parent folders as needed + */ +bool make_dir(std::string path) +{ + std::string parent; + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Creating folder " + path + "..."); + + bool bSuccess = false; + int nRC = ::mkdir(path.c_str(), 0775); + + if (nRC == -1) + { + switch (errno) + { + case ENOENT: + // parent didn't exist, try to create it + parent = path.substr(0, path.find_last_of('/')); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Need to create parent folder first: " + parent); + + if (make_dir(parent)) + { + // Now, try to create again. + bSuccess = 0 == ::mkdir(path.c_str(), 0775); + } + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create parent folder: " + parent); + bSuccess = false; + } + break; + + case EEXIST: + // Done! + bSuccess = true; + break; + + default: + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create folder: " + path + " (errno: " + std::to_string(errno) + ")"); + bSuccess = false; + break; + } + } + else + { + bSuccess = true; + } + + return bSuccess; +} + +bool ctype_space(const char c, string adddelimiter) +{ + if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 11) + { + return true; + } + + if (adddelimiter.find(c) != string::npos) + { + return true; + } + + return false; +} + +std::string trim_string_left_right(std::string istring, std::string adddelimiter) +{ + bool trimmed = false; + + if (ctype_space(istring[istring.length() - 1], adddelimiter)) + { + istring.erase(istring.length() - 1); + trimmed = true; + } + + if (ctype_space(istring[0], adddelimiter)) + { + istring.erase(0, 1); + trimmed = true; + } + + if ((trimmed == false) || (istring.size() == 0)) + { + return istring; + } + else + { + return trim_string_left_right(istring, adddelimiter); + } +} + +std::string trim_string_left(std::string istring, std::string adddelimiter) +{ + bool trimmed = false; + + if (ctype_space(istring[0], adddelimiter)) + { + istring.erase(0, 1); + trimmed = true; + } + + if ((trimmed == false) || (istring.size() == 0)) + { + return istring; + } + else + { + return trim_string_left(istring, adddelimiter); + } +} + +std::string trim_string_right(std::string istring, std::string adddelimiter) +{ + bool trimmed = false; + + if (ctype_space(istring[istring.length() - 1], adddelimiter)) + { + istring.erase(istring.length() - 1); + trimmed = true; + } + + if ((trimmed == false) || (istring.size() == 0)) + { + return istring; + } + else + { + return trim_string_right(istring, adddelimiter); + } +} + +size_t find_delimiter_pos(string input, string delimiter) +{ + size_t pos = std::string::npos; + string akt_del; + + for (int anz = 0; anz < delimiter.length(); ++anz) + { + akt_del = delimiter[anz]; + size_t zw = input.find(akt_del); + + if (zw != std::string::npos) + { + if ((pos != std::string::npos) && (zw < pos)) + { + pos = zw; + } + else + { + pos = zw; + } + } + } + + return pos; +} + +bool rename_file(string from, string to) +{ + // ESP_LOGI(logTag, "Renaming File: %s", from.c_str()); + FILE *fpSourceFile = fopen(from.c_str(), "rb"); + + // Sourcefile does not exist otherwise there is a mistake when renaming! + if (!fpSourceFile) + { + ESP_LOGE(TAG, "RenameFile: File %s does not exist!", from.c_str()); + return false; + } + + fclose(fpSourceFile); + rename(from.c_str(), to.c_str()); + + return true; +} + +bool rename_folder(string from, string to) +{ + // ESP_LOGI(logTag, "Renaming Folder: %s", from.c_str()); + DIR *fpSourceFolder = opendir(from.c_str()); + + // Sourcefolder does not exist otherwise there is a mistake when renaming! + if (!fpSourceFolder) + { + ESP_LOGE(TAG, "RenameFolder: Folder %s does not exist!", from.c_str()); + return false; + } + + closedir(fpSourceFolder); + rename(from.c_str(), to.c_str()); + + return true; +} + +bool file_exists(string filename) +{ + FILE *fpSourceFile = fopen(filename.c_str(), "rb"); + + // Sourcefile does not exist + if (!fpSourceFile) + { + return false; + } + + fclose(fpSourceFile); + + return true; +} + +bool folder_exists(string foldername) +{ + DIR *fpSourceFolder = opendir(foldername.c_str()); + + // Sourcefolder does not exist + if (!fpSourceFolder) + { + return false; + } + + closedir(fpSourceFolder); + + return true; +} + +bool delete_file(string filename) +{ + // ESP_LOGI(logTag, "Deleting file: %s", filename.c_str()); + /* Delete file */ + FILE *fpSourceFile = fopen(filename.c_str(), "rb"); + + // Sourcefile does not exist otherwise there is a mistake in copying! + if (!fpSourceFile) + { + ESP_LOGD(TAG, "DeleteFile: File %s existiert nicht!", filename.c_str()); + return false; + } + + fclose(fpSourceFile); + unlink(filename.c_str()); + + return true; +} + +bool copy_file(string input, string output) +{ + input = format_filename(input); + output = format_filename(output); + + if (to_upper(input).compare(NETWORK_CONFIG_FILE) == 0) + { + ESP_LOGD(TAG, "wlan.ini kann nicht kopiert werden!"); + return false; + } + + char cTemp; + FILE *fpSourceFile = fopen(input.c_str(), "rb"); + + // Sourcefile existiert nicht sonst gibt es einen Fehler beim Kopierversuch! + if (!fpSourceFile) + { + ESP_LOGD(TAG, "File %s existiert nicht!", input.c_str()); + return false; + } + + FILE *fpTargetFile = fopen(output.c_str(), "wb"); + + // Code Section + + // Read From The Source File - "Copy" + while (fread(&cTemp, 1, 1, fpSourceFile) == 1) + { + // Write To The Target File - "Paste" + fwrite(&cTemp, 1, 1, fpTargetFile); + } + + // Close The Files + fclose(fpSourceFile); + fclose(fpTargetFile); + ESP_LOGD(TAG, "File copied: %s to %s", input.c_str(), output.c_str()); + + return true; +} + +string get_file_full_filename(string filename) +{ + size_t lastpos = filename.find_last_of('/'); + + if (lastpos == string::npos) + { + return ""; + } + + string zw = filename.substr(lastpos + 1, filename.size() - lastpos); + + return zw; +} + +string get_directory(string filename) +{ + size_t lastpos = filename.find('/'); + + if (lastpos == string::npos) + { + lastpos = filename.find('\\'); + } + + if (lastpos == string::npos) + { + return ""; + } + + string zw = filename.substr(0, lastpos - 1); + return zw; +} + +string get_file_type(string filename) +{ + size_t lastpos = filename.rfind(".", filename.length()); + size_t neu_pos; + + while ((neu_pos = filename.find(".", lastpos + 1)) > -1) + { + lastpos = neu_pos; + } + + if (lastpos == string::npos) + { + return ""; + } + + string zw = filename.substr(lastpos + 1, filename.size() - lastpos); + zw = to_upper(zw); + + return zw; +} + +/* recursive mkdir */ +int mkdir_r(const char *dir, const mode_t mode) +{ + char tmp[FILE_PATH_MAX]; + char *p = NULL; + struct stat sb; + size_t len; + + /* copy path */ + len = strnlen(dir, FILE_PATH_MAX); + + if (len == 0 || len == FILE_PATH_MAX) + { + return -1; + } + + memcpy(tmp, dir, len); + tmp[len] = '\0'; + + /* remove trailing slash */ + if (tmp[len - 1] == '/') + { + tmp[len - 1] = '\0'; + } + + /* check if path exists and is a directory */ + if (stat(tmp, &sb) == 0) + { + if (S_ISDIR(sb.st_mode)) + { + return 0; + } + } + + /* recursive mkdir */ + for (p = tmp + 1; *p; p++) + { + if (*p == '/') + { + *p = 0; + + /* test path */ + if (stat(tmp, &sb) != 0) + { + /* path does not exist - create directory */ + if (mkdir(tmp, mode) < 0) + { + return -1; + } + } + else if (!S_ISDIR(sb.st_mode)) + { + /* not a directory */ + return -1; + } + + *p = '/'; + } + } + + /* test path */ + if (stat(tmp, &sb) != 0) + { + /* path does not exist - create directory */ + if (mkdir(tmp, mode) < 0) + { + return -1; + } + } + else if (!S_ISDIR(sb.st_mode)) + { + /* not a directory */ + return -1; + } + + return 0; +} + +time_t add_days(time_t startTime, int days) +{ + struct tm *tm = localtime(&startTime); + tm->tm_mday += days; + return mktime(tm); +} + +int remove_folder(const char *folderPath, const char *logTag) +{ + // ESP_LOGD(logTag, "Delete content in path %s", folderPath); + + DIR *dir = opendir(folderPath); + + if (!dir) + { + ESP_LOGE(logTag, "Failed to stat dir: %s", folderPath); + return -1; + } + + struct dirent *entry; + int deleted = 0; + + while ((entry = readdir(dir)) != NULL) + { + std::string path = string(folderPath) + "/" + entry->d_name; + + if (entry->d_type == DT_REG) + { + // ESP_LOGD(logTag, "Delete file %s", path.c_str()); + if (unlink(path.c_str()) == 0) + { + deleted++; + } + else + { + ESP_LOGE(logTag, "can't delete file: %s", path.c_str()); + } + } + else if (entry->d_type == DT_DIR) + { + deleted += remove_folder(path.c_str(), logTag); + } + } + + closedir(dir); + + if (rmdir(folderPath) != 0) + { + ESP_LOGE(logTag, "can't delete folder: %s", folderPath); + } + + ESP_LOGD(logTag, "%d files in folder %s deleted.", deleted, folderPath); + + return deleted; +} + +std::string replace_string(std::string subject, const std::string &search, const std::string &replace) +{ + size_t pos = 0; + + while ((pos = subject.find(search, pos)) != std::string::npos) + { + subject.replace(pos, search.length(), replace); + pos += replace.length(); + } + + return subject; +} + +string round_output(double _in, int _anzNachkomma) { std::stringstream stream; - int _zw = _in; + int temp_value = _in; // ESP_LOGD(TAG, "AnzNachkomma: %d", _anzNachkomma); if (_anzNachkomma > 0) @@ -1057,13 +1474,13 @@ string RundeOutput(double _in, int _anzNachkomma) } else { - stream << _zw; + stream << temp_value; } return stream.str(); } -string getMac(void) +string get_mac(void) { uint8_t macInt[6]; char macFormated[6 * 2 + 5 + 1]; // AA:BB:CC:DD:EE:FF @@ -1074,30 +1491,30 @@ string getMac(void) return macFormated; } -void setSystemStatusFlag(SystemStatusFlag_t flag) +void set_system_statusflag(SystemStatusFlag_t flag) { systemStatus = systemStatus | flag; // set bit char buf[20]; - snprintf(buf, sizeof(buf), "0x%08X", getSystemStatus()); + snprintf(buf, sizeof(buf), "0x%08X", get_system_status()); LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "New System Status: " + std::string(buf)); } -void clearSystemStatusFlag(SystemStatusFlag_t flag) +void clear_system_statusflag(SystemStatusFlag_t flag) { systemStatus = systemStatus | ~flag; // clear bit char buf[20]; - snprintf(buf, sizeof(buf), "0x%08X", getSystemStatus()); + snprintf(buf, sizeof(buf), "0x%08X", get_system_status()); LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "New System Status: " + std::string(buf)); } -int getSystemStatus(void) +int get_system_status(void) { return systemStatus; } -bool isSetSystemStatusFlag(SystemStatusFlag_t flag) +bool is_set_system_statusflag(SystemStatusFlag_t flag) { // ESP_LOGE(TAG, "Flag (0x%08X) is set (0x%08X): %d", flag, systemStatus , ((systemStatus & flag) == flag)); @@ -1111,12 +1528,12 @@ bool isSetSystemStatusFlag(SystemStatusFlag_t flag) } } -time_t getUpTime(void) +time_t get_uptime(void) { return (uint32_t)(esp_timer_get_time() / 1000 / 1000); // in seconds } -string getResetReason(void) +string get_reset_reason(void) { std::string reasonText; @@ -1164,12 +1581,12 @@ string getResetReason(void) /** * Returns the current uptime formated ad xxf xxh xxm [xxs] */ -std::string getFormatedUptime(bool compact) +std::string get_formated_uptime(bool compact) { char buf[20]; #pragma GCC diagnostic ignored "-Wformat-truncation" - int uptime = getUpTime(); // in seconds + int uptime = get_uptime(); // in seconds int days = int(floor(uptime / (3600 * 24))); int hours = int(floor((uptime - days * 3600 * 24) / (3600))); @@ -1199,7 +1616,7 @@ const char *get404(void) ""; // Make sure we load the overview page } -std::string UrlDecode(const std::string &value) +std::string url_decode(const std::string &value) { std::string result; result.reserve(value.size()); @@ -1228,12 +1645,12 @@ std::string UrlDecode(const std::string &value) return result; } -bool replaceString(std::string &s, std::string const &toReplace, std::string const &replaceWith) +bool replace_string(std::string &s, std::string const &toReplace, std::string const &replaceWith) { - return replaceString(s, toReplace, replaceWith, true); + return replace_string(s, toReplace, replaceWith, true); } -bool replaceString(std::string &s, std::string const &toReplace, std::string const &replaceWith, bool logIt) +bool replace_string(std::string &s, std::string const &toReplace, std::string const &replaceWith, bool logIt) { std::size_t pos = s.find(toReplace); @@ -1254,11 +1671,23 @@ bool replaceString(std::string &s, std::string const &toReplace, std::string con return true; } -bool isInString(std::string &s, std::string const &toFind) +// from https://stackoverflow.com/a/14678800 +void replace_all(std::string &s, const std::string &toReplace, const std::string &replaceWith) +{ + size_t pos = 0; + + while ((pos = s.find(toReplace, pos)) != std::string::npos) + { + s.replace(pos, toReplace.length(), replaceWith); + pos += replaceWith.length(); + } +} + +bool is_in_string(std::string &s, std::string const &toFind) { std::size_t pos = s.find(toFind); - if (pos == std::string::npos) + if (pos == std::string::npos) { // Not found return false; @@ -1267,28 +1696,16 @@ bool isInString(std::string &s, std::string const &toFind) return true; } -// from https://stackoverflow.com/a/14678800 -void replaceAll(std::string& s, const std::string& toReplace, const std::string& replaceWith) -{ - size_t pos = 0; - - while ((pos = s.find(toReplace, pos)) != std::string::npos) - { - s.replace(pos, toReplace.length(), replaceWith); - pos += replaceWith.length(); - } -} - -bool isStringNumeric(std::string &input) +bool is_string_numeric(std::string &input) { if (input.size() <= 0) { return false; } - + // Replace comma with a dot - replaceString(input, ",", ".", false); - + replace_string(input, ",", ".", false); + int start = 0; int punkt_existiert_schon = 0; @@ -1313,7 +1730,7 @@ bool isStringNumeric(std::string &input) return true; } -bool isStringAlphabetic(std::string &input) +bool is_string_alphabetic(std::string &input) { for (int i = 0; i < input.size(); i++) { @@ -1326,7 +1743,7 @@ bool isStringAlphabetic(std::string &input) return true; } -bool isStringAlphanumeric(std::string &input) +bool is_string_alphanumeric(std::string &input) { for (int i = 0; i < input.size(); i++) { @@ -1339,21 +1756,21 @@ bool isStringAlphanumeric(std::string &input) return true; } -bool alphanumericToBoolean(std::string &input) +bool alphanumeric_to_boolean(std::string &input) { - if (isStringAlphabetic(input)) + if (is_string_alphabetic(input)) { - return stringToBoolean(toUpper(input)); + return string_to_boolean(to_upper(input)); } - else if (isStringNumeric(input)) + else if (is_string_numeric(input)) { - return numericStrToBool(input); + return numeric_str_to_boolean(input); } return false; } -int clipInt(int input, int high, int low) +int clip_int(int input, int high, int low) { if (input < low) { @@ -1366,12 +1783,12 @@ int clipInt(int input, int high, int low) return input; } -bool numericStrToBool(std::string input) +bool numeric_str_to_boolean(std::string input) { return (std::stoi(input) != 0); } -bool stringToBoolean(std::string input) +bool string_to_boolean(std::string input) { return (input == "TRUE"); } diff --git a/code/components/jomjol_helper/Helper.h b/code/components/jomjol_helper/Helper.h index a408c096..6f69996a 100644 --- a/code/components/jomjol_helper/Helper.h +++ b/code/components/jomjol_helper/Helper.h @@ -3,113 +3,148 @@ #ifndef HELPER_H #define HELPER_H +#include "sdkconfig.h" + #include #include #include #include "sdmmc_cmd.h" +#include "defines.h" + +#ifdef CONFIG_SOC_TEMP_SENSOR_SUPPORTED +#include +#endif + +#if (ESP_IDF_VERSION_MAJOR >= 5) +#include "soc/periph_defs.h" +#include "esp_private/periph_ctrl.h" +#include "soc/gpio_sig_map.h" +#include "soc/gpio_periph.h" +#include "soc/io_mux_reg.h" +#include "esp_rom_gpio.h" +#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio +#define gpio_matrix_in(a, b, c) esp_rom_gpio_connect_in_signal(a, b, c) +#define gpio_matrix_out(a, b, c, d) esp_rom_gpio_connect_out_signal(a, b, c, d) +#define ets_delay_us(a) esp_rom_delay_us(a) +#endif + using namespace std; -std::string FormatFileName(std::string input); -std::size_t file_size(const std::string& file_name); -void FindReplace(std::string& line, std::string& oldString, std::string& newString); - -bool CopyFile(string input, string output); -bool DeleteFile(string filename); -bool RenameFile(string from, string to); -bool RenameFolder(string from, string to); -bool MakeDir(std::string _what); -bool FileExists(string filename); -bool FolderExists(string foldername); - -string RundeOutput(double _in, int _anzNachkomma); - -size_t findDelimiterPos(string input, string delimiter); -//string trim(string istring); -string trim(string istring, string adddelimiter = ""); -bool ctype_space(const char c, string adddelimiter); - -string getFileType(string filename); -string getFileFullFileName(string filename); -string getDirectory(string filename); - -int mkdir_r(const char *dir, const mode_t mode); -int removeFolder(const char* folderPath, const char* logTag); - -string toLower(string in); -string toUpper(string in); - -float temperatureRead(); - -std::string intToHexString(int _valueInt); -time_t addDays(time_t startTime, int days); - -void memCopyGen(uint8_t* _source, uint8_t* _target, int _size); - -std::vector HelperZerlegeZeile(std::string input, std::string _delimiter); -std::vector ZerlegeZeile(std::string input, std::string delimiter = " =, \t"); - -/////////////////////////// -size_t getInternalESPHeapSize(); -size_t getESPHeapSize(); -string getESPHeapInfo(); - -///////////////////////////// -string getSDCardPartitionSize(); -string getSDCardFreePartitionSpace(); -string getSDCardPartitionAllocationSize(); - -void SaveSDCardInfo(sdmmc_card_t* card); -string SDCardParseManufacturerIDs(int); -string getSDCardManufacturer(); -string getSDCardName(); -string getSDCardCapacity(); -string getSDCardSectorSize(); - -string getMac(void); - /* Error bit fields One bit per error Make sure it matches https://jomjol.github.io/AI-on-the-edge-device-docs/Error-Codes */ -enum SystemStatusFlag_t { // One bit per error - // First Byte - SYSTEM_STATUS_PSRAM_BAD = 1 << 0, // 1, Critical Error - SYSTEM_STATUS_HEAP_TOO_SMALL = 1 << 1, // 2, Critical Error - SYSTEM_STATUS_CAM_BAD = 1 << 2, // 4, Critical Error - SYSTEM_STATUS_SDCARD_CHECK_BAD = 1 << 3, // 8, Critical Error - SYSTEM_STATUS_FOLDER_CHECK_BAD = 1 << 4, // 16, Critical Error +enum SystemStatusFlag_t +{ // One bit per error + // First Byte + SYSTEM_STATUS_PSRAM_BAD = 1 << 0, // 1, Critical Error + SYSTEM_STATUS_HEAP_TOO_SMALL = 1 << 1, // 2, Critical Error + SYSTEM_STATUS_CAM_BAD = 1 << 2, // 4, Critical Error + SYSTEM_STATUS_SDCARD_CHECK_BAD = 1 << 3, // 8, Critical Error + SYSTEM_STATUS_FOLDER_CHECK_BAD = 1 << 4, // 16, Critical Error - // Second Byte - SYSTEM_STATUS_CAM_FB_BAD = 1 << (0+8), // 8, Flow still might work - SYSTEM_STATUS_NTP_BAD = 1 << (1+8), // 9, Flow will work but time will be wrong + // Second Byte + SYSTEM_STATUS_CAM_FB_BAD = 1 << (0 + 8), // 8, Flow still might work + SYSTEM_STATUS_NTP_BAD = 1 << (1 + 8), // 9, Flow will work but time will be wrong }; -void setSystemStatusFlag(SystemStatusFlag_t flag); -void clearSystemStatusFlag(SystemStatusFlag_t flag); -int getSystemStatus(void); -bool isSetSystemStatusFlag(SystemStatusFlag_t flag); +void string_to_ip4(const char *ip, int &a, int &b, int &c, int &d); +std::string bssid_to_string(const char *c); -time_t getUpTime(void); -string getResetReason(void); -std::string getFormatedUptime(bool compact); +std::string format_filename(std::string input); +std::size_t file_size(const std::string &file_name); +void find_replace(std::string &line, std::string &oldString, std::string &newString); -const char* get404(void); +bool copy_file(string input, string output); +bool delete_file(string filename); +bool rename_file(string from, string to); +bool rename_folder(string from, string to); +bool make_dir(std::string _what); +bool file_exists(string filename); +bool folder_exists(string foldername); -std::string UrlDecode(const std::string& value); +string round_output(double _in, int _anzNachkomma); -void replaceAll(std::string& s, const std::string& toReplace, const std::string& replaceWith); -bool replaceString(std::string& s, std::string const& toReplace, std::string const& replaceWith); -bool replaceString(std::string& s, std::string const& toReplace, std::string const& replaceWith, bool logIt); -bool isInString(std::string& s, std::string const& toFind); +size_t find_delimiter_pos(string input, string delimiter); -bool isStringNumeric(std::string &input); -bool isStringAlphabetic(std::string &input); -bool isStringAlphanumeric(std::string &input); -bool alphanumericToBoolean(std::string &input); +std::string trim_string_left_right(std::string istring, std::string adddelimiter = ""); +std::string trim_string_left(std::string istring, std::string adddelimiter = ""); +std::string trim_string_right(std::string istring, std::string adddelimiter = ""); -int clipInt(int input, int high, int low); -bool numericStrToBool(std::string input); -bool stringToBoolean(std::string input); +bool ctype_space(const char c, string adddelimiter); -#endif //HELPER_H +string get_file_type(string filename); +string get_file_full_filename(string filename); +string get_directory(string filename); + +int mkdir_r(const char *dir, const mode_t mode); +int remove_folder(const char *folderPath, const char *logTag); + +string to_lower(string in); +string to_upper(string in); + +static float temp_sens_value = -1; +#ifdef CONFIG_SOC_TEMP_SENSOR_SUPPORTED +void init_tempsensor(void); +#endif +float read_tempsensor(void); + +time_t add_days(time_t startTime, int days); +void mem_copy_gen(uint8_t *_source, uint8_t *_target, int _size); + +std::vector split_string(const std::string &str); +std::vector split_line(std::string input, std::string _delimiter = "="); + +/////////////////////////// +size_t get_internal_heapsize(); +size_t get_heapsize(); +string get_heapinfo(); + +///////////////////////////// +string get_sd_card_partition_size(); +string get_sd_card_free_partition_space(); +string get_sd_card_partition_allocation_size(); + +void save_sd_card_info(sdmmc_card_t *card); +string sd_card_parse_manufacturer_ids(int); +string get_sd_card_manufacturer(); +string get_sd_card_name(); +string get_sd_card_capacity(); +string get_sd_card_sector_size(); + +string get_mac(void); + +void set_system_statusflag(SystemStatusFlag_t flag); +void clear_system_statusflag(SystemStatusFlag_t flag); +int get_system_status(void); +bool is_set_system_statusflag(SystemStatusFlag_t flag); + +time_t get_uptime(void); +string get_reset_reason(void); +std::string get_formated_uptime(bool compact); + +const char *get404(void); + +std::string url_decode(const std::string &value); + +void replace_all(std::string &s, const std::string &toReplace, const std::string &replaceWith); +bool replace_string(std::string &s, std::string const &toReplace, std::string const &replaceWith); +bool replace_string(std::string &s, std::string const &toReplace, std::string const &replaceWith, bool logIt); + +std::string encrypt_decrypt_string(std::string toEncrypt); +std::string encrypt_pw_string(std::string toEncrypt); +std::string decrypt_pw_string(std::string toDecrypt); +esp_err_t encrypt_decrypt_pw_on_sd(bool _encrypt, std::string filename); + +bool is_in_string(std::string &s, std::string const &toFind); +bool is_string_numeric(std::string &input); +bool is_string_alphabetic(std::string &input); +bool is_string_alphanumeric(std::string &input); +bool alphanumeric_to_boolean(std::string &input); + +int clip_int(int input, int high, int low); +bool numeric_str_to_boolean(std::string input); +bool string_to_boolean(std::string input); + +#endif // HELPER_H diff --git a/code/components/jomjol_helper/esp_sys.cpp b/code/components/jomjol_helper/esp_sys.cpp deleted file mode 100644 index 8df251cb..00000000 --- a/code/components/jomjol_helper/esp_sys.cpp +++ /dev/null @@ -1,179 +0,0 @@ -#include "../../include/defines.h" - -#ifdef DEBUG_ENABLE_SYSINFO - -#include "esp_sys.h" -#include "esp_chip_info.h" - -#include - - -#include "esp_chip_info.h" - - -void Restart() { - esp_restart(); -} - -//source : https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/misc_system_api.html#_CPPv416esp_chip_model_t - -//https://github.com/espressif/esp-idf/blob/8464186e67e34b417621df6b6f1f289a6c60b859/components/esp_hw_support/include/esp_chip_info.h -/* -typedef enum { - CHIP_ESP32 = 1, //!< ESP32 - CHIP_ESP32S2 = 2, //!< ESP32-S2 - CHIP_ESP32S3 = 9, //!< ESP32-S3 - CHIP_ESP32C3 = 5, //!< ESP32-C3 - CHIP_ESP32H4 = 6, //!< ESP32-H4 - CHIP_ESP32C2 = 12, //!< ESP32-C2 - CHIP_ESP32C6 = 13, //!< ESP32-C6 - CHIP_ESP32H2 = 16, //!< ESP32-H2 - CHIP_POSIX_LINUX = 999, //!< The code is running on POSIX/Linux simulator -} esp_chip_model_t; -*/ - -char* GetChipModel(){ - esp_chip_info_t chipInfo; - esp_chip_info(&chipInfo); - switch((int)chipInfo.model) { - case 0 : return (char*)"ESP8266"; - case (int)esp_chip_model_t::CHIP_ESP32 : return (char*)"ESP32"; -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) - case (int)esp_chip_model_t::CHIP_ESP32S2 : return (char*)"ESP32-S2"; - case (int)esp_chip_model_t::CHIP_ESP32S3 : return (char*)"ESP32-S3"; - case (int)esp_chip_model_t::CHIP_ESP32C3 : return (char*)"ESP32-C3"; - case 6 : return (char*)"ESP32-H4"; - case 12 : return (char*)"ESP32-C2"; - case 13 : return (char*)"ESP32-C6"; - //case (int)esp_chip_model_t::CHIP_ESP32H4 : return (char*)"ESP32-H4"; - //case (int)esp_chip_model_t::CHIP_ESP32C2 : return (char*)"ESP32-C2"; - //case (int)esp_chip_model_t::CHIP_ESP32C6 : return (char*)"ESP32-C6"; - //case (int)esp_chip_model_t::CHIP_ESP32H2 : return (char*)"ESP32-H2"; - case 16 : return (char*)"ESP32-H2"; - //case (int)esp_chip_model_t::CHIP_POSIX_LINUX : return (char*)"CHIP_POSIX_LINUX"; - -#endif - } - return (char*)"Chip Unknown"; -} - -uint8_t GetChipCoreCount() { - esp_chip_info_t chipInfo; - esp_chip_info(&chipInfo); - return chipInfo.cores; -} - -uint16_t GetChipRevision() { - esp_chip_info_t chipInfo; - esp_chip_info(&chipInfo); - return chipInfo.revision; -} - -uint32_t GetChipfeatures() { - esp_chip_info_t chipInfo; - esp_chip_info(&chipInfo); - return chipInfo.features; -} - - -uint32_t GetFreeHeap() { - return esp_get_free_heap_size(); -} - -uint32_t GetLeastHeapFreeSinceBoot() { - return esp_get_minimum_free_heap_size(); -} - - -std::string get_device_info() -{ - esp_chip_info_t chip_info; - esp_chip_info(&chip_info); - - std::string espInfoResultStr = ""; - char aMsgBuf[40]; - - espInfoResultStr += "Device Info:"; - espInfoResultStr += "---------------\n"; - espInfoResultStr += "Chip Model: " + std::string(GetChipModel()) +"\n"; - sprintf(aMsgBuf,"Chip Revision: %d\n", chip_info.revision); - espInfoResultStr += std::string(aMsgBuf); - sprintf(aMsgBuf,"CPU Cores: %d\n", chip_info.cores); - espInfoResultStr += std::string(aMsgBuf); - sprintf(aMsgBuf,"Flash Memory: %dMB\n", spi_flash_get_chip_size()/(1024*1024)); - espInfoResultStr += std::string(aMsgBuf); - if(chip_info.features & CHIP_FEATURE_WIFI_BGN) - //espInfoResultStr += "Base MAC: " + std::string(getMac()) +"\n"; - espInfoResultStr += "ESP-IDF version: " + std::string(esp_get_idf_version()) +"\n"; - if((chip_info.features & CHIP_FEATURE_WIFI_BGN) || (chip_info.features & CHIP_FEATURE_BT) || - (chip_info.features & CHIP_FEATURE_BLE) || (chip_info.features & CHIP_FEATURE_EMB_FLASH)) - { - espInfoResultStr += "Characteristics:\n"; - if(chip_info.features & CHIP_FEATURE_WIFI_BGN) - espInfoResultStr += " WiFi 2.4GHz\n"; - if(chip_info.features & CHIP_FEATURE_BT) - espInfoResultStr += " Bluetooth Classic\n"; - if(chip_info.features & CHIP_FEATURE_BLE) - espInfoResultStr += " Bluetooth Low Energy\n"; - if(chip_info.features & CHIP_FEATURE_EMB_FLASH) - espInfoResultStr += " Embedded Flash memory\n"; - else - espInfoResultStr += " External Flash memory\n"; - } - - #ifdef USE_HIMEM_IF_AVAILABLE - sprintf(aMsgBuf,"spiram size %u\n", esp_psram_get_size()); - espInfoResultStr += std::string(aMsgBuf); - sprintf(aMsgBuf,"himem free %u\n", esp_himem_get_free_size()); - espInfoResultStr += std::string(aMsgBuf); - sprintf(aMsgBuf,"himem phys %u\n", esp_himem_get_phys_size()); - espInfoResultStr += std::string(aMsgBuf); - sprintf(aMsgBuf,"himem reserved %u\n", esp_himem_reserved_area_size()); - espInfoResultStr += std::string(aMsgBuf); - #endif - - return espInfoResultStr; -} - - -size_t getFreeMemoryInternal(){ //Current Free Memory - return heap_caps_get_free_size(MALLOC_CAP_8BIT) - heap_caps_get_free_size(MALLOC_CAP_SPIRAM); -} - -size_t getFreeMemorySPIRAM(){ //Current Free Memory - return heap_caps_get_free_size(MALLOC_CAP_SPIRAM); -} - - -size_t getLargestFreeBlockInternal(){ //Largest Free Block - return heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); -} - -size_t getLargestFreeBlockSPIRAM(){ //Largest Free Block - return heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM); -} - - -size_t getMinEverFreeMemInternal(){ //Min. Ever Free Size - return heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); -} - -size_t getMinEverFreeMemSPIRAM(){ //Min. Ever Free Size - return heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM); -} - -#ifdef USE_HIMEM_IF_AVAILABLE - size_t getHimemTotSpace(){ - return esp_himem_get_phys_size(); - } - - size_t getHimemFreeSpace(){ - return esp_himem_get_free_size(); - } - - size_t getHimemReservedArea(){ - return esp_himem_reserved_area_size(); - } -#endif - -#endif //DEBUG_ENABLE_SYSINFO diff --git a/code/components/jomjol_helper/esp_sys.h b/code/components/jomjol_helper/esp_sys.h deleted file mode 100644 index addd5920..00000000 --- a/code/components/jomjol_helper/esp_sys.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include "../../include/defines.h" - -#ifdef DEBUG_ENABLE_SYSINFO - -#ifndef ESP_SYS_H -#define ESP_SYS_H - - -#include - - -// Device libraries (ESP-IDF) -#include -#include -#include - -// for esp_psram_get_size -extern "C" { - #include "esp_psram.h" - #ifdef USE_HIMEM_IF_AVAILABLE - #include - #endif -} - - - - void Restart(); - char *GetChipModel(); - uint8_t GetChipCoreCount(); - uint16_t GetChipRevision(); - uint32_t GetChipfeatures(); - uint32_t GetFreeHeap(); - uint32_t GetLeastHeapFreeSinceBoot(); - - std::string get_device_info(); - - size_t getFreeMemoryInternal(); - size_t getFreeMemorySPIRAM(); - size_t getLargestFreeBlockInternal(); - size_t getLargestFreeBlockSPIRAM(); - size_t getMinEverFreeMemInternal(); - size_t getMinEverFreeMemSPIRAM(); - #ifdef USE_HIMEM_IF_AVAILABLE - size_t getHimemTotSpace(); - size_t getHimemFreeSpace(); - size_t getHimemReservedArea(); - #endif - - -#endif //ESP_SYS_H - -#endif // DEBUG_ENABLE_SYSINFO diff --git a/code/components/jomjol_helper/himem_memory_check.cpp b/code/components/jomjol_helper/himem_memory_check.cpp deleted file mode 100644 index 5d1955e6..00000000 --- a/code/components/jomjol_helper/himem_memory_check.cpp +++ /dev/null @@ -1,115 +0,0 @@ - -// need [env:esp32cam-dev-himem] -//CONFIG_SPIRAM_BANKSWITCH_ENABLE=y -//CONFIG_SPIRAM_BANKSWITCH_RESERVE=4 - - - -#include "../../include/defines.h" - -#ifdef DEBUG_HIMEM_MEMORY_CHECK - -#include "himem_memory_check.h" - -//source adapted from : https://github.com/espressif/esp-idf/blob/master/examples/system/himem/main/himem_example_main.c - - -//Fill memory with pseudo-random data generated from the given seed. -//Fills the memory in 32-bit words for speed. -static void fill_mem_seed(int seed, void *mem, int len) -{ - uint32_t *p = (uint32_t *)mem; - unsigned int rseed = seed ^ 0xa5a5a5a5; - for (int i = 0; i < len / 4; i++) { - *p++ = rand_r(&rseed); - } -} - -//Check the memory filled by fill_mem_seed. Returns true if the data matches the data -//that fill_mem_seed wrote (when given the same seed). -//Returns true if there's a match, false when the region differs from what should be there. -static bool check_mem_seed(int seed, void *mem, int len, int phys_addr) -{ - uint32_t *p = (uint32_t *)mem; - unsigned int rseed = seed ^ 0xa5a5a5a5; - for (int i = 0; i < len / 4; i++) { - uint32_t ex = rand_r(&rseed); - if (ex != *p) { - //printf("check_mem_seed: %x has 0x%08"PRIx32" expected 0x%08"PRIx32"\n", phys_addr+((char*)p-(char*)mem), *p, ex); - return false; - } - p++; - } - return true; -} - -//Allocate a himem region, fill it with data, check it and release it. -static bool test_region(int check_size, int seed) -{ - esp_himem_handle_t mh; //Handle for the address space we're using - esp_himem_rangehandle_t rh; //Handle for the actual RAM. - bool ret = true; - - //Allocate the memory we're going to check. - ESP_ERROR_CHECK(esp_himem_alloc(check_size, &mh)); - //Allocate a block of address range - ESP_ERROR_CHECK(esp_himem_alloc_map_range(ESP_HIMEM_BLKSZ, &rh)); - for (int i = 0; i < check_size; i += ESP_HIMEM_BLKSZ) { - uint32_t *ptr = NULL; - //Map in block, write pseudo-random data, unmap block. - ESP_ERROR_CHECK(esp_himem_map(mh, rh, i, 0, ESP_HIMEM_BLKSZ, 0, (void**)&ptr)); - fill_mem_seed(i ^ seed, ptr, ESP_HIMEM_BLKSZ); // - ESP_ERROR_CHECK(esp_himem_unmap(rh, ptr, ESP_HIMEM_BLKSZ)); - } - vTaskDelay(5); //give the OS some time to do things so the task watchdog doesn't bark - for (int i = 0; i < check_size; i += ESP_HIMEM_BLKSZ) { - uint32_t *ptr; - //Map in block, check against earlier written pseudo-random data, unmap block. - ESP_ERROR_CHECK(esp_himem_map(mh, rh, i, 0, ESP_HIMEM_BLKSZ, 0, (void**)&ptr)); - if (!check_mem_seed(i ^ seed, ptr, ESP_HIMEM_BLKSZ, i)) { - //printf("Error in block %d\n", i / ESP_HIMEM_BLKSZ); - ret = false; - } - ESP_ERROR_CHECK(esp_himem_unmap(rh, ptr, ESP_HIMEM_BLKSZ)); - if (!ret) break; //don't check rest of blocks if error occurred - } - //Okay, all done! - ESP_ERROR_CHECK(esp_himem_free(mh)); - ESP_ERROR_CHECK(esp_himem_free_map_range(rh)); - return ret; -} - - -std::string himem_memory_check() -{ - size_t memcnt=esp_himem_get_phys_size(); - size_t memfree=esp_himem_get_free_size(); - - std::string espInfoResultStr = ""; - char aMsgBuf[40]; - - espInfoResultStr += "Running HIMEM memory check"; - - sprintf(aMsgBuf,"Himem has %dKiB of memory", (int)memcnt/1024); - espInfoResultStr += std::string(aMsgBuf); - - sprintf(aMsgBuf,"%dKiB of which is free", (int)memfree/1024); - espInfoResultStr += std::string(aMsgBuf); - - espInfoResultStr += "\n please wait ....\n"; - - //running memory checks - //assert(test_region(memfree, 0xaaaa)); - - if(test_region(memfree, 0xaaaa)) { - espInfoResultStr += "Himem check Failed!\n"; - } else { - espInfoResultStr += "Himem check Done!\n"; - } - - return espInfoResultStr; - -} - - -#endif // DEBUG_HIMEM_MEMORY_CHECK diff --git a/code/components/jomjol_helper/himem_memory_check.h b/code/components/jomjol_helper/himem_memory_check.h deleted file mode 100644 index a09321a5..00000000 --- a/code/components/jomjol_helper/himem_memory_check.h +++ /dev/null @@ -1,41 +0,0 @@ - -// need [env:esp32cam-dev-himem] -//CONFIG_SPIRAM_BANKSWITCH_ENABLE=y -//CONFIG_SPIRAM_BANKSWITCH_RESERVE=4 - -#pragma once - -#include "../../include/defines.h" - -#ifdef DEBUG_HIMEM_MEMORY_CHECK - -#ifndef HIMEM_MEMORY_CHECK_H -#define HIMEM_MEMORY_CHECK_H - - - -//source : //source : https://github.com/espressif/esp-idf/blob/master/examples/system/himem/main/himem_example_main.c - - -#include -#include -#include -#include - - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "esp_system.h" -#include "esp_heap_caps.h" -#include "esp32/himem.h" - -#include -#include "esp32/himem.h" - - -std::string himem_memory_check(); - -#endif //HIMEM_MEMORY_CHECK_H - -#endif // DEBUG_HIMEM_MEMORY_CHECK diff --git a/code/components/jomjol_helper/perfmon.c b/code/components/jomjol_helper/perfmon.c deleted file mode 100644 index 47acce8b..00000000 --- a/code/components/jomjol_helper/perfmon.c +++ /dev/null @@ -1,87 +0,0 @@ -//source : https://github.com/Carbon225/esp32-perfmon - -#include "../../include/defines.h" - -/* -ESP32 CPU usage monitor -Gives you a rough idea of how the Xtensa cores are utilized. - -Works by attaching idle hooks and measuring how often they get called. The core usage is calculated: usage% = idle ticks since last measurement / expected idle ticks if core were idle * 100%. The expected idle tick count was measured by running an empty program. - -Limitations: -Should only be used for user information, not in logic that needs accurate values -New IDF versions could optimize performance and therefore introduce an error to usage estimation. -When one core is at 100% the other might report a negative value - -Usage: -#include "perfmon.h" -Call perfmon_start() once - -*/ - -#ifdef DEBUG_ENABLE_PERFMON - -#include "perfmon.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_freertos_hooks.h" -#include "sdkconfig.h" - -#include "esp_log.h" -static const char *TAG = "perfmon"; - -static uint64_t idle0Calls = 0; -static uint64_t idle1Calls = 0; - -#if defined(CONFIG_ESP32_DEFAULT_CPU_FREQ_240) -static const uint64_t MaxIdleCalls = 1855000; -#elif defined(CONFIG_ESP32_DEFAULT_CPU_FREQ_160) -static const uint64_t MaxIdleCalls = 1233100; -#else -#error "Unsupported CPU frequency" -#endif - -static bool idle_task_0() -{ - idle0Calls += 1; - return false; -} - -static bool idle_task_1() -{ - idle1Calls += 1; - return false; -} - -static void perfmon_task(void *args) -{ - while (1) - { - float idle0 = idle0Calls; - float idle1 = idle1Calls; - idle0Calls = 0; - idle1Calls = 0; - - int cpu0 = 100.f - idle0 / MaxIdleCalls * 100.f; - int cpu1 = 100.f - idle1 / MaxIdleCalls * 100.f; - - ESP_LOGI(TAG, "Core 0 at %d%%", cpu0); - ESP_LOGI(TAG, "Core 1 at %d%%", cpu1); - // TODO configurable delay - vTaskDelay(5000 / portTICK_PERIOD_MS); - } - vTaskDelete(NULL); -} - -esp_err_t perfmon_start() -{ - ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_task_0, 0)); - ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_task_1, 1)); - // TODO calculate optimal stack size - xTaskCreate(perfmon_task, "perfmon", 2048, NULL, 1, NULL); - return ESP_OK; -} - - -#endif // DEBUG_ENABLE_PERFMON diff --git a/code/components/jomjol_helper/perfmon.h b/code/components/jomjol_helper/perfmon.h deleted file mode 100644 index 969a1449..00000000 --- a/code/components/jomjol_helper/perfmon.h +++ /dev/null @@ -1,24 +0,0 @@ - -#include "../../include/defines.h" - -#ifdef DEBUG_ENABLE_PERFMON - -#ifndef COMPONENTS_PERFMON_INCLUDE_PERFMON_H_ -#define COMPONENTS_PERFMON_INCLUDE_PERFMON_H_ - - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_err.h" - -esp_err_t perfmon_start(); - -#ifdef __cplusplus -} -#endif - -#endif /* COMPONENTS_PERFMON_INCLUDE_PERFMON_H_ */ - -#endif //DEBUG_ENABLE_PERFMON diff --git a/code/components/jomjol_helper/psram.cpp b/code/components/jomjol_helper/psram.cpp index d2caf856..8cd88638 100644 --- a/code/components/jomjol_helper/psram.cpp +++ b/code/components/jomjol_helper/psram.cpp @@ -1,41 +1,41 @@ +#include "defines.h" + #include "ClassLogFile.h" -#include "../../include/defines.h" #include "psram.h" -static const char* TAG = "PSRAM"; +static const char *TAG = "PSRAM"; using namespace std; - void *shared_region = NULL; uint32_t allocatedBytesForSTBI = 0; std::string sharedMemoryInUseFor = ""; - /** Reserve a large block in the PSRAM which will be shared between the different steps. * Each step uses it differently but only wiuthin itself. */ -bool reserve_psram_shared_region(void) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating shared PSRAM region (" + - std::to_string(TENSOR_ARENA_SIZE + MAX_MODEL_SIZE) + " bytes)..."); - shared_region = malloc_psram_heap("Shared PSRAM region", TENSOR_ARENA_SIZE + MAX_MODEL_SIZE, - MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +bool reserve_psram_shared_region(void) +{ + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating shared PSRAM region (" + std::to_string(TENSOR_ARENA_SIZE + MAX_MODEL_SIZE) + " bytes)..."); + shared_region = malloc_psram_heap("Shared PSRAM region", TENSOR_ARENA_SIZE + MAX_MODEL_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - if (shared_region == NULL) { + if (shared_region == NULL) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocating shared PSRAM region!"); return false; } - else { + else + { return true; } } - - /******************************************************************* * Memory used in Take Image (STBI) *******************************************************************/ -bool psram_init_shared_memory_for_take_image_step(void) { - if (sharedMemoryInUseFor != "") { +bool psram_init_shared_memory_for_take_image_step(void) +{ + if (sharedMemoryInUseFor != "") + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!"); return false; } @@ -47,78 +47,87 @@ bool psram_init_shared_memory_for_take_image_step(void) { return true; } - -void psram_deinit_shared_memory_for_take_image_step(void) { +void psram_deinit_shared_memory_for_take_image_step(void) +{ LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Deinit shared memory for step 'Take Image' (STBI buffers)"); allocatedBytesForSTBI = 0; sharedMemoryInUseFor = ""; } +/******************************************************************* + * Memory used in Aligning Step + * During this step we only use the shared part of the PSRAM + * for the TempImage. + *******************************************************************/ +void *psram_reserve_shared_tmp_image_memory(void) +{ + if (sharedMemoryInUseFor != "") + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!"); + return NULL; + } -void *psram_reserve_shared_stbi_memory(size_t size) { - /* Only large buffers should be placed in the shared PSRAM + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating TempImage (" + std::to_string(IMAGE_SIZE) + " bytes, use shared memory in PSRAM)..."); + sharedMemoryInUseFor = "TempImage"; + return shared_region; // Use 1th part of the shared memory for the TempImage (only user) +} + +void psram_free_shared_temp_image_memory(void) +{ + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Shared memory used for TempImage (PSRAM, part of shared memory) is free again"); + sharedMemoryInUseFor = ""; +} + +/******************************************************************* + * + *******************************************************************/ +void *psram_reserve_shared_stbi_memory(size_t size) +{ + /* Only large buffers should be placed in the shared PSRAM * If we also place all smaller STBI buffers here, we get artefacts for some reasons. */ - if (size >= 100000) { - if ((allocatedBytesForSTBI + size) > TENSOR_ARENA_SIZE + MAX_MODEL_SIZE) { // Check if it still fits in the shared region - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM too small (STBI) to fit additional " + - std::to_string(size) + " bytes! Available: " + std::to_string(TENSOR_ARENA_SIZE + MAX_MODEL_SIZE - allocatedBytesForSTBI) + " bytes!"); + if (size >= 100000) + { + if ((allocatedBytesForSTBI + size) > TENSOR_ARENA_SIZE + MAX_MODEL_SIZE) + { + // Check if it still fits in the shared region + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM too small (STBI) to fit additional " + std::to_string(size) + " bytes! Available: " + std::to_string(TENSOR_ARENA_SIZE + MAX_MODEL_SIZE - allocatedBytesForSTBI) + " bytes!"); return NULL; } - + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating memory (" + std::to_string(size) + " bytes) for STBI (use shared memory in PSRAM)..."); allocatedBytesForSTBI += size; return (uint8_t *)shared_region + allocatedBytesForSTBI - size; } - else { // Normal PSRAM + else + { + // Normal PSRAM return malloc_psram_heap("STBI", size, MALLOC_CAP_SPIRAM); } } - -void *psram_reallocate_shared_stbi_memory(void *ptr, size_t newsize) { +void *psram_reallocate_shared_stbi_memory(void *ptr, size_t newsize) +{ char buf[20]; sprintf(buf, "%p", ptr); LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "STBI requested realloc for " + std::string(buf) + " but this is currently unsupported!"); return NULL; } - -void psram_free_shared_stbi_memory(void *p) { - if ((p >= shared_region) && (p <= ((uint8_t *)shared_region + allocatedBytesForSTBI))) { // was allocated inside the shared memory +void psram_free_shared_stbi_memory(void *p) +{ + if ((p >= shared_region) && (p <= ((uint8_t *)shared_region + allocatedBytesForSTBI))) + { + // was allocated inside the shared memory LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Part of shared memory used for STBI (PSRAM, part of shared memory) is free again"); } - else { // Normal PSRAM + else + { + // Normal PSRAM free_psram_heap("STBI", p); } } - - -/******************************************************************* - * Memory used in Aligning Step - * During this step we only use the shared part of the PSRAM - * for the tmpImage. - *******************************************************************/ -void *psram_reserve_shared_tmp_image_memory(void) { - if (sharedMemoryInUseFor != "") { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!"); - return NULL; - } - - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating tmpImage (" + std::to_string(IMAGE_SIZE) + " bytes, use shared memory in PSRAM)..."); - sharedMemoryInUseFor = "Aligning"; - return shared_region; // Use 1th part of the shared memory for the tmpImage (only user) -} - - -void psram_free_shared_temp_image_memory(void) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Shared memory used for tmpImage (PSRAM, part of shared memory) is free again"); - sharedMemoryInUseFor = ""; -} - - - /******************************************************************* * Memory used in Digitization Steps * During this step we only use the shared part of the PSRAM for the @@ -126,86 +135,96 @@ void psram_free_shared_temp_image_memory(void) { * The shared memory is large enough for the largest model and the * Tensor Arena. Therefore we do not need to monitor the usage. *******************************************************************/ -void *psram_get_shared_tensor_arena_memory(void) { - if ((sharedMemoryInUseFor == "") || (sharedMemoryInUseFor == "Digitization_Model")) { +void *psram_get_shared_tensor_arena_memory(void) +{ + if ((sharedMemoryInUseFor == "") || (sharedMemoryInUseFor == "Digitization_Model")) + { sharedMemoryInUseFor = "Digitization_Tensor"; LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating Tensor Arena (" + std::to_string(TENSOR_ARENA_SIZE) + " bytes, use shared memory in PSRAM)..."); return shared_region; // Use 1th part of the shared memory for Tensor } - else { + else + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!"); return NULL; } } - -void *psram_get_shared_model_memory(void) { - if ((sharedMemoryInUseFor == "") || (sharedMemoryInUseFor == "Digitization_Tensor")) { +void *psram_get_shared_model_memory(void) +{ + if ((sharedMemoryInUseFor == "") || (sharedMemoryInUseFor == "Digitization_Tensor")) + { sharedMemoryInUseFor = "Digitization_Model"; LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating Model memory (" + std::to_string(MAX_MODEL_SIZE) + " bytes, use shared memory in PSRAM)..."); return (uint8_t *)shared_region + TENSOR_ARENA_SIZE; // Use 2nd part of the shared memory (after Tensor Arena) for the model } - else { + else + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!"); return NULL; } } - -void psram_free_shared_tensor_arena_and_model_memory(void) { +void psram_free_shared_tensor_arena_and_model_memory(void) +{ sharedMemoryInUseFor = ""; LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Shared memory used for Tensor Arena and model (PSRAM, part of shared memory) is free again"); } - - /******************************************************************* * General *******************************************************************/ -void *malloc_psram_heap(std::string name, size_t size, uint32_t caps) { - void *ptr; +void *malloc_psram_heap(std::string name, size_t size, uint32_t caps) +{ + void *ptr; - ptr = heap_caps_malloc(size, caps); - if (ptr != NULL) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'"); - } - else { + ptr = heap_caps_malloc(size, caps); + if (ptr != NULL) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'"); + } + else + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate " + to_string(size) + " bytes in PSRAM for '" + name + "'!"); } - return ptr; + return ptr; } - -void *realloc_psram_heap(std::string name, void *ptr, size_t size, uint32_t caps) { - ptr = heap_caps_realloc(ptr, size, caps); - if (ptr != NULL) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Reallocated " + to_string(size) + " bytes in PSRAM for '" + name + "'"); - } - else { +void *realloc_psram_heap(std::string name, void *ptr, size_t size, uint32_t caps) +{ + ptr = heap_caps_realloc(ptr, size, caps); + if (ptr != NULL) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Reallocated " + to_string(size) + " bytes in PSRAM for '" + name + "'"); + } + else + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to reallocate " + to_string(size) + " bytes in PSRAM for '" + name + "'!"); } - return ptr; + return ptr; } +void *calloc_psram_heap(std::string name, size_t n, size_t size, uint32_t caps) +{ + void *ptr; -void *calloc_psram_heap(std::string name, size_t n, size_t size, uint32_t caps) { - void *ptr; - - ptr = heap_caps_calloc(n, size, caps); - if (ptr != NULL) { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'"); - } - else { + ptr = heap_caps_calloc(n, size, caps); + if (ptr != NULL) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'"); + } + else + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate " + to_string(size) + " bytes in PSRAM for '" + name + "'!"); } - return ptr; + return ptr; } - -void free_psram_heap(std::string name, void *ptr) { +void free_psram_heap(std::string name, void *ptr) +{ LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Freeing memory in PSRAM used for '" + name + "'..."); heap_caps_free(ptr); } diff --git a/code/components/jomjol_helper/psram.h b/code/components/jomjol_helper/psram.h index 71a5436f..b0ff92d2 100644 --- a/code/components/jomjol_helper/psram.h +++ b/code/components/jomjol_helper/psram.h @@ -4,10 +4,8 @@ #include "esp_heap_caps.h" - bool reserve_psram_shared_region(void); - /* Memory used in Take Image Step */ bool psram_init_shared_memory_for_take_image_step(void); void psram_deinit_shared_memory_for_take_image_step(void); @@ -15,7 +13,6 @@ void *psram_reserve_shared_stbi_memory(size_t size); void *psram_reallocate_shared_stbi_memory(void *ptr, size_t newsize); void psram_free_shared_stbi_memory(void *p); - /* Memory used in Aligning Step */ void *psram_reserve_shared_tmp_image_memory(void); void psram_free_shared_temp_image_memory(void); diff --git a/code/components/jomjol_helper/sdcard_check.h b/code/components/jomjol_helper/sdcard_check.h index 8bcf10f3..d428a043 100644 --- a/code/components/jomjol_helper/sdcard_check.h +++ b/code/components/jomjol_helper/sdcard_check.h @@ -3,7 +3,7 @@ #ifndef COMPONENTS_HELPER_SDCARD_CHECK_H #define COMPONENTS_HELPER_SDCARD_CHECK_H -#include "../../include/defines.h" +#include "defines.h" int SDCardCheckRW(void); bool SDCardCheckFolderFilePresence(void); diff --git a/code/components/jomjol_helper/sdcard_init.c b/code/components/jomjol_helper/sdcard_init.c deleted file mode 100644 index 1c306ac9..00000000 --- a/code/components/jomjol_helper/sdcard_init.c +++ /dev/null @@ -1,646 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include - -#include "sdcard_init.h" - -#include "esp_log.h" -#include "ffconf.h" -#include "esp_compiler.h" -#include "esp_vfs.h" -#include "vfs_fat_internal.h" -#include "diskio_impl.h" -#include "diskio_sdmmc.h" -#include "soc/soc_caps.h" -#include "driver/sdmmc_defs.h" - -#if SOC_SDMMC_HOST_SUPPORTED - #include "driver/sdmmc_host.h" -#endif - -static sdmmc_card_t* s_cards[FF_VOLUMES] = { NULL }; -static bool s_disk_status_check_en[FF_VOLUMES] = { }; - -static const char* TAG = "sdcard_init"; - -#define CHECK_EXECUTE_RESULT(err, str) do { \ - if ((err) !=ESP_OK) { \ - ESP_LOGE(TAG, str" (0x%x).", err); \ - goto cleanup; \ - } \ - } while(0) - -typedef struct mh_vfs_fat_sd_ctx_t { - BYTE pdrv; //Drive number that is mounted - esp_vfs_fat_mount_config_t mount_config; //Mount configuration - FATFS *fs; //FAT structure pointer that is registered - sdmmc_card_t *card; //Card info - char *base_path; //Path where partition is registered -} mh_vfs_fat_sd_ctx_t; - -static mh_vfs_fat_sd_ctx_t *s_ctx[FF_VOLUMES] = {}; - -/** - * This `s_saved_ctx_id` is only used by `esp_vfs_fat_sdmmc_unmount`, which is deprecated. - * This variable together with `esp_vfs_fat_sdmmc_unmount` should be removed in next major version - */ -static uint32_t s_saved_ctx_id = FF_VOLUMES; - - -static void call_host_deinit_mh(const sdmmc_host_t *host_config); -static esp_err_t partition_card_mh(const esp_vfs_fat_mount_config_t *mount_config, const char *drv, sdmmc_card_t *card, BYTE pdrv); - - -//Check if SD/MMC card is present -static DSTATUS ff_sdmmc_card_available_mh(BYTE pdrv) -{ - sdmmc_card_t* card = s_cards[pdrv]; - assert(card); - esp_err_t err = sdmmc_get_status(card); - - if (unlikely(err != ESP_OK)) { - ESP_LOGE(TAG, "Check status failed (0x%x)", err); - return STA_NOINIT; - } - return 0; -} - -/** -* ff_sdmmc_status() and ff_sdmmc_initialize() return STA_NOINIT when sdmmc_get_status() -* fails. This error value is checked throughout the FATFS code. -* Both functions return 0 on success. -*/ -DSTATUS ff_sdmmc_initialize_mh (BYTE pdrv) -{ - return ff_sdmmc_card_available_mh(pdrv); -} - -DSTATUS ff_sdmmc_status_mh(BYTE pdrv) -{ - if (s_disk_status_check_en[pdrv]) { - return ff_sdmmc_card_available_mh(pdrv); - } - return 0; -} - -DRESULT ff_sdmmc_read_mh (BYTE pdrv, BYTE* buff, DWORD sector, UINT count) -{ - sdmmc_card_t* card = s_cards[pdrv]; - assert(card); - esp_err_t err = sdmmc_read_sectors(card, buff, sector, count); - if (unlikely(err != ESP_OK)) { - ESP_LOGE(TAG, "sdmmc_read_blocks failed (%d)", err); - return RES_ERROR; - } - return RES_OK; -} - -DRESULT ff_sdmmc_write_mh (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) -{ - sdmmc_card_t* card = s_cards[pdrv]; - assert(card); - esp_err_t err = sdmmc_write_sectors(card, buff, sector, count); - if (unlikely(err != ESP_OK)) { - ESP_LOGE(TAG, "sdmmc_write_blocks failed (%d)", err); - return RES_ERROR; - } - return RES_OK; -} - -#if FF_USE_TRIM -DRESULT ff_sdmmc_trim_mh (BYTE pdrv, DWORD start_sector, DWORD sector_count) -{ - sdmmc_card_t* card = s_cards[pdrv]; - assert(card); - sdmmc_erase_arg_t arg; - - arg = sdmmc_can_discard(card) == ESP_OK ? SDMMC_DISCARD_ARG : SDMMC_ERASE_ARG; - esp_err_t err = sdmmc_erase_sectors(card, start_sector, sector_count, arg); - if (unlikely(err != ESP_OK)) { - ESP_LOGE(TAG, "sdmmc_erase_sectors failed (%d)", err); - return RES_ERROR; - } - return RES_OK; -} -#endif //FF_USE_TRIM - -DRESULT ff_sdmmc_ioctl_mh (BYTE pdrv, BYTE cmd, void* buff) -{ - sdmmc_card_t* card = s_cards[pdrv]; - assert(card); - switch(cmd) { - case CTRL_SYNC: - return RES_OK; - case GET_SECTOR_COUNT: - *((DWORD*) buff) = card->csd.capacity; - return RES_OK; - case GET_SECTOR_SIZE: - *((WORD*) buff) = card->csd.sector_size; - return RES_OK; - case GET_BLOCK_SIZE: - return RES_ERROR; -#if FF_USE_TRIM - case CTRL_TRIM: - if (sdmmc_can_trim(card) != ESP_OK) { - return RES_PARERR; - } - return ff_sdmmc_trim_mh (pdrv, *((DWORD*)buff), //start_sector - (*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count -#endif //FF_USE_TRIM - } - return RES_ERROR; -} - -void ff_sdmmc_set_disk_status_check_mh(BYTE pdrv, bool enable) -{ - s_disk_status_check_en[pdrv] = enable; -} - -void ff_diskio_register_sdmmc_mh(BYTE pdrv, sdmmc_card_t* card) -{ - static const ff_diskio_impl_t sdmmc_impl = { - .init = &ff_sdmmc_initialize_mh, - .status = &ff_sdmmc_status_mh, - .read = &ff_sdmmc_read_mh, - .write = &ff_sdmmc_write_mh, - .ioctl = &ff_sdmmc_ioctl_mh - }; - s_cards[pdrv] = card; - s_disk_status_check_en[pdrv] = false; - ff_diskio_register(pdrv, &sdmmc_impl); -} - -BYTE ff_diskio_get_pdrv_card_mh(const sdmmc_card_t* card) -{ - for (int i = 0; i < FF_VOLUMES; i++) { - if (card == s_cards[i]) { - return i; - } - } - return 0xff; -} - -static bool s_get_context_id_by_card_mh(const sdmmc_card_t *card, uint32_t *out_id) -{ - mh_vfs_fat_sd_ctx_t *p_ctx = NULL; - for (int i = 0; i < FF_VOLUMES; i++) { - p_ctx = s_ctx[i]; - if (p_ctx) { - if (p_ctx->card == card) { - *out_id = i; - return true; - } - } - } - return false; -} - -static uint32_t s_get_unused_context_id_mh(void) -{ - for (uint32_t i = 0; i < FF_VOLUMES; i++) { - if (!s_ctx[i]) { - return i; - } - } - return FF_VOLUMES; -} - -static esp_err_t mount_prepare_mem_mh(const char *base_path, BYTE *out_pdrv, char **out_dup_path, sdmmc_card_t** out_card) -{ - esp_err_t err = ESP_OK; - char* dup_path = NULL; - sdmmc_card_t* card = NULL; - - // connect SDMMC driver to FATFS - BYTE pdrv = FF_DRV_NOT_USED; - - if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == FF_DRV_NOT_USED) { - ESP_LOGD(TAG, "the maximum count of volumes is already mounted"); - return ESP_ERR_NO_MEM; - } - - // not using ff_memalloc here, as allocation in internal RAM is preferred - card = (sdmmc_card_t*)malloc(sizeof(sdmmc_card_t)); - - if (card == NULL) { - ESP_LOGD(TAG, "could not locate new sdmmc_card_t"); - err = ESP_ERR_NO_MEM; - goto cleanup; - } - - dup_path = strdup(base_path); - - if(!dup_path){ - ESP_LOGD(TAG, "could not copy base_path"); - err = ESP_ERR_NO_MEM; - goto cleanup; - } - - *out_card = card; - *out_pdrv = pdrv; - *out_dup_path = dup_path; - return ESP_OK; -cleanup: - free(card); - free(dup_path); - return err; -} - -static esp_err_t s_f_mount_mh(sdmmc_card_t *card, FATFS *fs, const char *drv, uint8_t pdrv, const esp_vfs_fat_mount_config_t *mount_config) -{ - esp_err_t err = ESP_OK; - FRESULT res = f_mount(fs, drv, 1); - if (res != FR_OK) { - err = ESP_FAIL; - ESP_LOGW(TAG, "failed to mount card (%d)", res); - - bool need_mount_again = (res == FR_NO_FILESYSTEM || res == FR_INT_ERR) && mount_config->format_if_mount_failed; - - if (!need_mount_again) { - return ESP_FAIL; - } - - err = partition_card_mh(mount_config, drv, card, pdrv); - - if (err != ESP_OK) { - return err; - } - - ESP_LOGW(TAG, "mounting again"); - res = f_mount(fs, drv, 0); - - if (res != FR_OK) { - err = ESP_FAIL; - ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res); - return err; - } - } - - return ESP_OK; -} - -static esp_err_t mount_to_vfs_fat_mh(const esp_vfs_fat_mount_config_t *mount_config, sdmmc_card_t *card, uint8_t pdrv, const char *base_path, FATFS **out_fs) -{ - FATFS *fs = NULL; - esp_err_t err; - ff_diskio_register_sdmmc_mh(pdrv, card); - ff_sdmmc_set_disk_status_check_mh(pdrv, mount_config->disk_status_check_enable); - ESP_LOGD(TAG, "using pdrv=%i", pdrv); - char drv[3] = {(char)('0' + pdrv), ':', 0}; - - // connect FATFS to VFS - err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs); - *out_fs = fs; - - if (err == ESP_ERR_INVALID_STATE) { - // it's okay, already registered with VFS - } else if (err != ESP_OK) { - ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", err); - goto fail; - } - - // Try to mount partition - err = s_f_mount_mh(card, fs, drv, pdrv, mount_config); - - if (err != ESP_OK) { - goto fail; - } - return ESP_OK; - -fail: - if (fs) { - f_mount(NULL, drv, 0); - } - esp_vfs_fat_unregister_path(base_path); - ff_diskio_unregister(pdrv); - return err; -} - -static esp_err_t partition_card_mh(const esp_vfs_fat_mount_config_t *mount_config, const char *drv, sdmmc_card_t *card, BYTE pdrv) -{ - FRESULT res = FR_OK; - esp_err_t err; - const size_t workbuf_size = 4096; - void* workbuf = NULL; - ESP_LOGW(TAG, "partitioning card"); - - workbuf = ff_memalloc(workbuf_size); - - if (workbuf == NULL) { - return ESP_ERR_NO_MEM; - } - - LBA_t plist[] = {100, 0, 0, 0}; - res = f_fdisk(pdrv, plist, workbuf); - - if (res != FR_OK) { - err = ESP_FAIL; - ESP_LOGD(TAG, "f_fdisk failed (%d)", res); - goto fail; - } - - size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(card->csd.sector_size, mount_config->allocation_unit_size); - - ESP_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size); - const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size}; - res = f_mkfs(drv, &opt, workbuf, workbuf_size); - - if (res != FR_OK) { - err = ESP_FAIL; - ESP_LOGD(TAG, "f_mkfs failed (%d)", res); - goto fail; - } - - free(workbuf); - return ESP_OK; -fail: - free(workbuf); - return err; -} - -#if SOC_SDMMC_HOST_SUPPORTED -static esp_err_t init_sdmmc_host_mh(int slot, const void *slot_config, int *out_slot) -{ - *out_slot = slot; - return sdmmc_host_init_slot(slot, (const sdmmc_slot_config_t*) slot_config); -} - -esp_err_t esp_vfs_fat_sdmmc_mount_mh(const char* base_path, const sdmmc_host_t* host_config, const void* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card) -{ - esp_err_t err; - mh_vfs_fat_sd_ctx_t *ctx = NULL; - uint32_t ctx_id = FF_VOLUMES; - FATFS *fs = NULL; - int card_handle = -1; //uninitialized - sdmmc_card_t* card = NULL; - BYTE pdrv = FF_DRV_NOT_USED; - char* dup_path = NULL; - bool host_inited = false; - - err = mount_prepare_mem_mh(base_path, &pdrv, &dup_path, &card); - - if (err != ESP_OK) { - ESP_LOGE(TAG, "mount_prepare failed"); - return err; - } - - err = (*host_config->init)(); - CHECK_EXECUTE_RESULT(err, "host init failed"); - //deinit() needs to be called to revert the init - host_inited = true; - //If this failed (indicated by card_handle != -1), slot deinit needs to called() - //leave card_handle as is to indicate that (though slot deinit not implemented yet. - err = init_sdmmc_host_mh(host_config->slot, slot_config, &card_handle); - CHECK_EXECUTE_RESULT(err, "slot init failed"); - - // probe and initialize card - err = sdmmc_card_init(host_config, card); - CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed"); - - err = mount_to_vfs_fat_mh(mount_config, card, pdrv, dup_path, &fs); - CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed"); - - if (out_card != NULL) { - *out_card = card; - } - - //For deprecation backward compatibility - if (s_saved_ctx_id == FF_VOLUMES) { - s_saved_ctx_id = 0; - } - - ctx = calloc(sizeof(mh_vfs_fat_sd_ctx_t), 1); - - if (!ctx) { - CHECK_EXECUTE_RESULT(ESP_ERR_NO_MEM, "no mem"); - } - - ctx->pdrv = pdrv; - memcpy(&ctx->mount_config, mount_config, sizeof(esp_vfs_fat_mount_config_t)); - ctx->card = card; - ctx->base_path = dup_path; - ctx->fs = fs; - ctx_id = s_get_unused_context_id_mh(); - assert(ctx_id != FF_VOLUMES); - s_ctx[ctx_id] = ctx; - - return ESP_OK; -cleanup: - if (host_inited) { - call_host_deinit_mh(host_config); - } - - free(card); - free(dup_path); - return err; -} -#endif - -static esp_err_t init_sdspi_host_mh(int slot, const void *slot_config, int *out_slot) -{ - esp_err_t err = sdspi_host_init_device((const sdspi_device_config_t*)slot_config, out_slot); - if (err != ESP_OK) { - ESP_LOGE(TAG, -"Failed to attach sdspi device onto an SPI bus (rc=0x%x), please initialize the \ -bus first and check the device parameters." - , err); - } - return err; -} - -esp_err_t esp_vfs_fat_sdspi_mount_mh(const char* base_path, const sdmmc_host_t* host_config_input, const sdspi_device_config_t* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card) -{ - const sdmmc_host_t* host_config = host_config_input; - esp_err_t err; - mh_vfs_fat_sd_ctx_t *ctx = NULL; - uint32_t ctx_id = FF_VOLUMES; - FATFS *fs = NULL; - int card_handle = -1; //uninitialized - bool host_inited = false; - BYTE pdrv = FF_DRV_NOT_USED; - sdmmc_card_t* card = NULL; - char* dup_path = NULL; - - err = mount_prepare_mem_mh(base_path, &pdrv, &dup_path, &card); - if (err != ESP_OK) { - ESP_LOGE(TAG, "mount_prepare failed"); - return err; - } - - //the init() function is usually empty, doesn't require any deinit to revert it - err = (*host_config->init)(); - CHECK_EXECUTE_RESULT(err, "host init failed"); - - err = init_sdspi_host_mh(host_config->slot, slot_config, &card_handle); - CHECK_EXECUTE_RESULT(err, "slot init failed"); - //Set `host_inited` to true to indicate that host_config->deinit() needs - //to be called to revert `init_sdspi_host` - host_inited = true; - - //The `slot` argument inside host_config should be replaced by the SD SPI handled returned - //above. But the input pointer is const, so create a new variable. - - sdmmc_host_t new_config; - - if (card_handle != host_config->slot) { - new_config = *host_config_input; - host_config = &new_config; - new_config.slot = card_handle; - } - - // probe and initialize card - err = sdmmc_card_init(host_config, card); - CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed"); - - err = mount_to_vfs_fat_mh(mount_config, card, pdrv, dup_path, &fs); - CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed"); - - if (out_card != NULL) { - *out_card = card; - } - - //For deprecation backward compatibility - if (s_saved_ctx_id == FF_VOLUMES) { - s_saved_ctx_id = 0; - } - - ctx = calloc(sizeof(mh_vfs_fat_sd_ctx_t), 1); - - if (!ctx) { - CHECK_EXECUTE_RESULT(ESP_ERR_NO_MEM, "no mem"); - } - - ctx->pdrv = pdrv; - memcpy(&ctx->mount_config, mount_config, sizeof(esp_vfs_fat_mount_config_t)); - ctx->card = card; - ctx->base_path = dup_path; - ctx->fs = fs; - ctx_id = s_get_unused_context_id_mh(); - assert(ctx_id != FF_VOLUMES); - s_ctx[ctx_id] = ctx; - - return ESP_OK; - -cleanup: - if (host_inited) { - call_host_deinit_mh(host_config); - } - - free(card); - free(dup_path); - return err; -} - -static void call_host_deinit_mh(const sdmmc_host_t *host_config) -{ - if (host_config->flags & SDMMC_HOST_FLAG_DEINIT_ARG) { - host_config->deinit_p(host_config->slot); - } else { - host_config->deinit(); - } -} - -static esp_err_t unmount_card_core_mh(const char *base_path, sdmmc_card_t *card) -{ - BYTE pdrv = ff_diskio_get_pdrv_card_mh(card); - - if (pdrv == 0xff) { - return ESP_ERR_INVALID_ARG; - } - - // unmount - char drv[3] = {(char)('0' + pdrv), ':', 0}; - f_mount(0, drv, 0); - // release SD driver - ff_diskio_unregister(pdrv); - - call_host_deinit_mh(&card->host); - free(card); - - esp_err_t err = esp_vfs_fat_unregister_path(base_path); - return err; -} - -esp_err_t esp_vfs_fat_sdmmc_unmount_mh(void) -{ - esp_err_t err = unmount_card_core_mh(s_ctx[s_saved_ctx_id]->base_path, s_ctx[s_saved_ctx_id]->card); - free(s_ctx[s_saved_ctx_id]); - s_ctx[s_saved_ctx_id] = NULL; - s_saved_ctx_id = FF_VOLUMES; - return err; -} - -esp_err_t esp_vfs_fat_sdcard_unmount_mh(const char *base_path, sdmmc_card_t *card) -{ - uint32_t id = FF_VOLUMES; - bool found = s_get_context_id_by_card_mh(card, &id); - - if (!found) { - return ESP_ERR_INVALID_ARG; - } - - free(s_ctx[id]); - s_ctx[id] = NULL; - - esp_err_t err = unmount_card_core_mh(base_path, card); - - return err; -} - -esp_err_t esp_vfs_fat_sdcard_format_mh(const char *base_path, sdmmc_card_t *card) -{ - esp_err_t ret = ESP_OK; - - if (!card) { - ESP_LOGE(TAG, "card not initialized"); - return ESP_ERR_INVALID_STATE; - } - - BYTE pdrv = ff_diskio_get_pdrv_card_mh(card); - - if (pdrv == 0xff) { - ESP_LOGE(TAG, "card driver not registered"); - return ESP_ERR_INVALID_STATE; - } - - const size_t workbuf_size = 4096; - void *workbuf = ff_memalloc(workbuf_size); - - if (workbuf == NULL) { - return ESP_ERR_NO_MEM; - } - - //unmount - char drv[3] = {(char)('0' + pdrv), ':', 0}; - f_mount(0, drv, 0); - - //format - uint32_t id = FF_VOLUMES; - bool found = s_get_context_id_by_card_mh(card, &id); - assert(found); - size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(card->csd.sector_size, s_ctx[id]->mount_config.allocation_unit_size); - ESP_LOGI(TAG, "Formatting card, allocation unit size=%d", alloc_unit_size); - const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size}; - FRESULT res = f_mkfs(drv, &opt, workbuf, workbuf_size); - free(workbuf); - - if (res != FR_OK) { - ret = ESP_FAIL; - ESP_LOGD(TAG, "f_mkfs failed (%d)", res); - } - - //mount back - esp_err_t err = s_f_mount_mh(card, s_ctx[id]->fs, drv, pdrv, &s_ctx[id]->mount_config); - - if (err != ESP_OK) { - unmount_card_core_mh(base_path, card); - ESP_LOGE(TAG, "failed to format, resources recycled, please mount again"); - } - - return ret; -} diff --git a/code/components/jomjol_helper/sdcard_init.h b/code/components/jomjol_helper/sdcard_init.h deleted file mode 100644 index bed3f2f3..00000000 --- a/code/components/jomjol_helper/sdcard_init.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include "esp_err.h" -#include "driver/gpio.h" -#include "sdmmc_cmd.h" -#include "driver/sdmmc_types.h" -#include "driver/sdspi_host.h" -#include "ff.h" -#include "esp_vfs_fat.h" -#include "wear_levelling.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Convenience function to get FAT filesystem on SD card registered in VFS - * - * This is an all-in-one function which does the following: - * - initializes SDMMC driver or SPI driver with configuration in host_config - * - initializes SD card with configuration in slot_config - * - mounts FAT partition on SD card using FATFS library, with configuration in mount_config - * - registers FATFS library with VFS, with prefix given by base_prefix variable - * - * This function is intended to make example code more compact. - * For real world applications, developers should implement the logic of - * probing SD card, locating and mounting partition, and registering FATFS in VFS, - * with proper error checking and handling of exceptional conditions. - * - * @note Use this API to mount a card through SDSPI is deprecated. Please call - * `esp_vfs_fat_sdspi_mount()` instead for that case. - * - * @param base_path path where partition should be registered (e.g. "/sdcard") - * @param host_config Pointer to structure describing SDMMC host. When using - * SDMMC peripheral, this structure can be initialized using - * SDMMC_HOST_DEFAULT() macro. When using SPI peripheral, - * this structure can be initialized using SDSPI_HOST_DEFAULT() - * macro. - * @param slot_config Pointer to structure with slot configuration. - * For SDMMC peripheral, pass a pointer to sdmmc_slot_config_t - * structure initialized using SDMMC_SLOT_CONFIG_DEFAULT. - * @param mount_config pointer to structure with extra parameters for mounting FATFS - * @param[out] out_card if not NULL, pointer to the card information structure will be returned via this argument - * @return - * - ESP_OK on success - * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called - * - ESP_ERR_NO_MEM if memory can not be allocated - * - ESP_FAIL if partition can not be mounted - * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers - */ -esp_err_t esp_vfs_fat_sdmmc_mount_mh(const char* base_path, const sdmmc_host_t* host_config, const void* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card); - -/** - * @brief Convenience function to get FAT filesystem on SD card registered in VFS - * - * This is an all-in-one function which does the following: - * - initializes an SPI Master device based on the SPI Master driver with configuration in - * slot_config, and attach it to an initialized SPI bus. - * - initializes SD card with configuration in host_config_input - * - mounts FAT partition on SD card using FATFS library, with configuration in mount_config - * - registers FATFS library with VFS, with prefix given by base_prefix variable - * - * This function is intended to make example code more compact. - * For real world applications, developers should implement the logic of - * probing SD card, locating and mounting partition, and registering FATFS in VFS, - * with proper error checking and handling of exceptional conditions. - * - * @note This function try to attach the new SD SPI device to the bus specified in host_config. - * Make sure the SPI bus specified in `host_config->slot` have been initialized by - * `spi_bus_initialize()` before. - * - * @param base_path path where partition should be registered (e.g. "/sdcard") - * @param host_config_input Pointer to structure describing SDMMC host. This structure can be - * initialized using SDSPI_HOST_DEFAULT() macro. - * @param slot_config Pointer to structure with slot configuration. - * For SPI peripheral, pass a pointer to sdspi_device_config_t - * structure initialized using SDSPI_DEVICE_CONFIG_DEFAULT(). - * @param mount_config pointer to structure with extra parameters for mounting FATFS - * @param[out] out_card If not NULL, pointer to the card information structure will be returned via - * this argument. It is suggested to hold this handle and use it to unmount the card later if - * needed. Otherwise it's not suggested to use more than one card at the same time and unmount one - * of them in your application. - * @return - * - ESP_OK on success - * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called - * - ESP_ERR_NO_MEM if memory can not be allocated - * - ESP_FAIL if partition can not be mounted - * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers - */ -esp_err_t esp_vfs_fat_sdspi_mount_mh(const char* base_path, const sdmmc_host_t* host_config_input, const sdspi_device_config_t* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card); - -#ifdef __cplusplus -} -#endif - - - - - - - - - - diff --git a/code/components/jomjol_helper/statusled.cpp b/code/components/jomjol_helper/statusled.cpp index d2f5650a..6921b12b 100644 --- a/code/components/jomjol_helper/statusled.cpp +++ b/code/components/jomjol_helper/statusled.cpp @@ -1,3 +1,5 @@ +#include "defines.h" + #include "statusled.h" #include @@ -5,7 +7,6 @@ #include "driver/gpio.h" #include "ClassLogFile.h" -#include "../../include/defines.h" // define `gpio_pad_select_gpip` for newer versions of IDF #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)) @@ -13,141 +14,153 @@ #define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio #endif -static const char* TAG = "STATUSLED"; +static const char *TAG = "STATUSLED"; TaskHandle_t xHandle_task_StatusLED = NULL; struct StatusLEDData StatusLEDData = {}; - -void task_StatusLED(void *pvParameter) +void task_status_led(void *pvParameter) { - //ESP_LOGD(TAG, "task_StatusLED - create"); - while (StatusLEDData.bProcessingRequest) + // ESP_LOGD(TAG, "task_StatusLED - create"); + while (StatusLEDData.bProcessingRequest) { - //ESP_LOGD(TAG, "task_StatusLED - start"); + // ESP_LOGD(TAG, "task_StatusLED - start"); struct StatusLEDData StatusLEDDataInt = StatusLEDData; - gpio_pad_select_gpio(BLINK_GPIO); // Init the GPIO + gpio_pad_select_gpio(BLINK_GPIO); // Init the GPIO gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); // Set the GPIO as a push/pull output - gpio_set_level(BLINK_GPIO, 1);// LED off + gpio_set_level(BLINK_GPIO, 1); // LED off - for (int i=0; i<2; ) // Default: repeat 2 times + for (int i = 0; i < 2;) // Default: repeat 2 times { if (!StatusLEDDataInt.bInfinite) + { ++i; + } for (int j = 0; j < StatusLEDDataInt.iSourceBlinkCnt; ++j) { gpio_set_level(BLINK_GPIO, 0); vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS); - gpio_set_level(BLINK_GPIO, 1); + gpio_set_level(BLINK_GPIO, 1); vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS); } - vTaskDelay(500 / portTICK_PERIOD_MS); // Delay between module code and error code + vTaskDelay(500 / portTICK_PERIOD_MS); // Delay between module code and error code for (int j = 0; j < StatusLEDDataInt.iCodeBlinkCnt; ++j) { - gpio_set_level(BLINK_GPIO, 0); + gpio_set_level(BLINK_GPIO, 0); vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS); gpio_set_level(BLINK_GPIO, 1); vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS); } - vTaskDelay(1500 / portTICK_PERIOD_MS); // Delay to signal new round + vTaskDelay(1500 / portTICK_PERIOD_MS); // Delay to signal new round } StatusLEDData.bProcessingRequest = false; - //ESP_LOGD(TAG, "task_StatusLED - done/wait"); - vTaskDelay(10000 / portTICK_PERIOD_MS); // Wait for an upcoming request otherwise continue and delete task to save memory + // ESP_LOGD(TAG, "task_StatusLED - done/wait"); + vTaskDelay(10000 / portTICK_PERIOD_MS); // Wait for an upcoming request otherwise continue and delete task to save memory } - //ESP_LOGD(TAG, "task_StatusLED - delete"); + // ESP_LOGD(TAG, "task_StatusLED - delete"); xHandle_task_StatusLED = NULL; - vTaskDelete(NULL); // Delete this task due to no request + vTaskDelete(NULL); // Delete this task due to no request } - -void StatusLED(StatusLedSource _eSource, int _iCode, bool _bInfinite) +void set_status_led(StatusLedSource _eSource, int _iCode, bool _bInfinite) { - //ESP_LOGD(TAG, "StatusLED - start"); + // ESP_LOGD(TAG, "StatusLED - start"); - if (_eSource == WLAN_CONN) { + if (_eSource == WLAN_CONN) + { StatusLEDData.iSourceBlinkCnt = WLAN_CONN; StatusLEDData.iCodeBlinkCnt = _iCode; StatusLEDData.iBlinkTime = 250; StatusLEDData.bInfinite = _bInfinite; } - else if (_eSource == WLAN_INIT) { + else if (_eSource == WLAN_INIT) + { StatusLEDData.iSourceBlinkCnt = WLAN_INIT; StatusLEDData.iCodeBlinkCnt = _iCode; StatusLEDData.iBlinkTime = 250; StatusLEDData.bInfinite = _bInfinite; } - else if (_eSource == SDCARD_INIT) { + else if (_eSource == SDCARD_INIT) + { StatusLEDData.iSourceBlinkCnt = SDCARD_INIT; StatusLEDData.iCodeBlinkCnt = _iCode; StatusLEDData.iBlinkTime = 250; StatusLEDData.bInfinite = _bInfinite; } - else if (_eSource == SDCARD_CHECK) { + else if (_eSource == SDCARD_CHECK) + { StatusLEDData.iSourceBlinkCnt = SDCARD_CHECK; StatusLEDData.iCodeBlinkCnt = _iCode; StatusLEDData.iBlinkTime = 250; StatusLEDData.bInfinite = _bInfinite; } - else if (_eSource == CAM_INIT) { + else if (_eSource == CAM_INIT) + { StatusLEDData.iSourceBlinkCnt = CAM_INIT; StatusLEDData.iCodeBlinkCnt = _iCode; StatusLEDData.iBlinkTime = 250; StatusLEDData.bInfinite = _bInfinite; } - else if (_eSource == PSRAM_INIT) { + else if (_eSource == PSRAM_INIT) + { StatusLEDData.iSourceBlinkCnt = PSRAM_INIT; StatusLEDData.iCodeBlinkCnt = _iCode; StatusLEDData.iBlinkTime = 250; StatusLEDData.bInfinite = _bInfinite; } - else if (_eSource == TIME_CHECK) { + else if (_eSource == TIME_CHECK) + { StatusLEDData.iSourceBlinkCnt = TIME_CHECK; StatusLEDData.iCodeBlinkCnt = _iCode; StatusLEDData.iBlinkTime = 250; StatusLEDData.bInfinite = _bInfinite; } - else if (_eSource == AP_OR_OTA) { + else if (_eSource == AP_OR_OTA) + { StatusLEDData.iSourceBlinkCnt = AP_OR_OTA; StatusLEDData.iCodeBlinkCnt = _iCode; StatusLEDData.iBlinkTime = 350; StatusLEDData.bInfinite = _bInfinite; } - if (xHandle_task_StatusLED && !StatusLEDData.bProcessingRequest) { + if (xHandle_task_StatusLED && !StatusLEDData.bProcessingRequest) + { StatusLEDData.bProcessingRequest = true; - BaseType_t xReturned = xTaskAbortDelay(xHandle_task_StatusLED); // Reuse still running status LED task - /*if (xReturned == pdPASS) - ESP_LOGD(TAG, "task_StatusLED - abort waiting delay");*/ + BaseType_t xReturned = xTaskAbortDelay(xHandle_task_StatusLED); // Reuse still running status LED task + /*if (xReturned == pdPASS) + ESP_LOGD(TAG, "task_StatusLED - abort waiting delay");*/ } - else if (xHandle_task_StatusLED == NULL) { + else if (xHandle_task_StatusLED == NULL) + { StatusLEDData.bProcessingRequest = true; - BaseType_t xReturned = xTaskCreate(&task_StatusLED, "task_StatusLED", 1280, NULL, tskIDLE_PRIORITY+1, &xHandle_task_StatusLED); - if(xReturned != pdPASS) + BaseType_t xReturned = xTaskCreate(&task_status_led, "task_StatusLED", 1280, NULL, tskIDLE_PRIORITY + 1, &xHandle_task_StatusLED); + if (xReturned != pdPASS) { xHandle_task_StatusLED = NULL; LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "task_StatusLED failed to create"); - LogFile.WriteHeapInfo("task_StatusLED failed"); + LogFile.WriteHeapInfo("task_StatusLED failed"); } } - else { - ESP_LOGD(TAG, "task_StatusLED still processing, request skipped"); // Requests with high frequency could be skipped, but LED is only helpful for static states + else + { + ESP_LOGD(TAG, "task_StatusLED still processing, request skipped"); // Requests with high frequency could be skipped, but LED is only helpful for static states } - //ESP_LOGD(TAG, "StatusLED - done"); + // ESP_LOGD(TAG, "StatusLED - done"); } - -void StatusLEDOff(void) +void set_status_led_off(void) { if (xHandle_task_StatusLED) + { vTaskDelete(xHandle_task_StatusLED); // Delete task for StatusLED to force stop of blinking - - gpio_pad_select_gpio(BLINK_GPIO); // Init the GPIO + } + + gpio_pad_select_gpio(BLINK_GPIO); // Init the GPIO gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); // Set the GPIO as a push/pull output - gpio_set_level(BLINK_GPIO, 1);// LED off -} \ No newline at end of file + gpio_set_level(BLINK_GPIO, 1); // LED off +} diff --git a/code/components/jomjol_helper/statusled.h b/code/components/jomjol_helper/statusled.h index e4ea2d0c..7aaf2213 100644 --- a/code/components/jomjol_helper/statusled.h +++ b/code/components/jomjol_helper/statusled.h @@ -6,21 +6,22 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" - extern TaskHandle_t xHandle_task_StatusLED; -enum StatusLedSource { - WLAN_CONN = 1, +enum StatusLedSource +{ + WLAN_CONN = 1, WLAN_INIT = 2, SDCARD_INIT = 3, - SDCARD_CHECK = 4, + SDCARD_CHECK = 4, CAM_INIT = 5, PSRAM_INIT = 6, TIME_CHECK = 7, AP_OR_OTA = 8 }; -struct StatusLEDData { +struct StatusLEDData +{ int iSourceBlinkCnt = 1; int iCodeBlinkCnt = 1; int iBlinkTime = 250; @@ -28,7 +29,7 @@ struct StatusLEDData { bool bProcessingRequest = false; }; -void StatusLED(StatusLedSource _eSource, int _iCode, bool _bInfinite); -void StatusLEDOff(void); +void set_status_led(StatusLedSource _eSource, int _iCode, bool _bInfinite); +void set_status_led_off(void); -#endif //STATUSLED_H \ No newline at end of file +#endif // STATUSLED_H diff --git a/code/components/jomjol_image_proc/CAlignAndCutImage.cpp b/code/components/jomjol_image_proc/CAlignAndCutImage.cpp index b7f7afbd..f5886e37 100644 --- a/code/components/jomjol_image_proc/CAlignAndCutImage.cpp +++ b/code/components/jomjol_image_proc/CAlignAndCutImage.cpp @@ -1,4 +1,8 @@ +#include "defines.h" + #include "CAlignAndCutImage.h" +#include "ClassFlowAlignment.h" +#include "ClassControllCamera.h" #include "CRotateImage.h" #include "ClassLogFile.h" @@ -6,9 +10,8 @@ #include #include #include "psram.h" -#include "../../include/defines.h" -static const char* TAG = "c_align_and_cut_image"; +static const char *TAG = "c_align_and_cut_image"; CAlignAndCutImage::CAlignAndCutImage(std::string _name, CImageBasis *_org, CImageBasis *_temp) : CImageBasis(_name) { @@ -18,10 +21,8 @@ CAlignAndCutImage::CAlignAndCutImage(std::string _name, CImageBasis *_org, CImag width = _org->width; height = _org->height; bpp = _org->bpp; - externalImage = true; - - islocked = false; - + externalImage = true; + islocked = false; ImageTMP = _temp; } @@ -33,118 +34,122 @@ void CAlignAndCutImage::GetRefSize(int *ref_dx, int *ref_dy) ref_dy[1] = t1_dy; } -bool CAlignAndCutImage::Align(RefInfo *_temp1, RefInfo *_temp2) +int CAlignAndCutImage::Align(RefInfo *_temp1, RefInfo *_temp2) { - int dx, dy; - int r0_x, r0_y, r1_x, r1_y; - bool isSimilar1, isSimilar2; + CFindTemplate *ft = new CFindTemplate("align", rgb_image, channels, width, height, bpp); - CFindTemplate* ft = new CFindTemplate("align", rgb_image, channels, width, height, bpp); + ////////////////////////////////////////////// + bool isSimilar1 = ft->FindTemplate(_temp1); // search the alignment image 1 - r0_x = _temp1->target_x; - r0_y = _temp1->target_y; - ESP_LOGD(TAG, "Before ft->FindTemplate(_temp1); %s", _temp1->image_file.c_str()); - isSimilar1 = ft->FindTemplate(_temp1); _temp1->width = ft->tpl_width; - _temp1->height = ft->tpl_height; + _temp1->height = ft->tpl_height; + + int x1_relative_shift = (_temp1->target_x - _temp1->found_x); + int y1_relative_shift = (_temp1->target_y - _temp1->found_y); + + int x1_absolute_shift = _temp1->target_x + (_temp1->target_x - _temp1->found_x); + int y1_absolute_shift = _temp1->target_y + (_temp1->target_y - _temp1->found_y); + + ////////////////////////////////////////////// + bool isSimilar2 = ft->FindTemplate(_temp2); // search the alignment image 2 - r1_x = _temp2->target_x; - r1_y = _temp2->target_y; - ESP_LOGD(TAG, "Before ft->FindTemplate(_temp2); %s", _temp2->image_file.c_str()); - isSimilar2 = ft->FindTemplate(_temp2); _temp2->width = ft->tpl_width; - _temp2->height = ft->tpl_height; + _temp2->height = ft->tpl_height; + + int x2_relative_shift = (_temp2->target_x - _temp2->found_x); + int y2_relative_shift = (_temp2->target_y - _temp2->found_y); + + int x2_absolute_shift = _temp2->target_x + (_temp2->target_x - _temp2->found_x); + int y2_absolute_shift = _temp2->target_y + (_temp2->target_y - _temp2->found_y); delete ft; + int ret = Alignment_OK; - dx = _temp1->target_x - _temp1->found_x; - dy = _temp1->target_y - _temp1->found_y; + ////////////////////////////////////////////// + float radians_org = atan2((_temp2->found_y - _temp1->found_y), (_temp2->found_x - _temp1->found_x)); + float radians_cur = atan2((y2_absolute_shift - y1_absolute_shift), (x2_absolute_shift - x1_absolute_shift)); + float rotate_angle = (radians_cur - radians_org) * 180 / M_PI; // radians to degrees - r0_x += dx; - r0_y += dy; + ////////////////////////////////////////////// + if ((fabs(rotate_angle) > _temp1->search_max_angle) || (fabs(rotate_angle) > _temp2->search_max_angle)) + { + ret = Rotation_Alignment_Failed; + } - r1_x += dx; - r1_y += dy; - - float w_org, w_ist, d_winkel; - - w_org = atan2(_temp2->found_y - _temp1->found_y, _temp2->found_x - _temp1->found_x); - w_ist = atan2(r1_y - r0_y, r1_x - r0_x); - - d_winkel = (w_ist - w_org) * 180 / M_PI; - -/*#ifdef DEBUG_DETAIL_ON - std::string zw = "\tdx:\t" + std::to_string(dx) + "\tdy:\t" + std::to_string(dy) + "\td_winkel:\t" + std::to_string(d_winkel); - zw = zw + "\tt1_x_y:\t" + std::to_string(_temp1->found_x) + "\t" + std::to_string(_temp1->found_y); - zw = zw + "\tpara1_found_min_avg_max_SAD:\t" + std::to_string(_temp1->fastalg_min) + "\t" + std::to_string(_temp1->fastalg_avg) + "\t" + std::to_string(_temp1->fastalg_max) + "\t"+ std::to_string(_temp1->fastalg_SAD); - zw = zw + "\tt2_x_y:\t" + std::to_string(_temp2->found_x) + "\t" + std::to_string(_temp2->found_y); - zw = zw + "\tpara2_found_min_avg_max:\t" + std::to_string(_temp2->fastalg_min) + "\t" + std::to_string(_temp2->fastalg_avg) + "\t" + std::to_string(_temp2->fastalg_max) + "\t"+ std::to_string(_temp2->fastalg_SAD); - LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", zw); -#endif*/ + if ((abs(x1_relative_shift) >= _temp1->search_x) || (abs(y1_relative_shift) >= _temp1->search_y)) + { + ret = Shift_Alignment_Failed; + } + ////////////////////////////////////////////// CRotateImage rt("Align", this, ImageTMP); - rt.Translate(dx, dy); - rt.Rotate(d_winkel, _temp1->target_x, _temp1->target_y); - ESP_LOGD(TAG, "Alignment: dx %d - dy %d - rot %f", dx, dy, d_winkel); - return (isSimilar1 && isSimilar2); + if (rotate_angle != 0) + { + rt.Translate(x1_relative_shift, y1_relative_shift); + + if (Camera.ImageAntialiasing) + { + rt.RotateAntiAliasing(rotate_angle, _temp1->target_x, _temp1->target_y); + } + else + { + rt.Rotate(rotate_angle, _temp1->target_x, _temp1->target_y); + } + } + else if (x1_relative_shift != 0 || y1_relative_shift != 0) + { + rt.Translate(x1_relative_shift, y1_relative_shift); + } + + return ((isSimilar1 && isSimilar2) ? Fast_Alignment_OK : ret); } - - - - void CAlignAndCutImage::CutAndSave(std::string _template1, int x1, int y1, int dx, int dy) { - - int x2, y2; - - x2 = x1 + dx; - y2 = y1 + dy; - x2 = std::min(x2, width - 1); - y2 = std::min(y2, height - 1); + int x2 = std::min((x1 + dx), (width - 1)); + int y2 = std::min((y1 + dy), (height - 1)); dx = x2 - x1; dy = y2 - y1; int memsize = dx * dy * channels; - uint8_t* odata = (unsigned char*) malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM); + uint8_t *temp_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->temp_image", memsize, MALLOC_CAP_SPIRAM); - stbi_uc* p_target; - stbi_uc* p_source; + stbi_uc *p_target; + stbi_uc *p_source; RGBImageLock(); for (int x = x1; x < x2; ++x) + { for (int y = y1; y < y2; ++y) { - p_target = odata + (channels * ((y - y1) * dx + (x - x1))); + p_target = temp_image + (channels * ((y - y1) * dx + (x - x1))); p_source = rgb_image + (channels * (y * width + x)); + for (int _channels = 0; _channels < channels; ++_channels) + { p_target[_channels] = p_source[_channels]; + } } + } #ifdef STBI_ONLY_JPEG - stbi_write_jpg(_template1.c_str(), dx, dy, channels, odata, 100); + stbi_write_jpg(_template1.c_str(), dx, dy, channels, temp_image, 100); #else - stbi_write_bmp(_template1.c_str(), dx, dy, channels, odata); + stbi_write_bmp(_template1.c_str(), dx, dy, channels, temp_image); #endif - RGBImageRelease(); - - stbi_image_free(odata); + stbi_image_free(temp_image); } void CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy, CImageBasis *_target) { - int x2, y2; - - x2 = x1 + dx; - y2 = y1 + dy; - x2 = std::min(x2, width - 1); - y2 = std::min(y2, height - 1); + int x2 = std::min((x1 + dx), (width - 1)); + int y2 = std::min((y1 + dy), (height - 1)); dx = x2 - x1; dy = y2 - y1; @@ -155,57 +160,63 @@ void CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy, CImageBasis * return; } - uint8_t* odata = _target->RGBImageLock(); + uint8_t *temp_image = _target->RGBImageLock(); RGBImageLock(); - stbi_uc* p_target; - stbi_uc* p_source; + stbi_uc *p_target; + stbi_uc *p_source; for (int x = x1; x < x2; ++x) + { for (int y = y1; y < y2; ++y) { - p_target = odata + (channels * ((y - y1) * dx + (x - x1))); + p_target = temp_image + (channels * ((y - y1) * dx + (x - x1))); p_source = rgb_image + (channels * (y * width + x)); + for (int _channels = 0; _channels < channels; ++_channels) + { p_target[_channels] = p_source[_channels]; + } } + } RGBImageRelease(); _target->RGBImageRelease(); } - -CImageBasis* CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy) +CImageBasis *CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy) { - int x2, y2; - - x2 = x1 + dx; - y2 = y1 + dy; - x2 = std::min(x2, width - 1); - y2 = std::min(y2, height - 1); + int x2 = std::min((x1 + dx), (width - 1)); + int y2 = std::min((y1 + dy), (height - 1)); dx = x2 - x1; dy = y2 - y1; int memsize = dx * dy * channels; - uint8_t* odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM); + uint8_t *temp_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->temp_image", memsize, MALLOC_CAP_SPIRAM); - stbi_uc* p_target; - stbi_uc* p_source; + stbi_uc *p_target; + stbi_uc *p_source; RGBImageLock(); for (int x = x1; x < x2; ++x) + { for (int y = y1; y < y2; ++y) { - p_target = odata + (channels * ((y - y1) * dx + (x - x1))); + p_target = temp_image + (channels * ((y - y1) * dx + (x - x1))); p_source = rgb_image + (channels * (y * width + x)); + for (int _channels = 0; _channels < channels; ++_channels) + { p_target[_channels] = p_source[_channels]; + } } + } - CImageBasis* rs = new CImageBasis("CutAndSave", odata, channels, dx, dy, bpp); + CImageBasis *rs = new CImageBasis("CutAndSave", temp_image, channels, dx, dy, bpp); RGBImageRelease(); rs->SetIndepended(); + return rs; } diff --git a/code/components/jomjol_image_proc/CAlignAndCutImage.h b/code/components/jomjol_image_proc/CAlignAndCutImage.h index 46f5016e..94201279 100644 --- a/code/components/jomjol_image_proc/CAlignAndCutImage.h +++ b/code/components/jomjol_image_proc/CAlignAndCutImage.h @@ -6,22 +6,20 @@ #include "CImageBasis.h" #include "CFindTemplate.h" - class CAlignAndCutImage : public CImageBasis { - public: +public: int t0_dx, t0_dy, t1_dx, t1_dy; CImageBasis *ImageTMP; - CAlignAndCutImage(std::string name, std::string _image) : CImageBasis(name, _image) {ImageTMP = NULL;}; - CAlignAndCutImage(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL;}; + CAlignAndCutImage(std::string name, std::string _image) : CImageBasis(name, _image) { ImageTMP = NULL; }; + CAlignAndCutImage(std::string name, uint8_t *_rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) { ImageTMP = NULL; }; CAlignAndCutImage(std::string name, CImageBasis *_org, CImageBasis *_temp); - bool Align(RefInfo *_temp1, RefInfo *_temp2); -// void Align(std::string _template1, int x1, int y1, std::string _template2, int x2, int y2, int deltax = 40, int deltay = 40, std::string imageROI = ""); + int Align(RefInfo *_temp1, RefInfo *_temp2); void CutAndSave(std::string _template1, int x1, int y1, int dx, int dy); - CImageBasis* CutAndSave(int x1, int y1, int dx, int dy); + CImageBasis *CutAndSave(int x1, int y1, int dx, int dy); void CutAndSave(int x1, int y1, int dx, int dy, CImageBasis *_target); void GetRefSize(int *ref_dx, int *ref_dy); }; -#endif //CALIGNANDCUTIMAGE_H \ No newline at end of file +#endif // CALIGNANDCUTIMAGE_H diff --git a/code/components/jomjol_image_proc/CFindTemplate.cpp b/code/components/jomjol_image_proc/CFindTemplate.cpp index b6a90ccd..d81d2915 100644 --- a/code/components/jomjol_image_proc/CFindTemplate.cpp +++ b/code/components/jomjol_image_proc/CFindTemplate.cpp @@ -1,37 +1,30 @@ +#include "defines.h" + #include "CFindTemplate.h" #include "ClassLogFile.h" #include "Helper.h" -#include "../../include/defines.h" #include -static const char* TAG = "C FIND TEMPL"; - -// #define DEBUG_DETAIL_ON - +static const char *TAG = "C FIND TEMPL"; bool CFindTemplate::FindTemplate(RefInfo *_ref) { - uint8_t* rgb_template; - - if (file_size(_ref->image_file.c_str()) == 0) { + if (file_size(_ref->image_file.c_str()) == 0) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, _ref->image_file + " is empty!"); return false; } - - rgb_template = stbi_load(_ref->image_file.c_str(), &tpl_width, &tpl_height, &tpl_bpp, channels); - if (rgb_template == NULL) { + uint8_t *rgb_template = stbi_load(_ref->image_file.c_str(), &tpl_width, &tpl_height, &tpl_bpp, channels); + if (rgb_template == NULL) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to load " + _ref->image_file + "! Is it corrupted?"); return false; } -// ESP_LOGD(TAG, "FindTemplate 01"); - - int ow, ow_start, ow_stop; - int oh, oh_start, oh_stop; - + // ESP_LOGD(TAG, "FindTemplate 01"); if (_ref->search_x == 0) { _ref->search_x = width; @@ -44,148 +37,158 @@ bool CFindTemplate::FindTemplate(RefInfo *_ref) _ref->found_y = 0; } + int x_search_area_start = std::max((_ref->target_x - _ref->search_x), 0); + int x_search_area_stop = _ref->target_x + _ref->search_x; - ow_start = _ref->target_x - _ref->search_x; - ow_start = std::max(ow_start, 0); - ow_stop = _ref->target_x + _ref->search_x; - if ((ow_stop + tpl_width) > width) - ow_stop = width - tpl_width; - ow = ow_stop - ow_start + 1; - - oh_start = _ref->target_y - _ref->search_y; - oh_start = std::max(oh_start, 0); - oh_stop = _ref->target_y + _ref->search_y; - if ((oh_stop + tpl_height) > height) - oh_stop = height - tpl_height; - oh = oh_stop - oh_start + 1; - - float avg, SAD; - int min, max; - bool isSimilar = false; - -// ESP_LOGD(TAG, "FindTemplate 02"); - - if ((_ref->alignment_algo == 2) && (_ref->fastalg_x > -1) && (_ref->fastalg_y > -1)) // für Testzwecke immer Berechnen + if ((x_search_area_stop + tpl_width) > width) { - isSimilar = CalculateSimularities(rgb_template, _ref->fastalg_x, _ref->fastalg_y, ow, oh, min, avg, max, SAD, _ref->fastalg_SAD, _ref->fastalg_SAD_criteria); -/*#ifdef DEBUG_DETAIL_ON - std::string zw = "\t" + _ref->image_file + "\tt1_x_y:\t" + std::to_string(_ref->fastalg_x) + "\t" + std::to_string(_ref->fastalg_y); - zw = zw + "\tpara1_found_min_avg_max_SAD:\t" + std::to_string(min) + "\t" + std::to_string(avg) + "\t" + std::to_string(max) + "\t"+ std::to_string(SAD); - LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", zw); -#endif*/ + x_search_area_stop = width - tpl_width; } -// ESP_LOGD(TAG, "FindTemplate 03"); + int x_search_area = x_search_area_stop - x_search_area_start + 1; + int y_search_area_start = std::max((_ref->target_y - _ref->search_y), 0); + int y_search_area_stop = _ref->target_y + _ref->search_y; + if ((y_search_area_stop + tpl_height) > height) + { + y_search_area_stop = height - tpl_height; + } + + int y_search_area = y_search_area_stop - y_search_area_start + 1; + + float avg = 0, SAD = 0; + int min = 0, max = 0; + bool isSimilar = false; + + // ESP_LOGD(TAG, "FindTemplate 02"); + if ((_ref->alignment_algo == 2) && (_ref->fastalg_x > -1) && (_ref->fastalg_y > -1)) + { + isSimilar = CalculateSimularities(rgb_template, _ref->fastalg_x, _ref->fastalg_y, x_search_area, y_search_area, min, avg, max, SAD, _ref->fastalg_SAD, _ref->fastalg_SAD_criteria); + } + + // ESP_LOGD(TAG, "FindTemplate 03"); if (isSimilar) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Use FastAlignment sucessfull"); -#endif _ref->found_x = _ref->fastalg_x; _ref->found_y = _ref->fastalg_y; stbi_image_free(rgb_template); - + return true; } -// ESP_LOGD(TAG, "FindTemplate 04"); - - - double aktSAD; + // ESP_LOGD(TAG, "FindTemplate 04"); double minSAD = pow(tpl_width * tpl_height * 255, 2); RGBImageLock(); -// ESP_LOGD(TAG, "FindTemplate 05"); - int xouter, youter, tpl_x, tpl_y, _ch; + // ESP_LOGD(TAG, "FindTemplate 05"); int _anzchannels = channels; - if (_ref->alignment_algo == 0) // 0 = "Default" (nur R-Kanal) - _anzchannels = 1; - for (xouter = ow_start; xouter <= ow_stop; xouter++) - for (youter = oh_start; youter <= oh_stop; ++youter) + if (_ref->alignment_algo == 0) + { + // 0 = "Default" (nur R-Kanal) + _anzchannels = 1; + } + + for (int x_outer = x_search_area_start; x_outer <= x_search_area_stop; x_outer++) + { + for (int y_outer = y_search_area_start; y_outer <= y_search_area_stop; ++y_outer) { - aktSAD = 0; - for (tpl_x = 0; tpl_x < tpl_width; tpl_x++) - for (tpl_y = 0; tpl_y < tpl_height; tpl_y++) + double aktSAD = 0; + + for (int tpl_x = 0; tpl_x < tpl_width; tpl_x++) + { + for (int tpl_y = 0; tpl_y < tpl_height; tpl_y++) { - stbi_uc* p_org = rgb_image + (channels * ((youter + tpl_y) * width + (xouter + tpl_x))); - stbi_uc* p_tpl = rgb_template + (channels * (tpl_y * tpl_width + tpl_x)); - for (_ch = 0; _ch < _anzchannels; ++_ch) + stbi_uc *p_org = rgb_image + (channels * ((y_outer + tpl_y) * width + (x_outer + tpl_x))); + stbi_uc *p_tpl = rgb_template + (channels * (tpl_y * tpl_width + tpl_x)); + + for (int tpl_ch = 0; tpl_ch < _anzchannels; ++tpl_ch) { - aktSAD += pow(p_tpl[_ch] - p_org[_ch], 2); + aktSAD += pow(p_tpl[tpl_ch] - p_org[tpl_ch], 2); } } + } + if (aktSAD < minSAD) { minSAD = aktSAD; - _ref->found_x = xouter; - _ref->found_y = youter; + + _ref->found_x = x_outer; + _ref->found_y = y_outer; } } + } -// ESP_LOGD(TAG, "FindTemplate 06"); - - + // ESP_LOGD(TAG, "FindTemplate 06"); if (_ref->alignment_algo == 2) - CalculateSimularities(rgb_template, _ref->found_x, _ref->found_y, ow, oh, min, avg, max, SAD, _ref->fastalg_SAD, _ref->fastalg_SAD_criteria); + { + isSimilar = CalculateSimularities(rgb_template, _ref->found_x, _ref->found_y, x_search_area, y_search_area, min, avg, max, SAD, _ref->fastalg_SAD, _ref->fastalg_SAD_criteria); + } + // ESP_LOGD(TAG, "FindTemplate 07"); + if (isSimilar) + { + _ref->fastalg_x = _ref->found_x; + _ref->fastalg_y = _ref->found_y; -// ESP_LOGD(TAG, "FindTemplate 07"); + _ref->fastalg_min = min; + _ref->fastalg_max = max; - _ref->fastalg_x = _ref->found_x; - _ref->fastalg_y = _ref->found_y; - _ref->fastalg_min = min; - _ref->fastalg_avg = avg; - _ref->fastalg_max = max; - _ref->fastalg_SAD = SAD; + _ref->fastalg_avg = avg; + _ref->fastalg_SAD = SAD; - -/*#ifdef DEBUG_DETAIL_ON - std::string zw = "\t" + _ref->image_file + "\tt1_x_y:\t" + std::to_string(_ref->fastalg_x) + "\t" + std::to_string(_ref->fastalg_y); - zw = zw + "\tpara1_found_min_avg_max_SAD:\t" + std::to_string(min) + "\t" + std::to_string(avg) + "\t" + std::to_string(max) + "\t"+ std::to_string(SAD); - LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", zw); -#endif*/ + RGBImageRelease(); + stbi_image_free(rgb_template); + + return true; + } RGBImageRelease(); stbi_image_free(rgb_template); - -// ESP_LOGD(TAG, "FindTemplate 08"); + // ESP_LOGD(TAG, "FindTemplate 09"); return false; } - - -bool CFindTemplate::CalculateSimularities(uint8_t* _rgb_tmpl, int _startx, int _starty, int _sizex, int _sizey, int &min, float &avg, int &max, float &SAD, float _SADold, float _SADcrit) +bool CFindTemplate::CalculateSimularities(uint8_t *_rgb_tmpl, int _startx, int _starty, int _sizex, int _sizey, int &min, float &avg, int &max, float &SAD, float _SADold, float _SADcrit) { - int dif; + int dif = 0; int minDif = 255; int maxDif = -255; double avgDifSum = 0; long int anz = 0; - double aktSAD = 0; + double aktSAD = 0; - int xouter, youter, _ch; - - for (xouter = 0; xouter <= _sizex; xouter++) - for (youter = 0; youter <= _sizey; ++youter) + for (int x_outer = 0; x_outer <= _sizex; x_outer++) + { + for (int y_outer = 0; y_outer <= _sizey; ++y_outer) { - stbi_uc* p_org = rgb_image + (channels * ((youter + _starty) * width + (xouter + _startx))); - stbi_uc* p_tpl = _rgb_tmpl + (channels * (youter * tpl_width + xouter)); - for (_ch = 0; _ch < channels; ++_ch) + stbi_uc *p_org = rgb_image + (channels * ((y_outer + _starty) * width + (x_outer + _startx))); + stbi_uc *p_tpl = _rgb_tmpl + (channels * (y_outer * tpl_width + x_outer)); + + for (int _ch = 0; _ch < channels; ++_ch) { dif = p_tpl[_ch] - p_org[_ch]; - aktSAD += pow(p_tpl[_ch] - p_org[_ch], 2); - if (dif < minDif) minDif = dif; - if (dif > maxDif) maxDif = dif; + aktSAD += pow(p_tpl[_ch] - p_org[_ch], 2); + + if (dif < minDif) + { + minDif = dif; + } + + if (dif > maxDif) + { + maxDif = dif; + } + avgDifSum += dif; anz++; } } + } avg = avgDifSum / anz; min = minDif; @@ -197,10 +200,9 @@ bool CFindTemplate::CalculateSimularities(uint8_t* _rgb_tmpl, int _startx, int _ ESP_LOGD(TAG, "Anzahl %ld, avgDifSum %fd, avg %f, SAD_neu: %fd, _SAD_old: %f, _SAD_crit:%f", anz, avgDifSum, avg, SAD, _SADold, _SADdif); if (_SADdif <= _SADcrit) + { return true; + } return false; } - - - diff --git a/code/components/jomjol_image_proc/CFindTemplate.h b/code/components/jomjol_image_proc/CFindTemplate.h index 14b0261b..d92529ac 100644 --- a/code/components/jomjol_image_proc/CFindTemplate.h +++ b/code/components/jomjol_image_proc/CFindTemplate.h @@ -5,16 +5,18 @@ #include "CImageBasis.h" -struct RefInfo { - std::string image_file; - int target_x = 0; - int target_y = 0; - int width = 0; - int height = 0; +struct RefInfo +{ + std::string image_file; + int target_x = 0; // X-coordinate of the alignment image + int target_y = 0; // Y-coordinate of the alignment image + int width = 0; // Width of the alignment image + int height = 0; // Height of the alignment image + int search_x; // X-size (width) in which the reference is searched + int search_y; // Y-size (height) in which the reference is searched + float search_max_angle; // Max rotation angle in which the reference is searched int found_x; int found_y; - int search_x; - int search_y; int fastalg_x = -1; int fastalg_y = -1; int fastalg_min = -256; @@ -22,21 +24,23 @@ struct RefInfo { int fastalg_max = -1; float fastalg_SAD = -1; float fastalg_SAD_criteria = -1; - int alignment_algo = 0; // 0 = "Default" (nur R-Kanal), 1 = "HighAccuracy" (RGB-Kanal), 2 = "Fast" (1.x RGB, dann isSimilar) + int alignment_algo = 0; // 0 = "Default" (nur R-Kanal), 1 = "HighAccuracy" (RGB-Kanal), 2 = "Fast" (1.x RGB, dann isSimilar) }; - - - class CFindTemplate : public CImageBasis { - public: - int tpl_width, tpl_height, tpl_bpp; - CFindTemplate(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {}; +public: + int tpl_width, tpl_height, tpl_bpp; - bool FindTemplate(RefInfo *_ref); + #define Fast_Alignment_OK 1 + #define Alignment_OK 0 + #define Alignment_Failed -1 + #define Rotation_Alignment_Failed -2 + #define Shift_Alignment_Failed -3 - bool CalculateSimularities(uint8_t* _rgb_tmpl, int _startx, int _starty, int _sizex, int _sizey, int &min, float &avg, int &max, float &SAD, float _SADold, float _SADcrit); + CFindTemplate(std::string name, uint8_t *_rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {}; + bool FindTemplate(RefInfo *_ref); + bool CalculateSimularities(uint8_t *_rgb_tmpl, int _startx, int _starty, int _sizex, int _sizey, int &min, float &avg, int &max, float &SAD, float _SADold, float _SADcrit); }; -#endif //CFINDTEMPLATE_H \ No newline at end of file +#endif // CFINDTEMPLATE_H diff --git a/code/components/jomjol_image_proc/CImageBasis.cpp b/code/components/jomjol_image_proc/CImageBasis.cpp index 8038b01e..37cc9834 100644 --- a/code/components/jomjol_image_proc/CImageBasis.cpp +++ b/code/components/jomjol_image_proc/CImageBasis.cpp @@ -1,3 +1,5 @@ +#include "defines.h" + #include "CImageBasis.h" #include "Helper.h" #include "psram.h" @@ -5,116 +7,111 @@ #include "server_ota.h" #include -#include "../../include/defines.h" #include "esp_system.h" #include - #include #include - using namespace std; static const char *TAG = "C IMG BASIS"; -bool jpgFileTooLarge = false; // JPG creation verfication +bool jpgFileTooLarge = false; // JPG creation verfication - -//#define DEBUG_DETAIL_ON - - -uint8_t * CImageBasis::RGBImageLock(int _waitmaxsec) +uint8_t *CImageBasis::RGBImageLock(int _waitmaxsec) { if (islocked) { - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Image is locked: sleep for: %ds", _waitmaxsec); - #endif TickType_t xDelay; xDelay = 1000 / portTICK_PERIOD_MS; for (int i = 0; i <= _waitmaxsec; ++i) { - vTaskDelay( xDelay ); + vTaskDelay(xDelay); if (!islocked) + { break; + } } } if (islocked) + { return NULL; + } return rgb_image; } - void CImageBasis::RGBImageRelease() { islocked = false; } - -uint8_t * CImageBasis::RGBImageGet() +uint8_t *CImageBasis::RGBImageGet() { return rgb_image; } - void writejpghelp(void *context, void *data, int size) { -// ESP_LOGD(TAG, "Size all: %d, size %d", ((ImageData*)context)->size, size); - ImageData* _zw = (ImageData*) context; + // ESP_LOGD(TAG, "Size all: %d, size %d", ((ImageData*)context)->size, size); + ImageData *_zw = (ImageData *)context; uint8_t *voidstart = _zw->data; - uint8_t *datastart = (uint8_t*) data; - - if ((_zw->size < MAX_JPG_SIZE)) { // Abort copy to prevent buffer overflow + uint8_t *datastart = (uint8_t *)data; + + if ((_zw->size < MAX_JPG_SIZE)) + { + // Abort copy to prevent buffer overflow voidstart += _zw->size; for (int i = 0; i < size; ++i) + { *(voidstart + i) = *(datastart + i); + } _zw->size += size; } - else { + else + { jpgFileTooLarge = true; } } - -ImageData* CImageBasis::writeToMemoryAsJPG(const int quality) +ImageData *CImageBasis::writeToMemoryAsJPG(const int quality) { - ImageData* ii = new ImageData; + ImageData *ii = new ImageData; RGBImageLock(); stbi_write_jpg_to_func(writejpghelp, ii, width, height, channels, rgb_image, quality); RGBImageRelease(); - if (jpgFileTooLarge) { + if (jpgFileTooLarge) + { jpgFileTooLarge = false; LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "writeToMemoryAsJPG: Creation aborted! JPG size > preallocated buffer: " + std::to_string(MAX_JPG_SIZE)); } return ii; } - -void CImageBasis::writeToMemoryAsJPG(ImageData* i, const int quality) +void CImageBasis::writeToMemoryAsJPG(ImageData *i, const int quality) { - ImageData* ii = new ImageData; - + ImageData *ii = new ImageData; + RGBImageLock(); stbi_write_jpg_to_func(writejpghelp, ii, width, height, channels, rgb_image, quality); RGBImageRelease(); - if (jpgFileTooLarge) { + if (jpgFileTooLarge) + { jpgFileTooLarge = false; LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "writeToMemoryAsJPG: Creation aborted! JPG size > preallocated buffer: " + std::to_string(MAX_JPG_SIZE)); } - memCopy((uint8_t*) ii, (uint8_t*) i, sizeof(ImageData)); + memCopy((uint8_t *)ii, (uint8_t *)i, sizeof(ImageData)); delete ii; } - struct SendJPGHTTP { httpd_req_t *req; @@ -123,23 +120,23 @@ struct SendJPGHTTP int size = 0; }; - inline void writejpgtohttphelp(void *context, void *data, int size) { - SendJPGHTTP* _send = (SendJPGHTTP*) context; - if ((_send->size + size) >= HTTP_BUFFER_SENT) // data no longer fits in buffer - { - if (httpd_resp_send_chunk(_send->req, _send->buf, _send->size) != ESP_OK) - { - ESP_LOGE(TAG, "File sending failed!"); - _send->res = ESP_FAIL; - } - _send->size = 0; - } - std::memcpy((void*) (&(_send->buf[0]) + _send->size), data, size); - _send->size+= size; -} + SendJPGHTTP *_send = (SendJPGHTTP *)context; + // data no longer fits in buffer + if ((_send->size + size) >= HTTP_BUFFER_SENT) + { + if (httpd_resp_send_chunk(_send->req, _send->buf, _send->size) != ESP_OK) + { + ESP_LOGE(TAG, "File sending failed!"); + _send->res = ESP_FAIL; + } + _send->size = 0; + } + std::memcpy((void *)(&(_send->buf[0]) + _send->size), data, size); + _send->size += size; +} esp_err_t CImageBasis::SendJPGtoHTTP(httpd_req_t *_req, const int quality) { @@ -153,23 +150,24 @@ esp_err_t CImageBasis::SendJPGtoHTTP(httpd_req_t *_req, const int quality) if (ii.size > 0) { - if (httpd_resp_send_chunk(_req, (char*) ii.buf, ii.size) != ESP_OK) //still send the rest + // still send the rest + if (httpd_resp_send_chunk(_req, (char *)ii.buf, ii.size) != ESP_OK) { ESP_LOGE(TAG, "File sending failed!"); - ii.res = ESP_FAIL; + ii.res = ESP_FAIL; } } RGBImageRelease(); return ii.res; -} +} - -bool CImageBasis::CopyFromMemory(uint8_t* _source, int _size) +bool CImageBasis::CopyFromMemory(uint8_t *_source, int _size) { int gr = height * width * channels; - if (gr != _size) // Size does not fit + // Size does not fit + if (gr != _size) { ESP_LOGE(TAG, "Cannot copy image from memory - sizes do not match: should be %d, but is %d", _size, gr); return false; @@ -182,46 +180,48 @@ bool CImageBasis::CopyFromMemory(uint8_t* _source, int _size) return true; } - uint8_t CImageBasis::GetPixelColor(int x, int y, int ch) { - stbi_uc* p_source; + stbi_uc *p_source; p_source = rgb_image + (channels * (y * width + x)); return p_source[ch]; } - -void CImageBasis::memCopy(uint8_t* _source, uint8_t* _target, int _size) +void CImageBasis::memCopy(uint8_t *_source, uint8_t *_target, int _size) { -#ifdef _ESP32_PSRAM +#if CONFIG_SPIRAM for (int i = 0; i < _size; ++i) + { *(_target + i) = *(_source + i); + } #else memcpy(_target, _source, _size); #endif } - bool CImageBasis::isInImage(int x, int y) { if ((x < 0) || (x > width - 1)) + { return false; + } - if ((y < 0) || (y > height- 1)) + if ((y < 0) || (y > height - 1)) + { return false; + } return true; } - void CImageBasis::setPixelColor(int x, int y, int r, int g, int b) { - stbi_uc* p_source; + stbi_uc *p_source; RGBImageLock(); p_source = rgb_image + (channels * (y * width + x)); p_source[0] = r; - if ( channels > 2) + if (channels > 2) { p_source[1] = g; p_source[2] = b; @@ -229,7 +229,6 @@ void CImageBasis::setPixelColor(int x, int y, int r, int g, int b) RGBImageRelease(); } - void CImageBasis::drawRect(int x, int y, int dx, int dy, int r, int g, int b, int thickness) { int zwx1, zwx2, zwy1, zwy2; @@ -243,54 +242,86 @@ void CImageBasis::drawRect(int x, int y, int dx, int dy, int r, int g, int b, in RGBImageLock(); for (_thick = 0; _thick < thickness; _thick++) + { for (_x = zwx1; _x <= zwx2; ++_x) + { for (_y = zwy1; _y <= zwy2; _y++) + { if (isInImage(_x, _y)) + { setPixelColor(_x, _y - _thick, r, g, b); + } + } + } + } zwx1 = x - thickness + 1; zwx2 = x + dx + thickness - 1; zwy1 = y + dy; zwy2 = y + dy; for (_thick = 0; _thick < thickness; _thick++) + { for (_x = zwx1; _x <= zwx2; ++_x) + { for (_y = zwy1; _y <= zwy2; _y++) + { if (isInImage(_x, _y)) + { setPixelColor(_x, _y + _thick, r, g, b); + } + } + } + } zwx1 = x; zwx2 = x; zwy1 = y; zwy2 = y + dy; for (_thick = 0; _thick < thickness; _thick++) + { for (_x = zwx1; _x <= zwx2; ++_x) + { for (_y = zwy1; _y <= zwy2; _y++) + { if (isInImage(_x, _y)) + { setPixelColor(_x - _thick, _y, r, g, b); + } + } + } + } zwx1 = x + dx; zwx2 = x + dx; zwy1 = y; zwy2 = y + dy; for (_thick = 0; _thick < thickness; _thick++) + { for (_x = zwx1; _x <= zwx2; ++_x) + { for (_y = zwy1; _y <= zwy2; _y++) + { if (isInImage(_x, _y)) + { setPixelColor(_x + _thick, _y, r, g, b); + } + } + } + } RGBImageRelease(); } - void CImageBasis::drawLine(int x1, int y1, int x2, int y2, int r, int g, int b, int thickness) { int _x, _y, _thick; int _zwy1, _zwy2; - thickness = (thickness-1) / 2; + thickness = (thickness - 1) / 2; RGBImageLock(); for (_thick = 0; _thick <= thickness; ++_thick) + { for (_x = x1 - _thick; _x <= x2 + _thick; ++_x) { if (x2 == x1) @@ -305,14 +336,18 @@ void CImageBasis::drawLine(int x1, int y1, int x2, int y2, int r, int g, int b, } for (_y = _zwy1 - _thick; _y <= _zwy2 + _thick; _y++) + { if (isInImage(_x, _y)) + { setPixelColor(_x, _y, r, g, b); + } + } } - + } + RGBImageRelease(); } - void CImageBasis::drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int b, int thickness) { float deltarad, aktrad; @@ -320,25 +355,30 @@ void CImageBasis::drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int rad = radx; if (rady > radx) + { rad = rady; + } deltarad = 1 / (4 * M_PI * (rad + thickness - 1)); RGBImageLock(); for (aktrad = 0; aktrad <= (2 * M_PI); aktrad += deltarad) + { for (_thick = 0; _thick < thickness; ++_thick) { _x = sin(aktrad) * (radx + _thick) + x1; _y = cos(aktrad) * (rady + _thick) + y1; if (isInImage(_x, _y)) + { setPixelColor(_x, _y, r, g, b); + } } + } RGBImageRelease(); } - void CImageBasis::drawCircle(int x1, int y1, int rad, int r, int g, int b, int thickness) { float deltarad, aktrad; @@ -349,18 +389,21 @@ void CImageBasis::drawCircle(int x1, int y1, int rad, int r, int g, int b, int t RGBImageLock(); for (aktrad = 0; aktrad <= (2 * M_PI); aktrad += deltarad) + { for (_thick = 0; _thick < thickness; ++_thick) { _x = sin(aktrad) * (rad + _thick) + x1; _y = cos(aktrad) * (rad + _thick) + y1; if (isInImage(_x, _y)) + { setPixelColor(_x, _y, r, g, b); + } } + } RGBImageRelease(); } - CImageBasis::CImageBasis(string _name) { name = _name; @@ -368,11 +411,10 @@ CImageBasis::CImageBasis(string _name) rgb_image = NULL; width = 0; height = 0; - channels = 0; + channels = 0; islocked = false; } - void CImageBasis::CreateEmptyImage(int _width, int _height, int _channels) { bpp = _channels; @@ -382,13 +424,16 @@ void CImageBasis::CreateEmptyImage(int _width, int _height, int _channels) RGBImageLock(); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CreateEmptyImage"); - #endif - memsize = width * height * channels; - rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM); + if (name == "TempImage") + { + rgb_image = (unsigned char *)psram_reserve_shared_tmp_image_memory(); + } + else + { + rgb_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM); + } if (rgb_image == NULL) { @@ -398,59 +443,61 @@ void CImageBasis::CreateEmptyImage(int _width, int _height, int _channels) return; } - stbi_uc* p_source; + stbi_uc *p_source; for (int x = 0; x < width; ++x) + { for (int y = 0; y < height; ++y) { p_source = rgb_image + (channels * (y * width + x)); for (int _channels = 0; _channels < channels; ++_channels) - p_source[_channels] = (uint8_t) 0; + { + p_source[_channels] = (uint8_t)0; + } } + } RGBImageRelease(); } - void CImageBasis::EmptyImage() { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("EmptyImage"); - #endif - - stbi_uc* p_source; + stbi_uc *p_source; RGBImageLock(); for (int x = 0; x < width; ++x) + { for (int y = 0; y < height; ++y) { p_source = rgb_image + (channels * (y * width + x)); for (int _channels = 0; _channels < channels; ++_channels) - p_source[_channels] = (uint8_t) 0; + { + p_source[_channels] = (uint8_t)0; + } } + } RGBImageRelease(); } - void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len) { RGBImageLock(); - if (rgb_image != NULL) { + if (rgb_image != NULL) + { stbi_image_free(rgb_image); - //free_psram_heap(std::string(TAG) + "->rgb_image (LoadFromMemory)", rgb_image); } rgb_image = stbi_load_from_memory(_buffer, len, &width, &height, &channels, STBI_rgb); bpp = channels; ESP_LOGD(TAG, "Image loaded from memory: %d, %d, %d", width, height, channels); - + if ((width * height * channels) == 0) { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Image with size 0 loaded --> reboot to be done! " - "Check that your camera module is working and connected properly."); + "Check that your camera module is working and connected properly."); LogFile.WriteHeapInfo("LoadFromMemory"); doReboot(); @@ -458,36 +505,49 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len) RGBImageRelease(); } - void CImageBasis::crop_image(unsigned short cropLeft, unsigned short cropRight, unsigned short cropTop, unsigned short cropBottom) { unsigned int maxTopIndex = cropTop * width * channels; - unsigned int minBottomIndex = ((width*height) - (cropBottom * width)) * channels; + unsigned int minBottomIndex = ((width * height) - (cropBottom * width)) * channels; unsigned short maxX = width - cropRight; // In pixels unsigned short newWidth = width - cropLeft - cropRight; unsigned short newHeight = height - cropTop - cropBottom; unsigned int writeIndex = 0; // Loop over all bytes - for (int i = 0; i < width * height * channels; i += channels) { + for (int i = 0; i < width * height * channels; i += channels) + { // Calculate current X, Y pixel position - int x = (i/channels) % width; + int x = (i / channels) % width; // Crop from the top - if (i < maxTopIndex) { continue; } + if (i < maxTopIndex) + { + continue; + } // Crop from the bottom - if (i > minBottomIndex) { continue; } + if (i > minBottomIndex) + { + continue; + } // Crop from the left - if (x <= cropLeft) { continue; } + if (x <= cropLeft) + { + continue; + } // Crop from the right - if (x > maxX) { continue; } + if (x > maxX) + { + continue; + } // If we get here, keep the pixels - for (int c = 0; c < channels; c++) { - rgb_image[writeIndex++] = rgb_image[i+c]; + for (int c = 0; c < channels; c++) + { + rgb_image[writeIndex++] = rgb_image[i + c]; } } @@ -496,8 +556,7 @@ void CImageBasis::crop_image(unsigned short cropLeft, unsigned short cropRight, height = newHeight; } - -CImageBasis::CImageBasis(string _name, CImageBasis *_copyfrom) +CImageBasis::CImageBasis(string _name, CImageBasis *_copyfrom) { name = _name; islocked = false; @@ -509,18 +568,15 @@ CImageBasis::CImageBasis(string _name, CImageBasis *_copyfrom) RGBImageLock(); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CImageBasis_copyfrom - Start"); - #endif - memsize = width * height * channels; - - if (name == "tmpImage") { - rgb_image = (unsigned char*)psram_reserve_shared_tmp_image_memory(); + if (name == "TempImage") + { + rgb_image = (unsigned char *)psram_reserve_shared_tmp_image_memory(); } - else { - rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM); + else + { + rgb_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM); } if (rgb_image == NULL) @@ -533,13 +589,8 @@ CImageBasis::CImageBasis(string _name, CImageBasis *_copyfrom) memCopy(_copyfrom->rgb_image, rgb_image, memsize); RGBImageRelease(); - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CImageBasis_copyfrom - done"); - #endif } - CImageBasis::CImageBasis(string _name, int _width, int _height, int _channels) { name = _name; @@ -552,13 +603,16 @@ CImageBasis::CImageBasis(string _name, int _width, int _height, int _channels) RGBImageLock(); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CImageBasis_width,height,ch - Start"); - #endif - memsize = width * height * channels; - rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM); + if (name == "TempImage") + { + rgb_image = (unsigned char *)psram_reserve_shared_tmp_image_memory(); + } + else + { + rgb_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM); + } if (rgb_image == NULL) { @@ -569,13 +623,8 @@ CImageBasis::CImageBasis(string _name, int _width, int _height, int _channels) } RGBImageRelease(); - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CImageBasis_width,height,ch - done"); - #endif } - CImageBasis::CImageBasis(string _name, std::string _image) { name = _name; @@ -584,46 +633,33 @@ CImageBasis::CImageBasis(string _name, std::string _image) externalImage = false; filename = _image; - if (file_size(_image.c_str()) == 0) { + if (file_size(_image.c_str()) == 0) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, _image + " is empty!"); return; } RGBImageLock(); - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CImageBasis_image - Start"); - #endif - rgb_image = stbi_load(_image.c_str(), &width, &height, &bpp, channels); - if (rgb_image == NULL) { + if (rgb_image == NULL) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CImageBasis-image: Failed to load " + _image + "! Is it corrupted?"); LogFile.WriteHeapInfo("CImageBasis-image"); RGBImageRelease(); return; } - + RGBImageRelease(); - - #ifdef DEBUG_DETAIL_ON - std::string zw = "CImageBasis after load " + _image; - ESP_LOGD(TAG, "%s", zw.c_str()); - ESP_LOGD(TAG, "w %d, h %d, b %d, c %d", width, height, bpp, channels); - #endif - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CImageBasis_image - done"); - #endif } - -bool CImageBasis::ImageOkay(){ +bool CImageBasis::ImageOkay() +{ return rgb_image != NULL; } - -CImageBasis::CImageBasis(string _name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) +CImageBasis::CImageBasis(string _name, uint8_t *_rgb_image, int _channels, int _width, int _height, int _bpp) { name = _name; islocked = false; @@ -635,58 +671,66 @@ CImageBasis::CImageBasis(string _name, uint8_t* _rgb_image, int _channels, int _ externalImage = true; } - void CImageBasis::Negative(void) { RGBImageLock(); - for (int i = 0; i < width * height * channels; i += channels) { - for (int c = 0; c < channels; c++) { - rgb_image[i+c] = 255 - rgb_image[i+c]; + for (int i = 0; i < width * height * channels; i += channels) + { + for (int c = 0; c < channels; c++) + { + rgb_image[i + c] = 255 - rgb_image[i + c]; } } RGBImageRelease(); } - -void CImageBasis::Contrast(float _contrast) //input range [-100..100] +// input range [-100..100] +void CImageBasis::Contrast(float _contrast) { - stbi_uc* p_source; - - float contrast = (_contrast/100) + 1; //convert to decimal & shift range: [0..2] + stbi_uc *p_source; + + float contrast = (_contrast / 100) + 1; // convert to decimal & shift range: [0..2] float intercept = 128 * (1 - contrast); RGBImageLock(); - for (int x = 0; x < width; ++x) + { for (int y = 0; y < height; ++y) { p_source = rgb_image + (channels * (y * width + x)); for (int _channels = 0; _channels < channels; ++_channels) - p_source[_channels] = (uint8_t) std::min(255, std::max(0, (int) (p_source[_channels] * contrast + intercept))); + { + p_source[_channels] = (uint8_t)std::min(255, std::max(0, (int)(p_source[_channels] * contrast + intercept))); + } } + } RGBImageRelease(); } - CImageBasis::~CImageBasis() { RGBImageLock(); - - if (!externalImage) { - if (name == "tmpImage") { // This image should be placed in the shared part of PSRAM + if (!externalImage) + { + if (name == "TempImage") + { + // This image should be placed in the shared part of PSRAM psram_free_shared_temp_image_memory(); } - else { // All other images are much smaller and can go into the normal PSRAM region - //stbi_image_free(rgb_image); - if (memsize == 0) { + else + { + // All other images are much smaller and can go into the normal PSRAM region + if (memsize == 0) + { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Not freeing (" + name + " as there was never PSRAM allocated for it)"); } - else { + else + { free_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ", " + to_string(memsize) + ")", rgb_image); } } @@ -695,18 +739,18 @@ CImageBasis::~CImageBasis() RGBImageRelease(); } - void CImageBasis::SaveToFile(std::string _imageout) { - string typ = getFileType(_imageout); + string typ = get_file_type(_imageout); RGBImageLock(); - if ((typ == "jpg") || (typ == "JPG")) // CAUTION PROBLEMATIC IN ESP32 + if ((typ == "jpg") || (typ == "JPG")) { + // CAUTION PROBLEMATIC IN ESP32 stbi_write_jpg(_imageout.c_str(), width, height, channels, rgb_image, 0); } - + #ifndef STBI_ONLY_JPEG if ((typ == "bmp") || (typ == "BMP")) { @@ -716,27 +760,25 @@ void CImageBasis::SaveToFile(std::string _imageout) RGBImageRelease(); } - void CImageBasis::Resize(int _new_dx, int _new_dy) { memsize = _new_dx * _new_dy * channels; - uint8_t* odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM); + uint8_t *temp_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->temp_image", memsize, MALLOC_CAP_SPIRAM); RGBImageLock(); - stbir_resize_uint8(rgb_image, width, height, 0, odata, _new_dx, _new_dy, 0, channels); + stbir_resize_uint8(rgb_image, width, height, 0, temp_image, _new_dx, _new_dy, 0, channels); - rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis Resize (" + name + ")", memsize, MALLOC_CAP_SPIRAM); - memCopy(odata, rgb_image, memsize); + rgb_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->CImageBasis Resize (" + name + ")", memsize, MALLOC_CAP_SPIRAM); + memCopy(temp_image, rgb_image, memsize); width = _new_dx; height = _new_dy; - free_psram_heap(std::string(TAG) + "->odata", odata); + free_psram_heap(std::string(TAG) + "->temp_image", temp_image); RGBImageRelease(); } - void CImageBasis::Resize(int _new_dx, int _new_dy, CImageBasis *_target) { if ((_target->height != _new_dy) || (_target->width != _new_dx) || (_target->channels != channels)) @@ -747,9 +789,8 @@ void CImageBasis::Resize(int _new_dx, int _new_dy, CImageBasis *_target) RGBImageLock(); - uint8_t* odata = _target->rgb_image; - stbir_resize_uint8(rgb_image, width, height, 0, odata, _new_dx, _new_dy, 0, channels); + uint8_t *temp_image = _target->rgb_image; + stbir_resize_uint8(rgb_image, width, height, 0, temp_image, _new_dx, _new_dy, 0, channels); RGBImageRelease(); } - diff --git a/code/components/jomjol_image_proc/CImageBasis.h b/code/components/jomjol_image_proc/CImageBasis.h index ca9ba14b..0d105b96 100644 --- a/code/components/jomjol_image_proc/CImageBasis.h +++ b/code/components/jomjol_image_proc/CImageBasis.h @@ -3,16 +3,17 @@ #ifndef CIMAGEBASIS_H #define CIMAGEBASIS_H -#include +#include "defines.h" + #include -#include - -#include "../../include/defines.h" - #include +#include + +#include #include "../stb/stb_image.h" #include "../stb/stb_image_write.h" +// #include "../stb/deprecated/stb_image_resize.h" #include "../stb/stb_image_resize.h" #include "esp_heap_caps.h" @@ -23,74 +24,69 @@ struct ImageData size_t size = 0; }; - - class CImageBasis { - protected: - bool externalImage; - std::string filename; - std::string name; // Just used for diagnostics - int memsize = 0; +protected: + bool externalImage; + std::string filename; + std::string name; // Just used for diagnostics + int memsize = 0; - void memCopy(uint8_t* _source, uint8_t* _target, int _size); - bool isInImage(int x, int y); + void memCopy(uint8_t *_source, uint8_t *_target, int _size); + bool isInImage(int x, int y); - bool islocked; + bool islocked; - public: - uint8_t* rgb_image = NULL; - int channels; - int width, height, bpp; +public: + uint8_t *rgb_image = NULL; + int channels; + int width, height, bpp; - uint8_t * RGBImageLock(int _waitmaxsec = 60); - void RGBImageRelease(); - uint8_t * RGBImageGet(); + uint8_t *RGBImageLock(int _waitmaxsec = 60); + void RGBImageRelease(); + uint8_t *RGBImageGet(); - int getWidth(){return this->width;}; - int getHeight(){return this->height;}; - int getChannels(){return this->channels;}; - void drawRect(int x, int y, int dx, int dy, int r = 255, int g = 255, int b = 255, int thickness = 1); - void drawLine(int x1, int y1, int x2, int y2, int r, int g, int b, int thickness = 1); - void drawCircle(int x1, int y1, int rad, int r, int g, int b, int thickness = 1); - void drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int b, int thickness = 1); + int getWidth() { return this->width; }; + int getHeight() { return this->height; }; + int getChannels() { return this->channels; }; + void drawRect(int x, int y, int dx, int dy, int r = 255, int g = 255, int b = 255, int thickness = 1); + void drawLine(int x1, int y1, int x2, int y2, int r, int g, int b, int thickness = 1); + void drawCircle(int x1, int y1, int rad, int r, int g, int b, int thickness = 1); + void drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int b, int thickness = 1); - void setPixelColor(int x, int y, int r, int g, int b); - void Negative(void); - void Contrast(float _contrast); - bool ImageOkay(); - bool CopyFromMemory(uint8_t* _source, int _size); + void setPixelColor(int x, int y, int r, int g, int b); + void Negative(void); + void Contrast(float _contrast); + bool ImageOkay(); + bool CopyFromMemory(uint8_t *_source, int _size); - void SetIndepended(){externalImage = false;}; + void SetIndepended() { externalImage = false; }; - void CreateEmptyImage(int _width, int _height, int _channels); - void EmptyImage(); + void CreateEmptyImage(int _width, int _height, int _channels); + void EmptyImage(); + CImageBasis(std::string name); + CImageBasis(std::string name, std::string _image); + CImageBasis(std::string name, uint8_t *_rgb_image, int _channels, int _width, int _height, int _bpp); + CImageBasis(std::string name, int _width, int _height, int _channels); + CImageBasis(std::string name, CImageBasis *_copyfrom); - CImageBasis(std::string name); - CImageBasis(std::string name, std::string _image); - CImageBasis(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp); - CImageBasis(std::string name, int _width, int _height, int _channels); - CImageBasis(std::string name, CImageBasis *_copyfrom); + void Resize(int _new_dx, int _new_dy); + void Resize(int _new_dx, int _new_dy, CImageBasis *_target); + void crop_image(unsigned short cropLeft, unsigned short cropRight, unsigned short cropTop, unsigned short cropBottom); - void Resize(int _new_dx, int _new_dy); - void Resize(int _new_dx, int _new_dy, CImageBasis *_target); - void crop_image(unsigned short cropLeft, unsigned short cropRight, unsigned short cropTop, unsigned short cropBottom); + void LoadFromMemory(stbi_uc *_buffer, int len); - void LoadFromMemory(stbi_uc *_buffer, int len); + ImageData *writeToMemoryAsJPG(const int quality = 90); + void writeToMemoryAsJPG(ImageData *ii, const int quality = 90); - ImageData* writeToMemoryAsJPG(const int quality = 90); - void writeToMemoryAsJPG(ImageData* ii, const int quality = 90); + esp_err_t SendJPGtoHTTP(httpd_req_t *req, const int quality = 90); - esp_err_t SendJPGtoHTTP(httpd_req_t *req, const int quality = 90); + uint8_t GetPixelColor(int x, int y, int ch); - uint8_t GetPixelColor(int x, int y, int ch); + ~CImageBasis(); - ~CImageBasis(); - - void SaveToFile(std::string _imageout); + void SaveToFile(std::string _imageout); }; - -#endif //CIMAGEBASIS_H - +#endif // CIMAGEBASIS_H diff --git a/code/components/jomjol_image_proc/CMakeLists.txt b/code/components/jomjol_image_proc/CMakeLists.txt index 675889eb..c306e84a 100644 --- a/code/components/jomjol_image_proc/CMakeLists.txt +++ b/code/components/jomjol_image_proc/CMakeLists.txt @@ -1,7 +1,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." - REQUIRES jomjol_helper jomjol_logfile esp_http_server jomjol_fileserver_ota) + INCLUDE_DIRS "." "../../include" + REQUIRES jomjol_helper jomjol_logfile jomjol_flowcontroll esp_http_server jomjol_fileserver_ota) diff --git a/code/components/jomjol_image_proc/CRotateImage.cpp b/code/components/jomjol_image_proc/CRotateImage.cpp index bd1c91e7..99a37e9e 100644 --- a/code/components/jomjol_image_proc/CRotateImage.cpp +++ b/code/components/jomjol_image_proc/CRotateImage.cpp @@ -11,9 +11,9 @@ CRotateImage::CRotateImage(std::string _name, CImageBasis *_org, CImageBasis *_t width = _org->width; height = _org->height; bpp = _org->bpp; - externalImage = true; - ImageTMP = _temp; - ImageOrg = _org; + externalImage = true; + ImageTMP = _temp; + ImageOrg = _org; islocked = false; doflip = _flip; } @@ -31,10 +31,13 @@ void CRotateImage::Rotate(float _angle, int _centerx, int _centery) { org_width = width; org_height = height; + height = org_width; width = org_height; - x_center = x_center - (org_width/2) + (org_height/2); - y_center = y_center + (org_width/2) - (org_height/2); + + x_center = x_center - (org_width / 2) + (org_height / 2); + y_center = y_center + (org_width / 2) - (org_height / 2); + if (ImageOrg) { ImageOrg->height = height; @@ -57,32 +60,32 @@ void CRotateImage::Rotate(float _angle, int _centerx, int _centery) if (doflip) { - m[0][2] = m[0][2] + (org_width/2) - (org_height/2); - m[1][2] = m[1][2] - (org_width/2) + (org_height/2); + m[0][2] = m[0][2] + (org_width / 2) - (org_height / 2); + m[1][2] = m[1][2] - (org_width / 2) + (org_height / 2); } int memsize = width * height * channels; - uint8_t* odata; + uint8_t *temp_image; if (ImageTMP) { - odata = ImageTMP->RGBImageLock(); + temp_image = ImageTMP->RGBImageLock(); } else { - odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM); + temp_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->temp_image", memsize, MALLOC_CAP_SPIRAM); } - int x_source, y_source; - stbi_uc* p_target; - stbi_uc* p_source; + stbi_uc *p_target; + stbi_uc *p_source; RGBImageLock(); for (int x = 0; x < width; ++x) + { for (int y = 0; y < height; ++y) { - p_target = odata + (channels * (y * width + x)); + p_target = temp_image + (channels * (y * width + x)); x_source = int(m[0][0] * x + m[0][1] * y); y_source = int(m[1][0] * x + m[1][1] * y); @@ -93,30 +96,44 @@ void CRotateImage::Rotate(float _angle, int _centerx, int _centery) if ((x_source >= 0) && (x_source < org_width) && (y_source >= 0) && (y_source < org_height)) { p_source = rgb_image + (channels * (y_source * org_width + x_source)); + for (int _channels = 0; _channels < channels; ++_channels) + { p_target[_channels] = p_source[_channels]; + } } else { for (int _channels = 0; _channels < channels; ++_channels) - p_target[_channels] = 255; + { + // p_target[_channels] = 255; // nicht vorhandene Pixel werden Weiß gemacht + // p_target[_channels] = 0; // nicht vorhandene Pixel werden Schwarz gemacht + // p_target[_channels] = 128; // nicht vorhandene Pixel werden Grau gemacht + p_target[_channels] = pixel_fill_color; + } } } - - // memcpy(rgb_image, odata, memsize); - memCopy(odata, rgb_image, memsize); - - if (!ImageTMP) - { - free_psram_heap(std::string(TAG) + "->odata", odata); } + + memCopy(temp_image, rgb_image, memsize); + if (ImageTMP) + { ImageTMP->RGBImageRelease(); + } + else + { + free_psram_heap(std::string(TAG) + "->temp_image", temp_image); + } RGBImageRelease(); } - +void CRotateImage::Rotate(float _angle) +{ + // ESP_LOGD(TAG, "width %d, height %d", width, height); + Rotate(_angle, width / 2, height / 2); +} void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery) { @@ -131,10 +148,13 @@ void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery) { org_width = width; org_height = height; + height = org_width; width = org_height; - x_center = x_center - (org_width/2) + (org_height/2); - y_center = y_center + (org_width/2) - (org_height/2); + + x_center = x_center - (org_width / 2) + (org_height / 2); + y_center = y_center + (org_width / 2) - (org_height / 2); + if (ImageOrg) { ImageOrg->height = height; @@ -157,34 +177,34 @@ void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery) if (doflip) { - m[0][2] = m[0][2] + (org_width/2) - (org_height/2); - m[1][2] = m[1][2] - (org_width/2) + (org_height/2); + m[0][2] = m[0][2] + (org_width / 2) - (org_height / 2); + m[1][2] = m[1][2] - (org_width / 2) + (org_height / 2); } int memsize = width * height * channels; - uint8_t* odata; + uint8_t *temp_image; if (ImageTMP) { - odata = ImageTMP->RGBImageLock(); + temp_image = ImageTMP->RGBImageLock(); } else { - odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM); + temp_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->temp_image", memsize, MALLOC_CAP_SPIRAM); } - int x_source_1, y_source_1, x_source_2, y_source_2; float x_source, y_source; float quad_ul, quad_ur, quad_ol, quad_or; - stbi_uc* p_target; + stbi_uc *p_target; stbi_uc *p_source_ul, *p_source_ur, *p_source_ol, *p_source_or; RGBImageLock(); for (int x = 0; x < width; ++x) + { for (int y = 0; y < height; ++y) { - p_target = odata + (channels * (y * width + x)); + p_target = temp_image + (channels * (y * width + x)); x_source = (m[0][0] * x + m[0][1] * y); y_source = (m[1][0] * x + m[1][1] * y); @@ -198,10 +218,9 @@ void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery) y_source_2 = y_source_1 + 1; quad_ul = (x_source_2 - x_source) * (y_source_2 - y_source); - quad_ur = (1- (x_source_2 - x_source)) * (y_source_2 - y_source); - quad_or = (x_source_2 - x_source) * (1-(y_source_2 - y_source)); - quad_ol = (1- (x_source_2 - x_source)) * (1-(y_source_2 - y_source)); - + quad_ur = (1 - (x_source_2 - x_source)) * (y_source_2 - y_source); + quad_or = (x_source_2 - x_source) * (1 - (y_source_2 - y_source)); + quad_ol = (1 - (x_source_2 - x_source)) * (1 - (y_source_2 - y_source)); if ((x_source_1 >= 0) && (x_source_2 < org_width) && (y_source_1 >= 0) && (y_source_2 < org_height)) { @@ -209,72 +228,70 @@ void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery) p_source_ur = rgb_image + (channels * (y_source_1 * org_width + x_source_2)); p_source_or = rgb_image + (channels * (y_source_2 * org_width + x_source_1)); p_source_ol = rgb_image + (channels * (y_source_2 * org_width + x_source_2)); + for (int _channels = 0; _channels < channels; ++_channels) { - p_target[_channels] = (int)((float)p_source_ul[_channels] * quad_ul - + (float)p_source_ur[_channels] * quad_ur - + (float)p_source_or[_channels] * quad_or - + (float)p_source_ol[_channels] * quad_ol); + p_target[_channels] = (int)((float)p_source_ul[_channels] * quad_ul + (float)p_source_ur[_channels] * quad_ur + (float)p_source_or[_channels] * quad_or + (float)p_source_ol[_channels] * quad_ol); } } else { for (int _channels = 0; _channels < channels; ++_channels) - p_target[_channels] = 255; + { + // p_target[_channels] = 255; // nicht vorhandene Pixel werden Weiß gemacht + // p_target[_channels] = 0; // nicht vorhandene Pixel werden Schwarz gemacht + // p_target[_channels] = 128; // nicht vorhandene Pixel werden Grau gemacht + p_target[_channels] = pixel_fill_color; + } } } - - // memcpy(rgb_image, odata, memsize); - memCopy(odata, rgb_image, memsize); - - if (!ImageTMP) - { - free_psram_heap(std::string(TAG) + "->odata", odata); } + + memCopy(temp_image, rgb_image, memsize); + if (ImageTMP) + { ImageTMP->RGBImageRelease(); + } + else + { + free_psram_heap(std::string(TAG) + "->temp_image", temp_image); + } RGBImageRelease(); } - -void CRotateImage::Rotate(float _angle) -{ -// ESP_LOGD(TAG, "width %d, height %d", width, height); - Rotate(_angle, width / 2, height / 2); -} - void CRotateImage::RotateAntiAliasing(float _angle) { -// ESP_LOGD(TAG, "width %d, height %d", width, height); + // ESP_LOGD(TAG, "width %d, height %d", width, height); RotateAntiAliasing(_angle, width / 2, height / 2); } void CRotateImage::Translate(int _dx, int _dy) { int memsize = width * height * channels; - uint8_t* odata; + uint8_t *temp_image; + if (ImageTMP) { - odata = ImageTMP->RGBImageLock(); + temp_image = ImageTMP->RGBImageLock(); } else { - odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM); + temp_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->temp_image", memsize, MALLOC_CAP_SPIRAM); } - - int x_source, y_source; - stbi_uc* p_target; - stbi_uc* p_source; + stbi_uc *p_target; + stbi_uc *p_source; RGBImageLock(); for (int x = 0; x < width; ++x) + { for (int y = 0; y < height; ++y) { - p_target = odata + (channels * (y * width + x)); + p_target = temp_image + (channels * (y * width + x)); x_source = x - _dx; y_source = y - _dy; @@ -282,28 +299,35 @@ void CRotateImage::Translate(int _dx, int _dy) if ((x_source >= 0) && (x_source < width) && (y_source >= 0) && (y_source < height)) { p_source = rgb_image + (channels * (y_source * width + x_source)); + for (int _channels = 0; _channels < channels; ++_channels) + { p_target[_channels] = p_source[_channels]; + } } else { for (int _channels = 0; _channels < channels; ++_channels) - p_target[_channels] = 255; + { + // p_target[_channels] = 255; // nicht vorhandene Pixel werden Weiß gemacht + // p_target[_channels] = 0; // nicht vorhandene Pixel werden Schwarz gemacht + // p_target[_channels] = 128; // nicht vorhandene Pixel werden Grau gemacht + p_target[_channels] = pixel_fill_color; + } } } - - // memcpy(rgb_image, odata, memsize); - memCopy(odata, rgb_image, memsize); - if (!ImageTMP) - { - free_psram_heap(std::string(TAG) + "->odata", odata); } + memCopy(temp_image, rgb_image, memsize); + if (ImageTMP) { ImageTMP->RGBImageRelease(); } + else + { + free_psram_heap(std::string(TAG) + "->temp_image", temp_image); + } + RGBImageRelease(); - } - diff --git a/code/components/jomjol_image_proc/CRotateImage.h b/code/components/jomjol_image_proc/CRotateImage.h index 6b7b542e..e7845486 100644 --- a/code/components/jomjol_image_proc/CRotateImage.h +++ b/code/components/jomjol_image_proc/CRotateImage.h @@ -5,24 +5,36 @@ #include "CImageBasis.h" - -class CRotateImage: public CImageBasis +class CRotateImage : public CImageBasis { +public: + CImageBasis *ImageTMP, *ImageOrg; + int pixel_fill_color = 216; // Gray + bool doflip; - public: - CImageBasis *ImageTMP, *ImageOrg; - bool doflip; - CRotateImage(std::string name, std::string _image, bool _flip = false) : CImageBasis(name, _image) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;}; - CRotateImage(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp, bool _flip = false) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;}; - CRotateImage(std::string name, CImageBasis *_org, CImageBasis *_temp, bool _flip = false); + CRotateImage(std::string name, std::string _image, bool _flip = false) : CImageBasis(name, _image) + { + ImageTMP = NULL; + ImageOrg = NULL; + doflip = _flip; + }; - void Rotate(float _angle); - void RotateAntiAliasing(float _angle); - - void Rotate(float _angle, int _centerx, int _centery); - void RotateAntiAliasing(float _angle, int _centerx, int _centery); + CRotateImage(std::string name, uint8_t *_rgb_image, int _channels, int _width, int _height, int _bpp, bool _flip = false) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) + { + ImageTMP = NULL; + ImageOrg = NULL; + doflip = _flip; + }; + + CRotateImage(std::string name, CImageBasis *_org, CImageBasis *_temp, bool _flip = false); - void Translate(int _dx, int _dy); + void Rotate(float _angle); + void RotateAntiAliasing(float _angle); + + void Rotate(float _angle, int _centerx, int _centery); + void RotateAntiAliasing(float _angle, int _centerx, int _centery); + + void Translate(int _dx, int _dy); }; -#endif //CROTATEIMAGE_H \ No newline at end of file +#endif // CROTATEIMAGE_H diff --git a/code/components/jomjol_image_proc/make_stb.cpp b/code/components/jomjol_image_proc/make_stb.cpp index 59875fcb..7d6e2e29 100644 --- a/code/components/jomjol_image_proc/make_stb.cpp +++ b/code/components/jomjol_image_proc/make_stb.cpp @@ -1,23 +1,22 @@ -#include +#include "defines.h" + #include +#include + #include "psram.h" -#include "../../include/defines.h" - - #define USE_SHARED_PSRAM_FOR_STBI #ifdef USE_SHARED_PSRAM_FOR_STBI -#define STBI_MALLOC(sz) psram_reserve_shared_stbi_memory(sz) -#define STBI_REALLOC(p,newsz) psram_reallocate_shared_stbi_memory(p, newsz) -#define STBI_FREE(p) psram_free_shared_stbi_memory(p) +#define STBI_MALLOC(sz) psram_reserve_shared_stbi_memory(sz) +#define STBI_REALLOC(p, newsz) psram_reallocate_shared_stbi_memory(p, newsz) +#define STBI_FREE(p) psram_free_shared_stbi_memory(p) #else // Use normal PSRAM -#define STBI_MALLOC(sz) malloc_psram_heap("STBI", sz, MALLOC_CAP_SPIRAM) -#define STBI_REALLOC(p,newsz) realloc_psram_heap("STBI", p, newsz, MALLOC_CAP_SPIRAM) -#define STBI_FREE(p) free_psram_heap("STBI", p) +#define STBI_MALLOC(sz) malloc_psram_heap("STBI", sz, MALLOC_CAP_SPIRAM) +#define STBI_REALLOC(p, newsz) realloc_psram_heap("STBI", p, newsz, MALLOC_CAP_SPIRAM) +#define STBI_FREE(p) free_psram_heap("STBI", p) #endif - #define STB_IMAGE_IMPLEMENTATION #include "../stb/stb_image.h" @@ -25,4 +24,5 @@ #include "../stb/stb_image_write.h" #define STB_IMAGE_RESIZE_IMPLEMENTATION -#include "../stb/stb_image_resize.h" \ No newline at end of file +// #include "../stb/deprecated/stb_image_resize.h" +#include "../stb/stb_image_resize.h" diff --git a/code/components/jomjol_influxdb/CMakeLists.txt b/code/components/jomjol_influxdb/CMakeLists.txt index 54513408..d8445d69 100644 --- a/code/components/jomjol_influxdb/CMakeLists.txt +++ b/code/components/jomjol_influxdb/CMakeLists.txt @@ -1,7 +1,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." - REQUIRES esp_http_client jomjol_logfile) + INCLUDE_DIRS "." "../../include" + REQUIRES esp_http_client jomjol_logfile jomjol_helper) diff --git a/code/components/jomjol_influxdb/interface_influxdb.cpp b/code/components/jomjol_influxdb/interface_influxdb.cpp index 8059c8f0..8ead47a4 100644 --- a/code/components/jomjol_influxdb/interface_influxdb.cpp +++ b/code/components/jomjol_influxdb/interface_influxdb.cpp @@ -1,4 +1,5 @@ -#ifdef ENABLE_INFLUXDB +#include "defines.h" + #include "interface_influxdb.h" #include "esp_log.h" @@ -6,19 +7,17 @@ #include "ClassLogFile.h" #include "esp_http_client.h" #include "time_sntp.h" -#include "../../include/defines.h" static const char *TAG = "INFLUXDB"; /** * @brief Buffer to store the HTTP response. - * + * * This character array is used to store the output of an HTTP response. * The size of the buffer is defined by the constant MAX_HTTP_OUTPUT_BUFFER. */ char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0}; - /** * @brief HTTP event handler callback function. * @@ -39,50 +38,49 @@ char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0}; */ static esp_err_t http_event_handler(esp_http_client_event_t *evt) { - switch(evt->event_id) + switch (evt->event_id) { - case HTTP_EVENT_ERROR: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered"); - break; - case HTTP_EVENT_ON_CONNECTED: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client connected"); - ESP_LOGI(TAG, "HTTP Client Connected"); - break; - case HTTP_EVENT_HEADERS_SENT: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client sent all request headers"); - break; - case HTTP_EVENT_ON_HEADER: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Header: key=" + std::string(evt->header_key) + ", value=" + std::string(evt->header_value)); - break; - case HTTP_EVENT_ON_DATA: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client data recevied: len=" + std::to_string(evt->data_len)); - break; - case HTTP_EVENT_ON_FINISH: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client finished"); - break; - case HTTP_EVENT_DISCONNECTED: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Disconnected"); - break; - case HTTP_EVENT_REDIRECT: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Redirect"); - break; + case HTTP_EVENT_ERROR: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered"); + break; + case HTTP_EVENT_ON_CONNECTED: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client connected"); + ESP_LOGI(TAG, "HTTP Client Connected"); + break; + case HTTP_EVENT_HEADERS_SENT: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client sent all request headers"); + break; + case HTTP_EVENT_ON_HEADER: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Header: key=" + std::string(evt->header_key) + ", value=" + std::string(evt->header_value)); + break; + case HTTP_EVENT_ON_DATA: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client data recevied: len=" + std::to_string(evt->data_len)); + break; + case HTTP_EVENT_ON_FINISH: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client finished"); + break; + case HTTP_EVENT_DISCONNECTED: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Disconnected"); + break; + case HTTP_EVENT_REDIRECT: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Redirect"); + break; } return ESP_OK; } - - /** * @brief Initializes the InfluxDB connection with version 1 settings. - * + * * This function sets up the connection parameters for InfluxDB version 1. - * + * * @param _influxDBURI The URI of the InfluxDB server. * @param _database The name of the database to connect to. * @param _user The username for authentication. * @param _password The password for authentication. */ -void InfluxDB::InfluxDBInitV1(std::string _influxDBURI, std::string _database, std::string _user, std::string _password) { +void InfluxDB::InfluxDBInitV1(std::string _influxDBURI, std::string _database, std::string _user, std::string _password) +{ version = INFLUXDB_V1; influxDBURI = _influxDBURI; database = _database; @@ -92,16 +90,17 @@ void InfluxDB::InfluxDBInitV1(std::string _influxDBURI, std::string _database, s /** * @brief Initializes the InfluxDB client with version 2 settings. - * + * * This function sets up the InfluxDB client to use InfluxDB version 2 by * configuring the URI, bucket, organization, and token. - * + * * @param _influxDBURI The URI of the InfluxDB server. * @param _bucket The bucket name to store data in. * @param _org The organization name associated with the bucket. * @param _token The authentication token for accessing the InfluxDB server. */ -void InfluxDB::InfluxDBInitV2(std::string _influxDBURI, std::string _bucket, std::string _org, std::string _token) { +void InfluxDB::InfluxDBInitV2(std::string _influxDBURI, std::string _bucket, std::string _org, std::string _token) +{ version = INFLUXDB_V2; influxDBURI = _influxDBURI; bucket = _bucket; @@ -121,7 +120,8 @@ void InfluxDB::InfluxDBInitV2(std::string _influxDBURI, std::string _bucket, std * @param None * @return None */ -void InfluxDB::connectHTTP() { +void InfluxDB::connectHTTP() +{ esp_http_client_config_t config = {}; config.url = influxDBURI.c_str(); @@ -129,35 +129,39 @@ void InfluxDB::connectHTTP() { config.buffer_size = MAX_HTTP_OUTPUT_BUFFER; config.user_data = response_buffer; - - switch (version) { - case INFLUXDB_V1: - config.auth_type = HTTP_AUTH_TYPE_BASIC; - config.username = user.c_str(); - config.password = password.c_str(); - break; - case INFLUXDB_V2: - break; + switch (version) + { + case INFLUXDB_V1: + config.auth_type = HTTP_AUTH_TYPE_BASIC; + config.username = user.c_str(); + config.password = password.c_str(); + break; + case INFLUXDB_V2: + break; } InfluxDBdestroy(); httpClient = esp_http_client_init(&config); - if (!httpClient) { + if (!httpClient) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to initialize HTTP client"); - } else { + } + else + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP client initialized successfully"); } } - /** * @brief Destroys the InfluxDB instance by cleaning up the HTTP client. * * This function checks if the HTTP client is initialized. If it is, it cleans up the HTTP client * and logs the cleanup action. The HTTP client pointer is then set to NULL. */ -void InfluxDB::InfluxDBdestroy() { - if (httpClient) { +void InfluxDB::InfluxDBdestroy() +{ + if (httpClient) + { esp_http_client_cleanup(httpClient); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP client cleaned up"); httpClient = NULL; @@ -179,20 +183,20 @@ void InfluxDB::InfluxDBdestroy() { * It constructs the appropriate API URI based on the InfluxDB version and sends the data * using an HTTP POST request. */ -void InfluxDB::InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC) { - std::string apiURI; +void InfluxDB::InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC) +{ + std::string apiURI; std::string payload; char nowTimestamp[21]; connectHTTP(); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", timeUTC: " + std::to_string(_timeUTC)); if (_timeUTC > 0) { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Timestamp (UTC): " + std::to_string(_timeUTC)); - sprintf(nowTimestamp,"%ld000000000", _timeUTC); // UTC + sprintf(nowTimestamp, "%ld000000000", _timeUTC); // UTC payload = _measurement + " " + _key + "=" + _content + " " + nowTimestamp; } else @@ -206,43 +210,48 @@ void InfluxDB::InfluxDBPublish(std::string _measurement, std::string _key, std:: esp_err_t err; - switch (version) { - case INFLUXDB_V1: - apiURI = influxDBURI + "/write?db=" + database; - apiURI.shrink_to_fit(); + switch (version) + { + case INFLUXDB_V1: + apiURI = influxDBURI + "/write?db=" + database; + apiURI.shrink_to_fit(); - esp_http_client_set_url(httpClient, apiURI.c_str()); - esp_http_client_set_method(httpClient, HTTP_METHOD_POST); - esp_http_client_set_header(httpClient, "Content-Type", "text/plain"); - esp_http_client_set_post_field(httpClient, payload.c_str(), payload.length()); + esp_http_client_set_url(httpClient, apiURI.c_str()); + esp_http_client_set_method(httpClient, HTTP_METHOD_POST); + esp_http_client_set_header(httpClient, "Content-Type", "text/plain"); + esp_http_client_set_post_field(httpClient, payload.c_str(), payload.length()); - err = esp_http_client_perform(httpClient); - if (err == ESP_OK) { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Data published successfully: " + payload); - } else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to publish data: " + std::string(esp_err_to_name(err))); - } - break; + err = esp_http_client_perform(httpClient); + if (err == ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Data published successfully: " + payload); + } + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to publish data: " + std::string(esp_err_to_name(err))); + } + break; - case INFLUXDB_V2: - apiURI = influxDBURI + "/api/v2/write?org=" + org + "&bucket=" + bucket; - apiURI.shrink_to_fit(); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "apiURI: " + apiURI); + case INFLUXDB_V2: + apiURI = influxDBURI + "/api/v2/write?org=" + org + "&bucket=" + bucket; + apiURI.shrink_to_fit(); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "apiURI: " + apiURI); - esp_http_client_set_url(httpClient, apiURI.c_str()); - esp_http_client_set_method(httpClient, HTTP_METHOD_POST); - esp_http_client_set_header(httpClient, "Content-Type", "text/plain"); - std::string _zw = "Token " + token; - esp_http_client_set_header(httpClient, "Authorization", _zw.c_str()); - esp_http_client_set_post_field(httpClient, payload.c_str(), payload.length()); - err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(httpClient)); - if (err == ESP_OK) { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Data published successfully: " + payload); - } else { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Failed to publish data: " + std::string(esp_err_to_name(err))); - } + esp_http_client_set_url(httpClient, apiURI.c_str()); + esp_http_client_set_method(httpClient, HTTP_METHOD_POST); + esp_http_client_set_header(httpClient, "Content-Type", "text/plain"); + std::string _zw = "Token " + token; + esp_http_client_set_header(httpClient, "Authorization", _zw.c_str()); + esp_http_client_set_post_field(httpClient, payload.c_str(), payload.length()); + err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(httpClient)); + if (err == ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Data published successfully: " + payload); + } + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Failed to publish data: " + std::string(esp_err_to_name(err))); + } break; } } - -#endif //ENABLE_INFLUXDB diff --git a/code/components/jomjol_influxdb/interface_influxdb.h b/code/components/jomjol_influxdb/interface_influxdb.h index 80dbf50c..cca5fb5d 100644 --- a/code/components/jomjol_influxdb/interface_influxdb.h +++ b/code/components/jomjol_influxdb/interface_influxdb.h @@ -1,6 +1,5 @@ -#ifdef ENABLE_INFLUXDB - #pragma once + #ifndef INTERFACE_INFLUXDB_H #define INTERFACE_INFLUXDB_H @@ -8,13 +7,12 @@ #include #include - #include #include "esp_http_client.h" #include "esp_log.h" - -enum InfluxDBVersion { +enum InfluxDBVersion +{ INFLUXDB_V1, INFLUXDB_V2 }; @@ -22,61 +20,62 @@ enum InfluxDBVersion { /** * @class InfluxDB * @brief A class to handle connections and data publishing to InfluxDB servers. - * + * * This class supports both InfluxDB v1.x and v2.x versions. It provides methods to initialize * the connection parameters, publish data, and destroy the connection. - * + * * @private * @var std::string influxDBURI * URI for the InfluxDB server. - * + * * @var std::string database * Database name for InfluxDB v1.x. - * + * * @var std::string user * Username for InfluxDB v1.x. - * + * * @var std::string password * Password for InfluxDB v1.x. - * + * * @var std::string bucket * Bucket name for InfluxDB v2.x. - * + * * @var std::string org * Organization name for InfluxDB v2.x. - * + * * @var std::string token * Token for InfluxDB v2.x. - * + * * @var InfluxDBVersion version * Version of the InfluxDB server (v1.x or v2.x). - * + * * @var esp_http_client_handle_t httpClient * HTTP client handle for making requests to the InfluxDB server. - * + * * @var void connectHTTP() * Establishes an HTTP connection to the InfluxDB server. - * + * * @public * @fn void InfluxDBInitV1(std::string _influxDBURI, std::string _database, std::string _user, std::string _password) * Initializes the connection parameters for InfluxDB v1.x. - * + * * @fn void InfluxDBInitV2(std::string _influxDBURI, std::string _bucket, std::string _org, std::string _token) * Initializes the connection parameters for InfluxDB v2.x. - * + * * @fn void InfluxDBdestroy() * Destroys the InfluxDB connection. - * + * * @fn void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC) * Publishes data to the InfluxDB server. - * + * * @param _measurement The measurement name. * @param _key The key for the data point. * @param _content The content or value of the data point. * @param _timeUTC The timestamp in UTC for the data point. */ -class InfluxDB { +class InfluxDB +{ private: // Information for InfluxDB v1.x std::string influxDBURI = ""; @@ -107,7 +106,4 @@ public: void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC); }; - - -#endif //INTERFACE_INFLUXDB_H -#endif //ENABLE_INFLUXDB \ No newline at end of file +#endif // INTERFACE_INFLUXDB_H diff --git a/code/components/jomjol_logfile/CMakeLists.txt b/code/components/jomjol_logfile/CMakeLists.txt index c7f46e10..f993eb55 100644 --- a/code/components/jomjol_logfile/CMakeLists.txt +++ b/code/components/jomjol_logfile/CMakeLists.txt @@ -1,7 +1,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." + INCLUDE_DIRS "." "../../include" REQUIRES jomjol_time_sntp jomjol_helper) diff --git a/code/components/jomjol_logfile/ClassLogFile.cpp b/code/components/jomjol_logfile/ClassLogFile.cpp index 3420e6a9..e4f42672 100644 --- a/code/components/jomjol_logfile/ClassLogFile.cpp +++ b/code/components/jomjol_logfile/ClassLogFile.cpp @@ -1,3 +1,5 @@ +#include "defines.h" + #include "ClassLogFile.h" #include "time_sntp.h" #include "esp_log.h" @@ -7,7 +9,8 @@ #include #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif #include #ifdef __cplusplus @@ -16,42 +19,41 @@ extern "C" { #include "Helper.h" #include "time_sntp.h" -#include "../../include/defines.h" static const char *TAG = "LOGFILE"; ClassLogFile LogFile("/sdcard/log/message", "log_%Y-%m-%d.txt", "/sdcard/log/data", "data_%Y-%m-%d.csv"); - void ClassLogFile::WriteHeapInfo(std::string _id) { - if (loglevel >= ESP_LOG_DEBUG) { - std::string _zw = _id + "\t" + getESPHeapInfo(); + if (loglevel >= ESP_LOG_DEBUG) + { + std::string _zw = _id + "\t" + get_heapinfo(); WriteToFile(ESP_LOG_DEBUG, "HEAP", _zw); } } - -void ClassLogFile::WriteToData(std::string _timestamp, std::string _name, std::string _ReturnRawValue, std::string _ReturnValue, std::string _ReturnPreValue, std::string _ReturnRateValue, std::string _ReturnChangeAbsolute, std::string _ErrorMessageText, std::string _digit, std::string _analog) +void ClassLogFile::WriteToData(std::string _timestamp, std::string _name, std::string _ReturnRawValue, std::string _ReturnValue, std::string _ReturnPreValue, std::string _ReturnRateValue, std::string _ReturnChangeAbsolute, std::string _ErrorMessageText, std::string _digit, std::string _analog) { ESP_LOGD(TAG, "Start WriteToData"); time_t rawtime; - struct tm* timeinfo; + struct tm *timeinfo; char buffer[30]; time(&rawtime); timeinfo = localtime(&rawtime); strftime(buffer, 30, datafile.c_str(), timeinfo); - std::string logpath = dataroot + "/" + buffer; - - FILE* pFile; + std::string logpath = dataroot + "/" + buffer; + + FILE *pFile; std::string zwtime; ESP_LOGD(TAG, "Datalogfile: %s", logpath.c_str()); pFile = fopen(logpath.c_str(), "a+"); - if (pFile!=NULL) { + if (pFile != NULL) + { fputs(_timestamp.c_str(), pFile); fputs(",", pFile); fputs(_name.c_str(), pFile); @@ -71,36 +73,37 @@ void ClassLogFile::WriteToData(std::string _timestamp, std::string _name, std::s fputs(_analog.c_str(), pFile); fputs("\n", pFile); - fclose(pFile); - } else { + fclose(pFile); + } + else + { ESP_LOGE(TAG, "Can't open data file %s", logpath.c_str()); } - } - void ClassLogFile::setLogLevel(esp_log_level_t _logLevel) { std::string levelText; // Print log level to log file - switch(_logLevel) { - case ESP_LOG_WARN: - levelText = "WARNING"; - break; - - case ESP_LOG_INFO: - levelText = "INFO"; - break; - - case ESP_LOG_DEBUG: - levelText = "DEBUG"; - break; - - case ESP_LOG_ERROR: - default: - levelText = "ERROR"; - break; + switch (_logLevel) + { + case ESP_LOG_WARN: + levelText = "WARNING"; + break; + + case ESP_LOG_INFO: + levelText = "INFO"; + break; + + case ESP_LOG_DEBUG: + levelText = "DEBUG"; + break; + + case ESP_LOG_ERROR: + default: + levelText = "ERROR"; + break; } LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Set log level to " + levelText); @@ -115,40 +118,38 @@ void ClassLogFile::setLogLevel(esp_log_level_t _logLevel) */ } - -void ClassLogFile::SetLogFileRetention(unsigned short _LogFileRetentionInDays){ +void ClassLogFile::SetLogFileRetention(unsigned short _LogFileRetentionInDays) +{ logFileRetentionInDays = _LogFileRetentionInDays; } - -void ClassLogFile::SetDataLogRetention(unsigned short _DataLogRetentionInDays){ +void ClassLogFile::SetDataLogRetention(unsigned short _DataLogRetentionInDays) +{ dataLogRetentionInDays = _DataLogRetentionInDays; } - -void ClassLogFile::SetDataLogToSD(bool _doDataLogToSD){ +void ClassLogFile::SetDataLogToSD(bool _doDataLogToSD) +{ doDataLogToSD = _doDataLogToSD; } - -bool ClassLogFile::GetDataLogToSD(){ +bool ClassLogFile::GetDataLogToSD() +{ return doDataLogToSD; } - -static FILE* logFileAppendHandle = NULL; +static FILE *logFileAppendHandle = NULL; std::string fileNameDate; void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::string message, bool _time) { time_t rawtime; - struct tm* timeinfo; + struct tm *timeinfo; std::string fileNameDateNew; std::string zwtime; std::string ntpTime = ""; - time(&rawtime); timeinfo = localtime(&rawtime); char buf[30]; @@ -157,20 +158,21 @@ void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::stri std::replace(message.begin(), message.end(), '\n', ' '); // Replace all newline characters - if (tag != "") { + if (tag != "") + { ESP_LOG_LEVEL(level, tag.c_str(), "%s", message.c_str()); message = "[" + tag + "] " + message; } - else { + else + { ESP_LOG_LEVEL(level, "", "%s", message.c_str()); } - - if (level > loglevel) {// Only write to file if loglevel is below threshold + if (level > loglevel) + { // Only write to file if loglevel is below threshold return; } - if (_time) { char logLineDate[30]; @@ -178,43 +180,45 @@ void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::stri ntpTime = std::string(logLineDate); } - std::string loglevelString; - switch(level) { - case ESP_LOG_ERROR: - loglevelString = "ERR"; - break; - case ESP_LOG_WARN: - loglevelString = "WRN"; - break; - case ESP_LOG_INFO: - loglevelString = "INF"; - break; - case ESP_LOG_DEBUG: - loglevelString = "DBG"; - break; - case ESP_LOG_VERBOSE: - loglevelString = "VER"; - break; - case ESP_LOG_NONE: - default: - loglevelString = "NONE"; - break; + std::string loglevelString; + switch (level) + { + case ESP_LOG_ERROR: + loglevelString = "ERR"; + break; + case ESP_LOG_WARN: + loglevelString = "WRN"; + break; + case ESP_LOG_INFO: + loglevelString = "INF"; + break; + case ESP_LOG_DEBUG: + loglevelString = "DBG"; + break; + case ESP_LOG_VERBOSE: + loglevelString = "VER"; + break; + case ESP_LOG_NONE: + default: + loglevelString = "NONE"; + break; } - std::string formatedUptime = getFormatedUptime(true); - - std::string fullmessage = "[" + formatedUptime + "] " + ntpTime + "\t<" + loglevelString + ">\t" + message + "\n"; + std::string formatedUptime = get_formated_uptime(true); + std::string fullmessage = "[" + formatedUptime + "] " + ntpTime + "\t<" + loglevelString + ">\t" + message + "\n"; #ifdef KEEP_LOGFILE_OPEN_FOR_APPENDING - if (fileNameDateNew != fileNameDate) { // Filename changed + if (fileNameDateNew != fileNameDate) + { // Filename changed // Make sure each day gets its own logfile // Also we need to re-open it in case it needed to get closed for reading - std::string logpath = logroot + "/" + fileNameDateNew; + std::string logpath = logroot + "/" + fileNameDateNew; ESP_LOGI(TAG, "Opening logfile %s for appending", logpath.c_str()); logFileAppendHandle = fopen(logpath.c_str(), "a+"); - if (logFileAppendHandle==NULL) { + if (logFileAppendHandle == NULL) + { ESP_LOGE(TAG, "Can't open log file %s", logpath.c_str()); return; } @@ -222,16 +226,17 @@ void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::stri fileNameDate = fileNameDateNew; } #else - std::string logpath = logroot + "/" + fileNameDateNew; + std::string logpath = logroot + "/" + fileNameDateNew; logFileAppendHandle = fopen(logpath.c_str(), "a+"); - if (logFileAppendHandle==NULL) { + if (logFileAppendHandle == NULL) + { ESP_LOGE(TAG, "Can't open log file %s", logpath.c_str()); return; } - #endif +#endif fputs(fullmessage.c_str(), logFileAppendHandle); - + #ifdef KEEP_LOGFILE_OPEN_FOR_APPENDING fflush(logFileAppendHandle); fsync(fileno(logFileAppendHandle)); @@ -240,76 +245,75 @@ void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::stri #endif } - -void ClassLogFile::CloseLogFileAppendHandle() { - if (logFileAppendHandle != NULL) { +void ClassLogFile::CloseLogFileAppendHandle() +{ + if (logFileAppendHandle != NULL) + { fclose(logFileAppendHandle); logFileAppendHandle = NULL; fileNameDate = ""; } } - -void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::string message) { +void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::string message) +{ LogFile.WriteToFile(level, tag, message, true); } - std::string ClassLogFile::GetCurrentFileNameData() { time_t rawtime; - struct tm* timeinfo; + struct tm *timeinfo; char buffer[60]; time(&rawtime); timeinfo = localtime(&rawtime); strftime(buffer, 60, datafile.c_str(), timeinfo); - std::string logpath = dataroot + "/" + buffer; + std::string logpath = dataroot + "/" + buffer; return logpath; } - std::string ClassLogFile::GetCurrentFileName() { time_t rawtime; - struct tm* timeinfo; + struct tm *timeinfo; char buffer[60]; time(&rawtime); timeinfo = localtime(&rawtime); strftime(buffer, 60, logfile.c_str(), timeinfo); - std::string logpath = logroot + "/" + buffer; + std::string logpath = logroot + "/" + buffer; return logpath; } - void ClassLogFile::RemoveOldLogFile() { - if (logFileRetentionInDays == 0) { + if (logFileRetentionInDays == 0) + { return; } ESP_LOGD(TAG, "Remove old log files"); time_t rawtime; - struct tm* timeinfo; + struct tm *timeinfo; char cmpfilename[30]; time(&rawtime); - rawtime = addDays(rawtime, -logFileRetentionInDays + 1); + rawtime = add_days(rawtime, -logFileRetentionInDays + 1); timeinfo = localtime(&rawtime); - //ESP_LOGD(TAG, "logFileRetentionInDays: %d", logFileRetentionInDays); - + // ESP_LOGD(TAG, "logFileRetentionInDays: %d", logFileRetentionInDays); strftime(cmpfilename, 30, logfile.c_str(), timeinfo); - //ESP_LOGD(TAG, "log file name to compare: %s", cmpfilename); + // ESP_LOGD(TAG, "log file name to compare: %s", cmpfilename); DIR *dir = opendir(logroot.c_str()); - if (!dir) { + if (!dir) + { ESP_LOGE(TAG, "Failed to stat dir: %s", logroot.c_str()); return; } @@ -317,58 +321,67 @@ void ClassLogFile::RemoveOldLogFile() struct dirent *entry; int deleted = 0; int notDeleted = 0; - while ((entry = readdir(dir)) != NULL) { - if (entry->d_type == DT_REG) { - //ESP_LOGD(TAG, "compare log file: %s to %s", entry->d_name, cmpfilename); - if ((strlen(entry->d_name) == strlen(cmpfilename)) && (strcmp(entry->d_name, cmpfilename) < 0)) { - //ESP_LOGD(TAG, "delete log file: %s", entry->d_name); + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_type == DT_REG) + { + // ESP_LOGD(TAG, "compare log file: %s to %s", entry->d_name, cmpfilename); + if ((strlen(entry->d_name) == strlen(cmpfilename)) && (strcmp(entry->d_name, cmpfilename) < 0)) + { + // ESP_LOGD(TAG, "delete log file: %s", entry->d_name); std::string filepath = logroot + "/" + entry->d_name; - if ((strcmp(entry->d_name, "log_1970-01-01.txt") == 0) && getTimeWasNotSetAtBoot()) { // keep logfile log_1970-01-01.txt if time was not set at boot (some boot logs are in there) - //ESP_LOGD(TAG, "Skip deleting this file: %s", entry->d_name); + if ((strcmp(entry->d_name, "log_1970-01-01.txt") == 0) && getTimeWasNotSetAtBoot()) + { // keep logfile log_1970-01-01.txt if time was not set at boot (some boot logs are in there) + // ESP_LOGD(TAG, "Skip deleting this file: %s", entry->d_name); notDeleted++; } - else { - if (unlink(filepath.c_str()) == 0) { + else + { + if (unlink(filepath.c_str()) == 0) + { deleted++; - } - else { + } + else + { ESP_LOGE(TAG, "can't delete file: %s", entry->d_name); notDeleted++; } } - } - else { + } + else + { notDeleted++; } } } - ESP_LOGD(TAG, "log files deleted: %d | files not deleted (incl. leer.txt): %d", deleted, notDeleted); + ESP_LOGD(TAG, "log files deleted: %d | files not deleted (incl. leer.txt): %d", deleted, notDeleted); closedir(dir); } - void ClassLogFile::RemoveOldDataLog() { - if (dataLogRetentionInDays == 0 || !doDataLogToSD) { + if (dataLogRetentionInDays == 0 || !doDataLogToSD) + { return; } ESP_LOGD(TAG, "Remove old data files"); time_t rawtime; - struct tm* timeinfo; + struct tm *timeinfo; char cmpfilename[30]; time(&rawtime); - rawtime = addDays(rawtime, -dataLogRetentionInDays + 1); + rawtime = add_days(rawtime, -dataLogRetentionInDays + 1); timeinfo = localtime(&rawtime); - //ESP_LOGD(TAG, "dataLogRetentionInDays: %d", dataLogRetentionInDays); + // ESP_LOGD(TAG, "dataLogRetentionInDays: %d", dataLogRetentionInDays); strftime(cmpfilename, 30, datafile.c_str(), timeinfo); - //ESP_LOGD(TAG, "data file name to compare: %s", cmpfilename); + // ESP_LOGD(TAG, "data file name to compare: %s", cmpfilename); DIR *dir = opendir(dataroot.c_str()); - if (!dir) { + if (!dir) + { ESP_LOGE(TAG, "Failed to stat dir: %s", dataroot.c_str()); return; } @@ -376,46 +389,52 @@ void ClassLogFile::RemoveOldDataLog() struct dirent *entry; int deleted = 0; int notDeleted = 0; - while ((entry = readdir(dir)) != NULL) { - if (entry->d_type == DT_REG) { - //ESP_LOGD(TAG, "Compare data file: %s to %s", entry->d_name, cmpfilename); - if ((strlen(entry->d_name) == strlen(cmpfilename)) && (strcmp(entry->d_name, cmpfilename) < 0)) { - //ESP_LOGD(TAG, "delete data file: %s", entry->d_name); - std::string filepath = dataroot + "/" + entry->d_name; - if (unlink(filepath.c_str()) == 0) { - deleted ++; - } else { - ESP_LOGE(TAG, "can't delete file: %s", entry->d_name); - notDeleted ++; + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_type == DT_REG) + { + // ESP_LOGD(TAG, "Compare data file: %s to %s", entry->d_name, cmpfilename); + if ((strlen(entry->d_name) == strlen(cmpfilename)) && (strcmp(entry->d_name, cmpfilename) < 0)) + { + // ESP_LOGD(TAG, "delete data file: %s", entry->d_name); + std::string filepath = dataroot + "/" + entry->d_name; + if (unlink(filepath.c_str()) == 0) + { + deleted++; } - } else { - notDeleted ++; + else + { + ESP_LOGE(TAG, "can't delete file: %s", entry->d_name); + notDeleted++; + } + } + else + { + notDeleted++; } } } - ESP_LOGD(TAG, "data files deleted: %d | files not deleted (incl. leer.txt): %d", deleted, notDeleted); + ESP_LOGD(TAG, "data files deleted: %d | files not deleted (incl. leer.txt): %d", deleted, notDeleted); closedir(dir); } - bool ClassLogFile::CreateLogDirectories() { bool bRetval = false; - bRetval = MakeDir("/sdcard/log"); - bRetval = MakeDir("/sdcard/log/data"); - bRetval = MakeDir("/sdcard/log/analog"); - bRetval = MakeDir("/sdcard/log/digit"); - bRetval = MakeDir("/sdcard/log/message"); - bRetval = MakeDir("/sdcard/log/source"); + bRetval = make_dir("/sdcard/log"); + bRetval = make_dir("/sdcard/log/data"); + bRetval = make_dir("/sdcard/log/analog"); + bRetval = make_dir("/sdcard/log/digit"); + bRetval = make_dir("/sdcard/log/message"); + bRetval = make_dir("/sdcard/log/source"); return bRetval; } - ClassLogFile::ClassLogFile(std::string _logroot, std::string _logfile, std::string _logdatapath, std::string _datafile) { logroot = _logroot; - logfile = _logfile; + logfile = _logfile; datafile = _datafile; dataroot = _logdatapath; logFileRetentionInDays = 3; diff --git a/code/components/jomjol_logfile/ClassLogFile.h b/code/components/jomjol_logfile/ClassLogFile.h index 7ce4058e..9da87429 100644 --- a/code/components/jomjol_logfile/ClassLogFile.h +++ b/code/components/jomjol_logfile/ClassLogFile.h @@ -3,11 +3,9 @@ #ifndef CLASSLOGFILE_H #define CLASSLOGFILE_H - #include #include "esp_log.h" - class ClassLogFile { private: @@ -19,6 +17,7 @@ private: unsigned short dataLogRetentionInDays; bool doDataLogToSD; esp_log_level_t loglevel; + public: ClassLogFile(std::string _logpath, std::string _logfile, std::string _logdatapath, std::string _datafile); @@ -39,9 +38,8 @@ public: void RemoveOldLogFile(); void RemoveOldDataLog(); -// void WriteToData(std::string _ReturnRawValue, std::string _ReturnValue, std::string _ReturnPreValue, std::string _ErrorMessageText, std::string _digit, std::string _analog); - void WriteToData(std::string _timestamp, std::string _name, std::string _ReturnRawValue, std::string _ReturnValue, std::string _ReturnPreValue, std::string _ReturnRateValue, std::string _ReturnChangeAbsolute, std::string _ErrorMessageText, std::string _digit, std::string _analog); - + // void WriteToData(std::string _ReturnRawValue, std::string _ReturnValue, std::string _ReturnPreValue, std::string _ErrorMessageText, std::string _digit, std::string _analog); + void WriteToData(std::string _timestamp, std::string _name, std::string _ReturnRawValue, std::string _ReturnValue, std::string _ReturnPreValue, std::string _ReturnRateValue, std::string _ReturnChangeAbsolute, std::string _ErrorMessageText, std::string _digit, std::string _analog); std::string GetCurrentFileName(); std::string GetCurrentFileNameData(); @@ -49,4 +47,4 @@ public: extern ClassLogFile LogFile; -#endif //CLASSLOGFILE_H \ No newline at end of file +#endif // CLASSLOGFILE_H diff --git a/code/components/jomjol_mqtt/CMakeLists.txt b/code/components/jomjol_mqtt/CMakeLists.txt index 8aadb832..16999874 100644 --- a/code/components/jomjol_mqtt/CMakeLists.txt +++ b/code/components/jomjol_mqtt/CMakeLists.txt @@ -1,5 +1,5 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." - REQUIRES esp_timer esp-tflite-micro mqtt jomjol_tfliteclass jomjol_helper jomjol_mqtt jomjol_wlan json) + INCLUDE_DIRS "." "../../include" + REQUIRES esp_timer esp-tflite-micro mqtt jomjol_tfliteclass jomjol_helper jomjol_mqtt jomjol_network json) diff --git a/code/components/jomjol_mqtt/interface_mqtt.cpp b/code/components/jomjol_mqtt/interface_mqtt.cpp index 22489d5b..3b4aa444 100644 --- a/code/components/jomjol_mqtt/interface_mqtt.cpp +++ b/code/components/jomjol_mqtt/interface_mqtt.cpp @@ -1,86 +1,71 @@ -#ifdef ENABLE_MQTT +#include "defines.h" + #include "interface_mqtt.h" -#include "esp_log.h" -#if DEBUG_DETAIL_ON - #include "esp_timer.h" -#endif -#include "connect_wlan.h" -#include "mqtt_client.h" +#include +#include +#include +#include + +#include "Helper.h" + +#include "connect_wifi_sta.h" +#include "read_network_config.h" + #include "ClassLogFile.h" #include "MainFlowControl.h" -#include "cJSON.h" -#include "../../include/defines.h" - -#if DEBUG_DETAIL_ON -#include "esp_timer.h" -#endif static const char *TAG = "MQTT IF"; -std::map>* connectFunktionMap = NULL; -std::map>* subscribeFunktionMap = NULL; +std::map> *connectFunktionMap = NULL; +std::map> *subscribeFunktionMap = NULL; + +esp_mqtt_client_handle_t client = NULL; + +std::string caCert = ""; +std::string clientCert = ""; +std::string clientKey = ""; int failedOnRound = -1; int MQTTReconnectCnt = 0; - -esp_mqtt_event_id_t esp_mqtt_ID = MQTT_EVENT_ANY; -// ESP_EVENT_ANY_ID -bool mqtt_enabled = false; -bool mqtt_configOK = false; -bool mqtt_initialized = false; -bool mqtt_connected = false; - -esp_mqtt_client_handle_t client = NULL; -std::string uri, client_id, lwt_topic, lwt_connected, lwt_disconnected, user, password, maintopic, domoticz_in_topic; -std::string caCert, clientCert, clientKey; -bool validateServerCert = true; -int keepalive; -bool SetRetainFlag; void (*callbackOnConnected)(std::string, bool) = NULL; -bool MQTTPublish(std::string _key, std::string _content, int qos, bool retained_flag) +bool MQTTPublish(std::string _key, std::string _content, int qos, bool retained_flag) { - if (!mqtt_enabled) { // MQTT sevice not started / configured (MQTT_Init not called before) + if (!mqtt_controll_config.mqtt_enabled) + { + // MQTT sevice not started / configured (MQTT_Init not called before) return false; } - if (failedOnRound == getCountFlowRounds()) { // we already failed in this round, do not retry until the next round + if (failedOnRound == getCountFlowRounds()) + { + // we already failed in this round, do not retry until the next round return true; // Fail quietly } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("MQTT Publish"); - #endif - MQTT_Init(); // Re-Init client if not initialized yet/anymore - if (mqtt_initialized && mqtt_connected) { - #ifdef DEBUG_DETAIL_ON - long long int starttime = esp_timer_get_time(); - #endif + if (mqtt_controll_config.mqtt_initialized && mqtt_controll_config.mqtt_connected) + { int msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, qos, retained_flag); - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Publish msg_id %d in %lld ms", msg_id, (esp_timer_get_time() - starttime)/1000); - #endif - if (msg_id == -1) { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Failed to publish topic '" + _key + "', re-trying..."); - #ifdef DEBUG_DETAIL_ON - starttime = esp_timer_get_time(); - #endif + if (msg_id == -1) + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Failed to publish topic '" + _key + "', re-trying..."); msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, qos, retained_flag); - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "Publish msg_id %d in %lld ms", msg_id, (esp_timer_get_time() - starttime)/1000); - #endif - if (msg_id == -1) { + + if (msg_id == -1) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to publish topic '" + _key + "', skipping all MQTT publishings in this round!"); failedOnRound = getCountFlowRounds(); return false; } } - if (_content.length() > 80) { // Truncate message if too long + if (_content.length() > 80) + { + // Truncate message if too long _content.resize(80); _content.append(".."); } @@ -88,208 +73,216 @@ bool MQTTPublish(std::string _key, std::string _content, int qos, bool retained_ LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Published topic: " + _key + ", content: " + _content); return true; } - else { + else + { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publish skipped. Client not initalized or not connected. (topic: " + _key + ")"); return false; } } -static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) { +static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) +{ std::string topic = ""; - switch (event->event_id) { - case MQTT_EVENT_BEFORE_CONNECT: - mqtt_initialized = true; - break; - - case MQTT_EVENT_CONNECTED: + switch (event->event_id) + { + case MQTT_EVENT_BEFORE_CONNECT: + mqtt_controll_config.mqtt_initialized = true; + break; + + case MQTT_EVENT_CONNECTED: + MQTTReconnectCnt = 0; + mqtt_controll_config.mqtt_initialized = true; + mqtt_controll_config.mqtt_connected = true; + MQTTconnected(); + break; + + case MQTT_EVENT_DISCONNECTED: + mqtt_controll_config.mqtt_connected = false; + MQTTReconnectCnt++; + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected, trying to reconnect"); + + if (MQTTReconnectCnt >= 5) + { MQTTReconnectCnt = 0; - mqtt_initialized = true; - mqtt_connected = true; - MQTTconnected(); - break; - - case MQTT_EVENT_DISCONNECTED: - mqtt_connected = false; - MQTTReconnectCnt++; - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected, trying to reconnect"); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Disconnected, multiple reconnect attempts failed, still retrying..."); + } + break; - if (MQTTReconnectCnt >= 5) { - MQTTReconnectCnt = 0; - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Disconnected, multiple reconnect attempts failed, still retrying..."); - } - break; - - case MQTT_EVENT_SUBSCRIBED: - ESP_LOGD(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); - break; - - case MQTT_EVENT_UNSUBSCRIBED: - ESP_LOGD(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); - break; - - case MQTT_EVENT_PUBLISHED: - ESP_LOGD(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); - break; - - case MQTT_EVENT_DATA: - ESP_LOGD(TAG, "MQTT_EVENT_DATA"); - ESP_LOGD(TAG, "TOPIC=%.*s", event->topic_len, event->topic); - ESP_LOGD(TAG, "DATA=%.*s", event->data_len, event->data); - topic.assign(event->topic, event->topic_len); - if (subscribeFunktionMap != NULL) { - if (subscribeFunktionMap->find(topic) != subscribeFunktionMap->end()) { - ESP_LOGD(TAG, "call subcribe function for topic %s", topic.c_str()); - (*subscribeFunktionMap)[topic](topic, event->data, event->data_len); - } - } else { - ESP_LOGW(TAG, "no handler available\r\n"); - } - break; - - case MQTT_EVENT_ERROR: - // http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033 --> chapter 3.2.2.3 + case MQTT_EVENT_SUBSCRIBED: + ESP_LOGD(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + break; - // The server does not support the level of the MQTT protocol requested by the client - // NOTE: Only protocol 3.1.1 is supported (refer to setting in sdkconfig) - if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_PROTOCOL) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, unacceptable protocol version (0x01)"); - } - // The client identifier is correct UTF-8 but not allowed by the server - // e.g. clientID empty (cannot be the case -> default set in firmware) - else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_ID_REJECTED) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, identifier rejected (0x02)"); - } - // The network connection has been made but the MQTT service is unavailable - else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_SERVER_UNAVAILABLE) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, Server unavailable (0x03)"); - } - // The data in the user name or password is malformed - else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_BAD_USERNAME) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, malformed data in username or password (0x04)"); - } - // The client is not authorized to connect - else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_NOT_AUTHORIZED) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, not authorized. Check username/password (0x05)"); - } - else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Other event id:" + event->error_handle->connect_return_code); - ESP_LOGE(TAG, "Other event id:%d", event->error_handle->connect_return_code); - } + case MQTT_EVENT_UNSUBSCRIBED: + ESP_LOGD(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "MQTT_EVENT_ERROR - esp_mqtt_error_codes:"); - ESP_LOGD(TAG, "error_type:%d", event->error_handle->error_type); - ESP_LOGD(TAG, "connect_return_code:%d", event->error_handle->connect_return_code); - ESP_LOGD(TAG, "esp_transport_sock_errno:%d", event->error_handle->esp_transport_sock_errno); - ESP_LOGD(TAG, "esp_tls_last_esp_err:%d", event->error_handle->esp_tls_last_esp_err); - ESP_LOGD(TAG, "esp_tls_stack_err:%d", event->error_handle->esp_tls_stack_err); - ESP_LOGD(TAG, "esp_tls_cert_verify_flags:%d", event->error_handle->esp_tls_cert_verify_flags); - #endif + case MQTT_EVENT_PUBLISHED: + ESP_LOGD(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); + break; - break; - - default: - ESP_LOGD(TAG, "Other event id:%d", event->event_id); - break; + case MQTT_EVENT_DATA: + ESP_LOGD(TAG, "MQTT_EVENT_DATA"); + ESP_LOGD(TAG, "TOPIC=%.*s", event->topic_len, event->topic); + ESP_LOGD(TAG, "DATA=%.*s", event->data_len, event->data); + topic.assign(event->topic, event->topic_len); + if (subscribeFunktionMap != NULL) + { + if (subscribeFunktionMap->find(topic) != subscribeFunktionMap->end()) + { + ESP_LOGD(TAG, "call subcribe function for topic %s", topic.c_str()); + (*subscribeFunktionMap)[topic](topic, event->data, event->data_len); + } + } + else + { + ESP_LOGW(TAG, "no handler available\r\n"); + } + break; + + case MQTT_EVENT_ERROR: + // http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033 --> chapter 3.2.2.3 + + // The server does not support the level of the MQTT protocol requested by the client + // NOTE: Only protocol 3.1.1 is supported (refer to setting in sdkconfig) + if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_PROTOCOL) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, unacceptable protocol version (0x01)"); + } + // The client identifier is correct UTF-8 but not allowed by the server + // e.g. clientID empty (cannot be the case -> default set in firmware) + else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_ID_REJECTED) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, identifier rejected (0x02)"); + } + // The network connection has been made but the MQTT service is unavailable + else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_SERVER_UNAVAILABLE) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, Server unavailable (0x03)"); + } + // The data in the user name or password is malformed + else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_BAD_USERNAME) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, malformed data in username or password (0x04)"); + } + // The client is not authorized to connect + else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_NOT_AUTHORIZED) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, not authorized. Check username/password (0x05)"); + } + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Other event id:" + event->error_handle->connect_return_code); + ESP_LOGE(TAG, "Other event id:%d", event->error_handle->connect_return_code); + } + break; + + default: + ESP_LOGD(TAG, "Other event id:%d", event->event_id); + break; } return ESP_OK; } -static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { +static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) +{ ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, (int)event_id); - mqtt_event_handler_cb((esp_mqtt_event_handle_t) event_data); + mqtt_event_handler_cb((esp_mqtt_event_handle_t)event_data); } -bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password, - std::string _maintopic, std::string _domoticz_in_topic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected, - std::string _cacertfilename, bool _validateServerCert, std::string _clientcertfilename, std::string _clientkeyfilename, - int _keepalive, bool _SetRetainFlag, void *_callbackOnConnected) { - if ((_mqttURI.length() == 0) || (_maintopic.length() == 0) || (_clientid.length() == 0)) +bool MQTT_Configure(void *_callbackOnConnected) +{ + if ((mqtt_controll_config.uri.length() == 0) || (mqtt_controll_config.maintopic.length() == 0) || (mqtt_controll_config.clientname.length() == 0)) { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Init aborted! Config error (URI, MainTopic or ClientID missing)"); return false; } - uri = _mqttURI; - client_id = _clientid; - lwt_topic = _maintopic + "/" + _lwt; - lwt_connected = _lwt_connected; - lwt_disconnected = _lwt_disconnected; - keepalive = _keepalive; - SetRetainFlag = _SetRetainFlag; - maintopic = _maintopic; - domoticz_in_topic = _domoticz_in_topic; - callbackOnConnected = ( void (*)(std::string, bool) )(_callbackOnConnected); + callbackOnConnected = (void (*)(std::string, bool))(_callbackOnConnected); - if (_clientcertfilename.length() && _clientkeyfilename.length()) { - std::ifstream cert_ifs(_clientcertfilename); - if (cert_ifs.is_open()) { + if (mqtt_controll_config.clientCertFilename.length() && mqtt_controll_config.clientKeyFilename.length()) + { + std::ifstream cert_ifs(mqtt_controll_config.clientCertFilename); + if (cert_ifs.is_open()) + { std::string cert_content((std::istreambuf_iterator(cert_ifs)), (std::istreambuf_iterator())); clientCert = cert_content; cert_ifs.close(); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientCert: " + _clientcertfilename); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientCert: " + mqtt_controll_config.clientCertFilename); } - else { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "could not open clientCert: " + _clientcertfilename); + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "could not open clientCert: " + mqtt_controll_config.clientCertFilename); } - std::ifstream key_ifs(_clientkeyfilename); - if (key_ifs.is_open()) { + std::ifstream key_ifs(mqtt_controll_config.clientKeyFilename); + if (key_ifs.is_open()) + { std::string key_content((std::istreambuf_iterator(key_ifs)), (std::istreambuf_iterator())); clientKey = key_content; key_ifs.close(); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientKey: " + _clientkeyfilename); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientKey: " + mqtt_controll_config.clientKeyFilename); } - else { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "could not open clientKey: " + _clientkeyfilename); + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "could not open clientKey: " + mqtt_controll_config.clientKeyFilename); } } - if (_cacertfilename.length()) { - std::ifstream ca_ifs(_cacertfilename); - if (ca_ifs.is_open()) { + if (mqtt_controll_config.caCertFilename.length()) + { + std::ifstream ca_ifs(mqtt_controll_config.caCertFilename); + if (ca_ifs.is_open()) + { std::string content((std::istreambuf_iterator(ca_ifs)), (std::istreambuf_iterator())); caCert = content; ca_ifs.close(); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using caCert: " + _cacertfilename); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using caCert: " + mqtt_controll_config.caCertFilename); } - else { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "could not open caCert: " + _cacertfilename); + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "could not open caCert: " + mqtt_controll_config.caCertFilename); } } - validateServerCert = _validateServerCert; +#ifdef __HIDE_PASSWORD + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, + "URI: " + mqtt_controll_config.uri + ", clientname: " + mqtt_controll_config.clientname + + ", user: " + mqtt_controll_config.user + ", password: XXXXXXXX, maintopic: " + mqtt_controll_config.maintopic + + ", last-will-topic: " + mqtt_controll_config.lwt_topic + + ", keepAlive: " + std::to_string(mqtt_controll_config.keepAlive) + + ", RetainFlag: " + std::to_string(mqtt_controll_config.retainFlag)); +#else + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, + "URI: " + mqtt_controll_config.uri + ", clientname: " + mqtt_controll_config.clientname + + ", user: " + mqtt_controll_config.user + ", password: " + mqtt_controll_config.password + + ", maintopic: " + mqtt_controll_config.maintopic + ", last-will-topic: " + mqtt_controll_config.lwt_topic + + ", keepAlive: " + std::to_string(mqtt_controll_config.keepAlive) + + ", RetainFlag: " + std::to_string(mqtt_controll_config.retainFlag)); +#endif - if (_user.length() && _password.length()){ - user = _user; - password = _password; - } - - #ifdef __HIDE_PASSWORD - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "URI: " + uri + ", clientname: " + client_id + ", user: " + user + ", password: XXXXXXXX, maintopic: " - + maintopic + ", last-will-topic: " + lwt_topic + ", keepAlive: " + std::to_string(keepalive) + ", RetainFlag: " + std::to_string(SetRetainFlag)); - #else - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "URI: " + uri + ", clientname: " + client_id + ", user: " + user + ", password: " + password + ", maintopic: " - + maintopic + ", last-will-topic: " + lwt_topic + ", keepAlive: " + std::to_string(keepalive) + ", RetainFlag: " + std::to_string(SetRetainFlag)); - #endif - - mqtt_configOK = true; + mqtt_controll_config.mqtt_configOK = true; return true; } -int MQTT_Init() { - if (mqtt_initialized) { +int MQTT_Init() +{ + if (mqtt_controll_config.mqtt_initialized) + { return 0; } - if (mqtt_configOK) { - mqtt_enabled = true; - } else { + if (mqtt_controll_config.mqtt_configOK) + { + mqtt_controll_config.mqtt_enabled = true; + } + else + { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init called, but client is not yet configured."); return 0; } - if (!getWIFIisConnected()) { + if (!get_wifi_sta_is_connected()) + { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init called, but WIFI is not yet connected."); return 0; } @@ -297,31 +290,33 @@ int MQTT_Init() { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init"); MQTTdestroy_client(false); - esp_mqtt_client_config_t mqtt_cfg = { }; + esp_mqtt_client_config_t mqtt_cfg = {}; - mqtt_cfg.broker.address.uri = uri.c_str(); - mqtt_cfg.credentials.client_id = client_id.c_str(); - mqtt_cfg.network.disable_auto_reconnect = false; // Reconnection routine active (Default: false) - mqtt_cfg.network.reconnect_timeout_ms = 15000; // Try to reconnect to broker (Default: 10000ms) - mqtt_cfg.network.timeout_ms = 20000; // Network Timeout (Default: 10000ms) - mqtt_cfg.session.message_retransmit_timeout = 3000; // Time after message resent when broker not acknowledged (QoS1, QoS2) - mqtt_cfg.session.last_will.topic = lwt_topic.c_str(); + mqtt_cfg.broker.address.uri = mqtt_controll_config.uri.c_str(); + mqtt_cfg.credentials.client_id = mqtt_controll_config.clientname.c_str(); + mqtt_cfg.network.disable_auto_reconnect = false; // Reconnection routine active (Default: false) + mqtt_cfg.network.reconnect_timeout_ms = 15000; // Try to reconnect to broker (Default: 10000ms) + mqtt_cfg.network.timeout_ms = 20000; // Network Timeout (Default: 10000ms) + mqtt_cfg.session.message_retransmit_timeout = 3000; // Time after message resent when broker not acknowledged (QoS1, QoS2) + mqtt_cfg.session.last_will.topic = mqtt_controll_config.lwt_topic.c_str(); mqtt_cfg.session.last_will.retain = 1; - mqtt_cfg.session.last_will.msg = lwt_disconnected.c_str(); - mqtt_cfg.session.last_will.msg_len = (int)(lwt_disconnected.length()); - mqtt_cfg.session.keepalive = keepalive; - mqtt_cfg.buffer.size = 2048; // size of MQTT send/receive buffer + mqtt_cfg.session.last_will.msg = mqtt_controll_config.lwt_disconnected.c_str(); + mqtt_cfg.session.last_will.msg_len = (int)(mqtt_controll_config.lwt_disconnected.length()); + mqtt_cfg.session.keepalive = mqtt_controll_config.keepAlive; + mqtt_cfg.buffer.size = 2048; // size of MQTT send/receive buffer - if (caCert.length()) { + if (caCert.length()) + { mqtt_cfg.broker.verification.certificate = caCert.c_str(); mqtt_cfg.broker.verification.certificate_len = caCert.length() + 1; // Skip any validation of server certificate CN field, this reduces the // security of TLS and makes the *MQTT* client susceptible to MITM attacks - mqtt_cfg.broker.verification.skip_cert_common_name_check = !validateServerCert; + mqtt_cfg.broker.verification.skip_cert_common_name_check = !mqtt_controll_config.validateServerCert; } - if (clientCert.length() && clientKey.length()) { + if (clientCert.length() && clientKey.length()) + { mqtt_cfg.credentials.authentication.certificate = clientCert.c_str(); mqtt_cfg.credentials.authentication.certificate_len = clientCert.length() + 1; @@ -329,77 +324,79 @@ int MQTT_Init() { mqtt_cfg.credentials.authentication.key_len = clientKey.length() + 1; } - if (user.length() && password.length()){ - mqtt_cfg.credentials.username = user.c_str(); - mqtt_cfg.credentials.authentication.password = password.c_str(); + if (mqtt_controll_config.user.length() && mqtt_controll_config.password.length()) + { + mqtt_cfg.credentials.username = mqtt_controll_config.user.c_str(); + mqtt_cfg.credentials.authentication.password = mqtt_controll_config.password.c_str(); } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("MQTT Client Init"); - #endif - client = esp_mqtt_client_init(&mqtt_cfg); if (client) { - esp_err_t ret = esp_mqtt_client_register_event(client, esp_mqtt_ID, mqtt_event_handler, client); + esp_err_t ret = esp_mqtt_client_register_event(client, mqtt_controll_config.esp_mqtt_ID, mqtt_event_handler, client); if (ret != ESP_OK) { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Could not register event (ret=" + std::to_string(ret) + ")!"); - mqtt_initialized = false; + mqtt_controll_config.mqtt_initialized = false; return -1; } - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("MQTT Client Start"); - #endif ret = esp_mqtt_client_start(client); if (ret != ESP_OK) { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Client start failed (retval=" + std::to_string(ret) + ")!"); - mqtt_initialized = false; + mqtt_controll_config.mqtt_initialized = false; return -1; } - else { + else + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Client started, waiting for established connection..."); - mqtt_initialized = true; + mqtt_controll_config.mqtt_initialized = true; return 1; } } else { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Init failed, no handle created!"); - mqtt_initialized = false; + mqtt_controll_config.mqtt_initialized = false; return -1; } - } -void MQTTdestroy_client(bool _disable = false) { - if (client) { - if (mqtt_connected) { - MQTTdestroySubscribeFunction(); +void MQTTdestroy_client(bool _disable = false) +{ + if (client) + { + if (mqtt_controll_config.mqtt_connected) + { + MQTTdestroySubscribeFunction(); esp_mqtt_client_disconnect(client); - mqtt_connected = false; + mqtt_controll_config.mqtt_connected = false; } esp_mqtt_client_stop(client); esp_mqtt_client_destroy(client); client = NULL; - mqtt_initialized = false; + mqtt_controll_config.mqtt_initialized = false; } - if (_disable) // Disable MQTT service, avoid restart with MQTTPublish - mqtt_configOK = false; + if (_disable) + { + // Disable MQTT service, avoid restart with MQTTPublish + mqtt_controll_config.mqtt_configOK = false; + } } -bool getMQTTisEnabled() { - return mqtt_enabled; +bool getMQTTisEnabled() +{ + return mqtt_controll_config.mqtt_enabled; } -bool getMQTTisConnected() { - return mqtt_connected; +bool getMQTTisConnected() +{ + return mqtt_controll_config.mqtt_connected; } -bool mqtt_handler_flow_start(std::string _topic, char* _data, int _data_len) +bool mqtt_handler_flow_start(std::string _topic, char *_data, int _data_len) { ESP_LOGD(TAG, "Handler called: topic %s, data %.*s", _topic.c_str(), _data_len, _data); @@ -407,47 +404,60 @@ bool mqtt_handler_flow_start(std::string _topic, char* _data, int _data_len) return ESP_OK; } -bool mqtt_handler_set_prevalue(std::string _topic, char* _data, int _data_len) +bool mqtt_handler_set_prevalue(std::string _topic, char *_data, int _data_len) { - //ESP_LOGD(TAG, "Handler called: topic %s, data %.*s", _topic.c_str(), _data_len, _data); - //example: {"numbersname": "main", "value": 12345.1234567} + // ESP_LOGD(TAG, "Handler called: topic %s, data %.*s", _topic.c_str(), _data_len, _data); + // example: {"numbersname": "main", "value": 12345.1234567} - if (_data_len > 0) { // Check if data length > 0 + if (_data_len > 0) + { + // Check if data length > 0 cJSON *jsonData = cJSON_Parse(_data); cJSON *numbersname = cJSON_GetObjectItemCaseSensitive(jsonData, "numbersname"); cJSON *value = cJSON_GetObjectItemCaseSensitive(jsonData, "value"); - if (cJSON_IsString(numbersname) && (numbersname->valuestring != NULL)) { // Check if numbersname is valid - if (cJSON_IsNumber(value)) { // Check if value is a number - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_set_prevalue called: numbersname: " + std::string(numbersname->valuestring) + - ", value: " + std::to_string(value->valuedouble)); - if (flowctrl.UpdatePrevalue(std::to_string(value->valuedouble), std::string(numbersname->valuestring), true)) { + if (cJSON_IsString(numbersname) && (numbersname->valuestring != NULL)) + { + // Check if numbersname is valid + if (cJSON_IsNumber(value)) + { + // Check if value is a number + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_set_prevalue called: numbersname: " + std::string(numbersname->valuestring) + ", value: " + std::to_string(value->valuedouble)); + if (flowctrl.UpdatePrevalue(std::to_string(value->valuedouble), std::string(numbersname->valuestring), true)) + { cJSON_Delete(jsonData); return ESP_OK; } } - else { + else + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: value not a valid number (\"value\": 12345.12345)"); } } - else { + else + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: numbersname not a valid string (\"numbersname\": \"main\")"); } cJSON_Delete(jsonData); } - else { + else + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: handler called, but no data received"); } return ESP_FAIL; } -void MQTTconnected(){ - if (mqtt_connected) { +void MQTTconnected() +{ + if (mqtt_controll_config.mqtt_connected) + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Connected to broker"); - - if (connectFunktionMap != NULL) { - for(std::map>::iterator it = connectFunktionMap->begin(); it != connectFunktionMap->end(); ++it) { + + if (connectFunktionMap != NULL) + { + for (std::map>::iterator it = connectFunktionMap->begin(); it != connectFunktionMap->end(); ++it) + { it->second(); ESP_LOGD(TAG, "call connect function %s", it->first.c_str()); } @@ -456,58 +466,71 @@ void MQTTconnected(){ // Subcribe to topics // Note: Further subsriptions are handled in GPIO class //***************************************** - std::function subHandler1 = mqtt_handler_flow_start; - MQTTregisterSubscribeFunction(maintopic + "/ctrl/flow_start", subHandler1); // subcribe to maintopic/ctrl/flow_start + std::function subHandler1 = mqtt_handler_flow_start; + MQTTregisterSubscribeFunction(mqtt_controll_config.maintopic + "/ctrl/flow_start", subHandler1); // subcribe to maintopic/ctrl/flow_start - std::function subHandler2 = mqtt_handler_set_prevalue; - MQTTregisterSubscribeFunction(maintopic + "/ctrl/set_prevalue", subHandler2); // subcribe to maintopic/ctrl/set_prevalue + std::function subHandler2 = mqtt_handler_set_prevalue; + MQTTregisterSubscribeFunction(mqtt_controll_config.maintopic + "/ctrl/set_prevalue", subHandler2); // subcribe to maintopic/ctrl/set_prevalue - if (subscribeFunktionMap != NULL) { - for(std::map>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it) { + if (subscribeFunktionMap != NULL) + { + for (std::map>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it) + { int msg_id = esp_mqtt_client_subscribe(client, it->first.c_str(), 0); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "topic " + it->first + " subscribe successful"); } } /* Send Static Topics and Homeassistant Discovery */ - if (callbackOnConnected) { // Call onConnected callback routine --> mqtt_server - callbackOnConnected(maintopic, SetRetainFlag); + if (callbackOnConnected) + { + // Call onConnected callback routine --> mqtt_server + callbackOnConnected(mqtt_controll_config.maintopic, mqtt_controll_config.retainFlag); } } } -void MQTTregisterConnectFunction(std::string name, std::function func){ +void MQTTregisterConnectFunction(std::string name, std::function func) +{ ESP_LOGD(TAG, "MQTTregisteronnectFunction %s\r\n", name.c_str()); - if (connectFunktionMap == NULL) { + if (connectFunktionMap == NULL) + { connectFunktionMap = new std::map>(); } - if ((*connectFunktionMap)[name] != NULL) { + if ((*connectFunktionMap)[name] != NULL) + { ESP_LOGW(TAG, "connect function %s already registred", name.c_str()); return; } (*connectFunktionMap)[name] = func; - if (mqtt_connected) { + if (mqtt_controll_config.mqtt_connected) + { func(); } } -void MQTTunregisterConnectFunction(std::string name){ +void MQTTunregisterConnectFunction(std::string name) +{ ESP_LOGD(TAG, "unregisterConnnectFunction %s\r\n", name.c_str()); - if ((connectFunktionMap != NULL) && (connectFunktionMap->find(name) != connectFunktionMap->end())) { + if ((connectFunktionMap != NULL) && (connectFunktionMap->find(name) != connectFunktionMap->end())) + { connectFunktionMap->erase(name); } } -void MQTTregisterSubscribeFunction(std::string topic, std::function func){ +void MQTTregisterSubscribeFunction(std::string topic, std::function func) +{ ESP_LOGD(TAG, "registerSubscribeFunction %s", topic.c_str()); - if (subscribeFunktionMap == NULL) { - subscribeFunktionMap = new std::map>(); + if (subscribeFunktionMap == NULL) + { + subscribeFunktionMap = new std::map>(); } - if ((*subscribeFunktionMap)[topic] != NULL) { + if ((*subscribeFunktionMap)[topic] != NULL) + { ESP_LOGW(TAG, "topic %s already registered for subscription", topic.c_str()); return; } @@ -515,10 +538,14 @@ void MQTTregisterSubscribeFunction(std::string topic, std::function>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it) { +void MQTTdestroySubscribeFunction() +{ + if (subscribeFunktionMap != NULL) + { + if (mqtt_controll_config.mqtt_connected) + { + for (std::map>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it) + { int msg_id = esp_mqtt_client_unsubscribe(client, it->first.c_str()); ESP_LOGD(TAG, "topic %s unsubscribe successful, msg_id=%d", it->first.c_str(), msg_id); } @@ -529,4 +556,3 @@ void MQTTdestroySubscribeFunction(){ subscribeFunktionMap = NULL; } } -#endif //ENABLE_MQTT diff --git a/code/components/jomjol_mqtt/interface_mqtt.h b/code/components/jomjol_mqtt/interface_mqtt.h index 29b52bb0..3676f6ce 100644 --- a/code/components/jomjol_mqtt/interface_mqtt.h +++ b/code/components/jomjol_mqtt/interface_mqtt.h @@ -1,5 +1,3 @@ -#ifdef ENABLE_MQTT - #pragma once #ifndef INTERFACE_MQTT_H @@ -9,23 +7,19 @@ #include #include -bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password, - std::string _maintopic, std::string _domoticz_in_topic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected, - std::string _cacertfilename, bool _validateServerCert, std::string _clientcertfilename, std::string _clientkeyfilename, - int _keepalive, bool SetRetainFlag, void *callbackOnConnected); +bool MQTT_Configure(void *callbackOnConnected); int MQTT_Init(); void MQTTdestroy_client(bool _disable); -bool MQTTPublish(std::string _key, std::string _content, int qos, bool retained_flag = 1); // retained Flag as Standart +bool MQTTPublish(std::string _key, std::string _content, int qos, bool retained_flag = 1); // retained Flag as Standart bool getMQTTisEnabled(); bool getMQTTisConnected(); void MQTTregisterConnectFunction(std::string name, std::function func); void MQTTunregisterConnectFunction(std::string name); -void MQTTregisterSubscribeFunction(std::string topic, std::function func); +void MQTTregisterSubscribeFunction(std::string topic, std::function func); void MQTTdestroySubscribeFunction(); void MQTTconnected(); -#endif //INTERFACE_MQTT_H -#endif //#ENABLE_MQTT +#endif // INTERFACE_MQTT_H diff --git a/code/components/jomjol_mqtt/mqtt_outbox.c b/code/components/jomjol_mqtt/mqtt_outbox.c deleted file mode 100644 index c001a8b2..00000000 --- a/code/components/jomjol_mqtt/mqtt_outbox.c +++ /dev/null @@ -1,305 +0,0 @@ -/* This is a modification of https://github.com/espressif/esp-mqtt/blob/master/lib/mqtt_outbox.c - * to use the PSRAM instead of the internal heap. -*/ -#include "mqtt_outbox.h" -#include -#include -#include "sys/queue.h" -#include "esp_log.h" -#include "esp_heap_caps.h" - -/* Enable this to use the PSRAM for MQTT Publishing. - * This saves 10 kBytes of RAM, see https://github.com/jomjol/AI-on-the-edge-device/pull/2113 - * However we can run into PSRAM fragmentation issues, leading to insufficient large blocks to load the model. - * See https://github.com/jomjol/AI-on-the-edge-device/issues/2200 */ -#define USE_PSRAM - -#ifdef CONFIG_MQTT_CUSTOM_OUTBOX -static const char *TAG = "outbox"; - -typedef struct outbox_item { - char *buffer; - int len; - int msg_id; - int msg_type; - int msg_qos; - outbox_tick_t tick; - pending_state_t pending; - STAILQ_ENTRY(outbox_item) next; -} outbox_item_t; - -STAILQ_HEAD(outbox_list_t, outbox_item); - - -outbox_handle_t outbox_init(void) -{ -#ifdef USE_PSRAM - outbox_handle_t outbox = heap_caps_calloc(1, sizeof(struct outbox_list_t), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); -#else - outbox_handle_t outbox = calloc(1, sizeof(struct outbox_list_t)); -#endif - //ESP_MEM_CHECK(TAG, outbox, return NULL); - STAILQ_INIT(outbox); - return outbox; -} - -outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handle_t message, outbox_tick_t tick) -{ -#ifdef USE_PSRAM - outbox_item_handle_t item = heap_caps_calloc(1, sizeof(outbox_item_t), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); -#else - outbox_item_handle_t item = calloc(1, sizeof(outbox_item_t)); -#endif - //ESP_MEM_CHECK(TAG, item, return NULL); - item->msg_id = message->msg_id; - item->msg_type = message->msg_type; - item->msg_qos = message->msg_qos; - item->tick = tick; - item->len = message->len + message->remaining_len; - item->pending = QUEUED; -#ifdef USE_PSRAM - item->buffer = heap_caps_malloc(message->len + message->remaining_len, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); -#else - item->buffer = malloc(message->len + message->remaining_len); -#endif - /*ESP_MEM_CHECK(TAG, item->buffer, { - free(item); - return NULL; - });*/ - memcpy(item->buffer, message->data, message->len); - if (message->remaining_data) { - memcpy(item->buffer + message->len, message->remaining_data, message->remaining_len); - } - STAILQ_INSERT_TAIL(outbox, item, next); - ESP_LOGD(TAG, "ENQUEUE msgid=%d, msg_type=%d, len=%d, size=%d", message->msg_id, message->msg_type, message->len + message->remaining_len, outbox_get_size(outbox)); - return item; -} - -outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id) -{ - outbox_item_handle_t item; - STAILQ_FOREACH(item, outbox, next) { - if (item->msg_id == msg_id) { - return item; - } - } - return NULL; -} - -outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pending, outbox_tick_t *tick) -{ - outbox_item_handle_t item; - STAILQ_FOREACH(item, outbox, next) { - if (item->pending == pending) { - if (tick) { - *tick = item->tick; - } - return item; - } - } - return NULL; -} - -esp_err_t outbox_delete_item(outbox_handle_t outbox, outbox_item_handle_t item_to_delete) -{ - outbox_item_handle_t item; - STAILQ_FOREACH(item, outbox, next) { - if (item == item_to_delete) { - STAILQ_REMOVE(outbox, item, outbox_item, next); -#ifdef USE_PSRAM - heap_caps_free(item->buffer); - heap_caps_free(item); -#else - free(item->buffer); - free(item); -#endif - return ESP_OK; - } - } - return ESP_FAIL; -} - -uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t *msg_id, int *msg_type, int *qos) -{ - if (item) { - *len = item->len; - *msg_id = item->msg_id; - *msg_type = item->msg_type; - *qos = item->msg_qos; - return (uint8_t *)item->buffer; - } - return NULL; -} - -esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type) -{ - outbox_item_handle_t item, tmp; - STAILQ_FOREACH_SAFE(item, outbox, next, tmp) { - if (item->msg_id == msg_id && (0xFF & (item->msg_type)) == msg_type) { - STAILQ_REMOVE(outbox, item, outbox_item, next); -#ifdef USE_PSRAM - heap_caps_free(item->buffer); - heap_caps_free(item); -#else - free(item->buffer); - free(item); -#endif - ESP_LOGD(TAG, "DELETED msgid=%d, msg_type=%d, remain size=%d", msg_id, msg_type, outbox_get_size(outbox)); - return ESP_OK; - } - - } - return ESP_FAIL; -} -esp_err_t outbox_delete_msgid(outbox_handle_t outbox, int msg_id) -{ - outbox_item_handle_t item, tmp; - STAILQ_FOREACH_SAFE(item, outbox, next, tmp) { - if (item->msg_id == msg_id) { - STAILQ_REMOVE(outbox, item, outbox_item, next); -#ifdef USE_PSRAM - heap_caps_free(item->buffer); - heap_caps_free(item); -#else - free(item->buffer); - free(item); -#endif - } - - } - return ESP_OK; -} -esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id, pending_state_t pending) -{ - outbox_item_handle_t item = outbox_get(outbox, msg_id); - if (item) { - item->pending = pending; - return ESP_OK; - } - return ESP_FAIL; -} - -pending_state_t outbox_item_get_pending(outbox_item_handle_t item) -{ - if (item) { - return item->pending; - } - return QUEUED; -} - -esp_err_t outbox_set_tick(outbox_handle_t outbox, int msg_id, outbox_tick_t tick) -{ - outbox_item_handle_t item = outbox_get(outbox, msg_id); - if (item) { - item->tick = tick; - return ESP_OK; - } - return ESP_FAIL; -} - -esp_err_t outbox_delete_msgtype(outbox_handle_t outbox, int msg_type) -{ - outbox_item_handle_t item, tmp; - STAILQ_FOREACH_SAFE(item, outbox, next, tmp) { - if (item->msg_type == msg_type) { - STAILQ_REMOVE(outbox, item, outbox_item, next); -#ifdef USE_PSRAM - heap_caps_free(item->buffer); - heap_caps_free(item); -#else - free(item->buffer); - free(item); -#endif - } - - } - return ESP_OK; -} -int outbox_delete_single_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout) -{ - int msg_id = -1; - outbox_item_handle_t item; - STAILQ_FOREACH(item, outbox, next) { - if (current_tick - item->tick > timeout) { - STAILQ_REMOVE(outbox, item, outbox_item, next); - -#ifdef USE_PSRAM - heap_caps_free(item->buffer); -#else - free(item->buffer); -#endif - - msg_id = item->msg_id; - -#ifdef USE_PSRAM - heap_caps_free(item); -#else - free(item); -#endif - - return msg_id; - } - - } - return msg_id; -} - -int outbox_delete_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout) -{ - int deleted_items = 0; - outbox_item_handle_t item, tmp; - STAILQ_FOREACH_SAFE(item, outbox, next, tmp) { - if (current_tick - item->tick > timeout) { - STAILQ_REMOVE(outbox, item, outbox_item, next); -#ifdef USE_PSRAM - heap_caps_free(item->buffer); - heap_caps_free(item); -#else - free(item->buffer); - free(item); -#endif - deleted_items ++; - } - - } - return deleted_items; -} - -int outbox_get_size(outbox_handle_t outbox) -{ - int siz = 0; - outbox_item_handle_t item; - STAILQ_FOREACH(item, outbox, next) { - // Suppressing "use after free" warning as this could happen only if queue is in inconsistent state - // which never happens if STAILQ interface used - siz += item->len; // NOLINT(clang-analyzer-unix.Malloc) - } - return siz; -} - -void outbox_delete_all_items(outbox_handle_t outbox) -{ - outbox_item_handle_t item, tmp; - STAILQ_FOREACH_SAFE(item, outbox, next, tmp) { - STAILQ_REMOVE(outbox, item, outbox_item, next); -#ifdef USE_PSRAM - heap_caps_free(item->buffer); - heap_caps_free(item); -#else - free(item->buffer); - free(item); -#endif - } -} -void outbox_destroy(outbox_handle_t outbox) -{ - outbox_delete_all_items(outbox); - -#ifdef USE_PSRAM - heap_caps_free(outbox); -#else - free(outbox); -#endif -} - -#endif /* CONFIG_MQTT_CUSTOM_OUTBOX */ diff --git a/code/components/jomjol_mqtt/mqtt_outbox.h b/code/components/jomjol_mqtt/mqtt_outbox.h deleted file mode 100644 index 6004a4af..00000000 --- a/code/components/jomjol_mqtt/mqtt_outbox.h +++ /dev/null @@ -1,66 +0,0 @@ -/* This is an adaption of https://github.com/espressif/esp-mqtt/blob/master/lib/include/mqtt_outbox.h - * This file is subject to the terms and conditions defined in - * file 'LICENSE', which is part of this source code package. - * Tuan PM - */ -#ifndef _MQTT_OUTOBX_H_ -#define _MQTT_OUTOBX_H_ -//#include "platform.h" -#include "esp_err.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct outbox_item; - -typedef struct outbox_list_t *outbox_handle_t; -typedef struct outbox_item *outbox_item_handle_t; -typedef struct outbox_message *outbox_message_handle_t; -typedef long long outbox_tick_t; - -typedef struct outbox_message { - uint8_t *data; - int len; - int msg_id; - int msg_qos; - int msg_type; - uint8_t *remaining_data; - int remaining_len; -} outbox_message_t; - -typedef enum pending_state { - QUEUED, - TRANSMITTED, - ACKNOWLEDGED, - CONFIRMED -} pending_state_t; - -outbox_handle_t outbox_init(void); -outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handle_t message, outbox_tick_t tick); -outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pending, outbox_tick_t *tick); -outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id); -uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t *msg_id, int *msg_type, int *qos); -esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type); -esp_err_t outbox_delete_msgid(outbox_handle_t outbox, int msg_id); -esp_err_t outbox_delete_msgtype(outbox_handle_t outbox, int msg_type); -esp_err_t outbox_delete_item(outbox_handle_t outbox, outbox_item_handle_t item); -int outbox_delete_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout); -/** - * @brief Deletes single expired message returning it's message id - * - * @return msg id of the deleted message, -1 if no expired message in the outbox - */ -int outbox_delete_single_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout); - -esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id, pending_state_t pending); -pending_state_t outbox_item_get_pending(outbox_item_handle_t item); -esp_err_t outbox_set_tick(outbox_handle_t outbox, int msg_id, outbox_tick_t tick); -int outbox_get_size(outbox_handle_t outbox); -void outbox_destroy(outbox_handle_t outbox); -void outbox_delete_all_items(outbox_handle_t outbox); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/code/components/jomjol_mqtt/server_mqtt.cpp b/code/components/jomjol_mqtt/server_mqtt.cpp index bace88e4..fce0d46f 100644 --- a/code/components/jomjol_mqtt/server_mqtt.cpp +++ b/code/components/jomjol_mqtt/server_mqtt.cpp @@ -1,391 +1,371 @@ -#ifdef ENABLE_MQTT +#include "defines.h" + +#include "server_mqtt.h" + #include #include #include #include +#include + +#include "defines.h" +#include "Helper.h" + +#include "connect_wifi_sta.h" +#include "read_network_config.h" -#include "esp_log.h" #include "ClassLogFile.h" -#include "connect_wlan.h" -#include "read_wlanini.h" -#include "server_mqtt.h" +#include "ClassFlowMQTT.h" #include "interface_mqtt.h" #include "time_sntp.h" -#include "../../include/defines.h" #include "basic_auth.h" - - static const char *TAG = "MQTT SERVER"; - -extern const char* libfive_git_version(void); -extern const char* libfive_git_revision(void); -extern const char* libfive_git_branch(void); +extern const char *libfive_git_version(void); +extern const char *libfive_git_revision(void); +extern const char *libfive_git_branch(void); extern std::string getFwVersion(void); -std::vector* NUMBERS; -bool HomeassistantDiscovery = false; -std::string meterType = ""; -std::string valueUnit = ""; -std::string timeUnit = ""; -std::string rateUnit = "Unit/Minute"; -float roundInterval; // Minutes -int keepAlive = 0; // Seconds -bool retainFlag; -static std::string maintopic, domoticzintopic; +std::vector *NUMBERS; + bool sendingOf_DiscoveryAndStaticTopics_scheduled = true; // Set it to true to make sure it gets sent at least once after startup - - -void mqttServer_setParameter(std::vector* _NUMBERS, int _keepAlive, float _roundInterval) { +void mqttServer_setParameter(std::vector *_NUMBERS) +{ NUMBERS = _NUMBERS; - keepAlive = _keepAlive; - roundInterval = _roundInterval; -} - -void mqttServer_setMeterType(std::string _meterType, std::string _valueUnit, std::string _timeUnit,std::string _rateUnit) { - meterType = _meterType; - valueUnit = _valueUnit; - timeUnit = _timeUnit; - rateUnit = _rateUnit; } /** * Takes any multi-level MQTT-topic and returns the last topic level as nodeId * see https://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices/ for details about MQTT topics -*/ -std::string createNodeId(std::string &topic) { + */ +std::string createNodeId(std::string &topic) +{ auto splitPos = topic.find_last_of('/'); return (splitPos == std::string::npos) ? topic : topic.substr(splitPos + 1); } -bool sendHomeAssistantDiscoveryTopic(std::string group, std::string field, - std::string name, std::string icon, std::string unit, std::string deviceClass, std::string stateClass, std::string entityCategory, - int qos) { +bool sendHomeAssistantDiscoveryTopic(std::string group, std::string field, std::string name, std::string icon, std::string unit, std::string deviceClass, std::string stateClass, std::string entityCategory, int qos) +{ std::string version = std::string(libfive_git_version()); - if (version == "") { + if (version == "") + { version = std::string(libfive_git_branch()) + " (" + std::string(libfive_git_revision()) + ")"; } - + std::string topicFull; - std::string configTopic; - std::string payload; - std::string component; + std::string configTopic = field; - configTopic = field; - - if (group != "" && (*NUMBERS).size() > 1) { // There is more than one meter, prepend the group so we can differentiate them + if (group != "" && (*NUMBERS).size() > 1) + { + // There is more than one meter, prepend the group so we can differentiate them configTopic = group + "_" + field; name = group + " " + name; } - if (field == "problem") { // Special case: Binary sensor which is based on error topic + std::string component; + if (field == "problem") + { + // Special case: Binary sensor which is based on error topic component = "binary_sensor"; } - else if (field == "flowstart") { // Special case: Button + else if (field == "flowstart") + { + // Special case: Button component = "button"; } - else { + else + { component = "sensor"; } - /** + /** * homeassistant needs the MQTT discovery topic according to the following structure: * //[/]/config - * if the main topic is embedded in a nested structure, we just use the last part as node_id + * if the main topic is embedded in a nested structure, we just use the last part as node_id * This means a maintopic "home/test/watermeter" is transformed to the discovery topic "homeassistant/sensor/watermeter/..." - */ - std::string node_id = createNodeId(maintopic); - topicFull = "homeassistant/" + component + "/" + node_id + "/" + configTopic + "/config"; - - /* See https://www.home-assistant.io/docs/mqtt/discovery/ */ - payload = string("{") + - "\"~\": \"" + maintopic + "\"," + - "\"unique_id\": \"" + maintopic + "-" + configTopic + "\"," + - "\"object_id\": \"" + maintopic + "_" + configTopic + "\"," + // Default entity ID; required for HA <= 2025.10 - "\"default_entity_id\": \"" + component + "." + maintopic + "_" + configTopic + "\"," + // Default entity ID; required in HA >=2026.4 - "\"name\": \"" + name + "\"," + - "\"icon\": \"mdi:" + icon + "\","; + */ + std::string node_id = createNodeId(mqtt_controll_config.maintopic); + topicFull = mqtt_controll_config.discoveryprefix + "/" + component + "/" + node_id + "/" + configTopic + "/config"; - if (group != "") { - if (field == "problem") { // Special case: Binary sensor which is based on error topic + /* See https://www.home-assistant.io/docs/mqtt/discovery/ */ + std::string payload = string("{") + "\"~\": \"" + mqtt_controll_config.maintopic + "\"," + + "\"unique_id\": \"" + mqtt_controll_config.maintopic + "-" + configTopic + "\"," + + "\"object_id\": \"" + mqtt_controll_config.maintopic + "_" + configTopic + "\"," + // Default entity ID; required for HA <= 2025.10 + "\"default_entity_id\": \"" + component + "." + mqtt_controll_config.maintopic + "_" + configTopic + "\"," + // Default entity ID; required in HA >=2026.4 + "\"name\": \"" + name + "\"," + "\"icon\": \"mdi:" + icon + "\","; + + if (group != "") + { + if (field == "problem") + { + // Special case: Binary sensor which is based on error topic payload += "\"state_topic\": \"~/" + group + "/error\","; payload += "\"value_template\": \"{{ 'OFF' if 'no error' in value else 'ON'}}\","; } - else { + else + { payload += "\"state_topic\": \"~/" + group + "/" + field + "\","; } } - else { - if (field == "problem") { // Special case: Binary sensor which is based on error topic + else + { + if (field == "problem") + { + // Special case: Binary sensor which is based on error topic payload += "\"state_topic\": \"~/error\","; payload += "\"value_template\": \"{{ 'OFF' if 'no error' in value else 'ON'}}\","; } - else if (field == "flowstart") { // Special case: Button + else if (field == "flowstart") + { + // Special case: Button payload += "\"cmd_t\":\"~/ctrl/flow_start\","; // Add command topic } - else { + else + { payload += "\"state_topic\": \"~/" + field + "\","; } } - if (unit != "") { + if (unit != "") + { payload += "\"unit_of_meas\": \"" + unit + "\","; } - if (deviceClass != "") { + if (deviceClass != "") + { payload += "\"device_class\": \"" + deviceClass + "\","; } - if (stateClass != "") { + if (stateClass != "") + { payload += "\"state_class\": \"" + stateClass + "\","; - } + } - if (entityCategory != "") { + if (entityCategory != "") + { payload += "\"entity_category\": \"" + entityCategory + "\","; - } + } - payload += - "\"availability_topic\": \"~/" + std::string(LWT_TOPIC) + "\"," + - "\"payload_available\": \"" + LWT_CONNECTED + "\"," + + payload += + "\"availability_topic\": \"~/" + std::string(LWT_TOPIC) + "\"," + + "\"payload_available\": \"" + LWT_CONNECTED + "\"," + "\"payload_not_available\": \"" + LWT_DISCONNECTED + "\","; - payload += string("\"device\": {") + - "\"identifiers\": [\"" + maintopic + "\"]," + - "\"name\": \"" + maintopic + "\"," + - "\"model\": \"Meter Digitizer\"," + - "\"manufacturer\": \"AI on the Edge Device\"," + - "\"sw_version\": \"" + version + "\"," + - "\"configuration_url\": \"http://" + *getIPAddress() + "\"" + - "}" + - "}"; + payload += string("\"device\": {") + "\"identifiers\": [\"" + mqtt_controll_config.maintopic + "\"]," + + "\"name\": \"" + mqtt_controll_config.maintopic + "\"," + + "\"model\": \"Meter Digitizer\"," + + "\"manufacturer\": \"AI on the Edge Device\"," + + "\"sw_version\": \"" + version + "\"," + + "\"configuration_url\": \"http://" + network_config.ipaddress + "\"" + "}" + "}"; return MQTTPublish(topicFull, payload, qos, true); } -bool MQTThomeassistantDiscovery(int qos) { - bool allSendsSuccessed = false; - - if (!getMQTTisConnected()) { +bool MQTThomeassistantDiscovery(int qos) +{ + if (!getMQTTisConnected()) + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Unable to send Homeassistant Discovery Topics, we are not connected to the MQTT broker!"); return false; } - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing Homeassistant Discovery topics (Meter Type: '" + meterType + "', Value Unit: '" + valueUnit + "' , Rate Unit: '" + rateUnit + "') ..."); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing Homeassistant Discovery topics (Meter Type: '" + mqtt_controll_config.meterType + "', Value Unit: '" + mqtt_controll_config.valueUnit + "' , Rate Unit: '" + mqtt_controll_config.rateUnit + "') ..."); - int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + bool allSendsSuccessed = false; + int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); - // Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "uptime", "Uptime", "progress-clock", "s", "duration", "measurement", "diagnostic", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "MAC", "MAC Address", "network-outline", "", "", "", "diagnostic", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "fwVersion", "Firmware Version", "application-outline", "", "", "", "diagnostic", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "hostname", "Hostname", "network-outline", "", "", "", "diagnostic", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "freeMem", "Free Memory", "memory", "B", "", "measurement", "diagnostic", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "wifiRSSI", "Wi-Fi RSSI", "wifi", "dBm", "signal_strength", "", "diagnostic", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "CPUtemp", "CPU Temperature", "thermometer", "°C", "temperature", "measurement", "diagnostic", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "interval", "Interval", "clock-time-eight-outline", "min", "", "measurement", "diagnostic", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "IP", "IP", "network-outline", "", "", "", "diagnostic", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "status", "Status", "list-status", "", "", "", "diagnostic", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "flowstart", "Manual Flow Start", "timer-play-outline", "", "", "", "", qos); + // Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "uptime", "Uptime", "progress-clock", "s", "duration", "measurement", "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "MAC", "MAC Address", "network-outline", "", "", "", "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "fwVersion", "Firmware Version", "application-outline", "", "", "", "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "hostname", "Hostname", "network-outline", "", "", "", "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "freeMem", "Free Memory", "memory", "B", "", "measurement", "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "wifiRSSI", "Wi-Fi RSSI", "wifi", "dBm", "signal_strength", "", "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "CPUtemp", "CPU Temperature", "thermometer", "°C", "temperature", "measurement", "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "interval", "Interval", "clock-time-eight-outline", "min", "", "measurement", "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "IP", "IP", "network-outline", "", "", "", "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "status", "Status", "list-status", "", "", "", "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "flowstart", "Manual Flow Start", "timer-play-outline", "", "", "", "", qos); - - for (int i = 0; i < (*NUMBERS).size(); ++i) { + for (int i = 0; i < (*NUMBERS).size(); ++i) + { std::string group = (*NUMBERS)[i]->name; - if (group == "default") { + if (group == "default") + { group = ""; } /* If "Allow neg. rate" is true, use "measurement" instead of "total_increasing" for the State Class, see https://github.com/jomjol/AI-on-the-edge-device/issues/3331 */ std::string value_state_class = "total_increasing"; - if (meterType == "temperature") { + if (mqtt_controll_config.meterType == "temperature") + { value_state_class = "measurement"; } - else if ((*NUMBERS)[i]->AllowNegativeRates) { + else if ((*NUMBERS)[i]->AllowNegativeRates) + { value_state_class = "total"; } /* Energy meters need a different Device Class, see https://github.com/jomjol/AI-on-the-edge-device/issues/3333 */ std::string rate_device_class = "volume_flow_rate"; - if (meterType == "energy") { + if (mqtt_controll_config.meterType == "energy") + { rate_device_class = "power"; } - // Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category | QoS - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", valueUnit, meterType, value_state_class, "", qos); // State Class = "total_increasing" if .AllowNegativeRates = false, "measurement" in case of a thermometer, else use "total". - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", valueUnit, meterType, value_state_class, "diagnostic", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "error", "Error", "alert-circle-outline", "", "", "", "diagnostic", qos); + // Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category | QoS + // State Class = "total_increasing" if .AllowNegativeRates = false, "measurement" in case of a thermometer, else use "total". + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", mqtt_controll_config.valueUnit, mqtt_controll_config.meterType, value_state_class, "", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", mqtt_controll_config.valueUnit, mqtt_controll_config.meterType, value_state_class, "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "error", "Error", "alert-circle-outline", "", "", "", "diagnostic", qos); /* Not announcing "rate" as it is better to use rate_per_time_unit resp. rate_per_digitization_round */ - // allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate", "Rate (Unit/Minute)", "swap-vertical", "", "", "", "", qos); // Legacy, always Unit per Minute - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_time_unit", "Rate (" + rateUnit + ")", "swap-vertical", rateUnit, rate_device_class, "measurement", "", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_digitization_round","Change since last Digitization round", "arrow-expand-vertical", valueUnit, "", "measurement", "", qos); // correctly the Unit is Unit/Interval! - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "timestamp", "Timestamp", "clock-time-eight-outline", "", "timestamp", "", "diagnostic", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "json", "JSON", "code-json", "", "", "", "diagnostic", qos); - allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "problem", "Problem", "alert-outline", "", "problem", "", "", qos); // Special binary sensor which is based on error topic + // allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate", "Rate (Unit/Minute)", "swap-vertical", "", "", "", "", qos); // Legacy, always Unit per Minute + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_time_unit", "Rate (" + mqtt_controll_config.rateUnit + ")", "swap-vertical", mqtt_controll_config.rateUnit, rate_device_class, "measurement", "", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_digitization_round", "Change since last Digitization round", "arrow-expand-vertical", mqtt_controll_config.valueUnit, "", "measurement", "", qos); // correctly the Unit is Unit/Interval! + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "timestamp", "Timestamp", "clock-time-eight-outline", "", "timestamp", "", "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "json", "JSON", "code-json", "", "", "", "diagnostic", qos); + allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "problem", "Problem", "alert-outline", "", "problem", "", "", qos); // Special binary sensor which is based on error topic } LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Successfully published all Homeassistant Discovery MQTT topics"); int aFreeInternalHeapSizeAfter = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); - int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before Publishing Homeassistand Discovery Topics: " + - to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " + - to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize)); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before Publishing Homeassistand Discovery Topics: " + to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " + to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize)); return allSendsSuccessed; } -bool publishSystemData(int qos) { - bool allSendsSuccessed = false; - - if (!getMQTTisConnected()) { +bool publishSystemData(int qos) +{ + if (!getMQTTisConnected()) + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Unable to send System Topics, we are not connected to the MQTT broker!"); return false; } - char tmp_char[50]; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing System MQTT topics..."); - int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + char tmp_char[50]; + bool allSendsSuccessed = false; + int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); - allSendsSuccessed |= MQTTPublish(maintopic + "/" + std::string(LWT_TOPIC), LWT_CONNECTED, qos, retainFlag); // Publish "connected" to maintopic/connection + allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + std::string(LWT_TOPIC), LWT_CONNECTED, qos, mqtt_controll_config.retainFlag); // Publish "connected" to maintopic/connection - sprintf(tmp_char, "%ld", (long)getUpTime()); - allSendsSuccessed |= MQTTPublish(maintopic + "/" + "uptime", std::string(tmp_char), qos, retainFlag); - - sprintf(tmp_char, "%lu", (long) getESPHeapSize()); - allSendsSuccessed |= MQTTPublish(maintopic + "/" + "freeMem", std::string(tmp_char), qos, retainFlag); + sprintf(tmp_char, "%ld", (long)get_uptime()); + allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "uptime", std::string(tmp_char), qos, mqtt_controll_config.retainFlag); - sprintf(tmp_char, "%d", get_WIFI_RSSI()); - allSendsSuccessed |= MQTTPublish(maintopic + "/" + "wifiRSSI", std::string(tmp_char), qos, retainFlag); + sprintf(tmp_char, "%lu", (long)get_heapsize()); + allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "freeMem", std::string(tmp_char), qos, mqtt_controll_config.retainFlag); - sprintf(tmp_char, "%d", (int)temperatureRead()); - allSendsSuccessed |= MQTTPublish(maintopic + "/" + "CPUtemp", std::string(tmp_char), qos, retainFlag); + sprintf(tmp_char, "%d", get_wifi_rssi()); + allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "wifiRSSI", std::string(tmp_char), qos, mqtt_controll_config.retainFlag); + + sprintf(tmp_char, "%d", (int)read_tempsensor()); + allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "CPUtemp", std::string(tmp_char), qos, mqtt_controll_config.retainFlag); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Successfully published all System MQTT topics"); - int aFreeInternalHeapSizeAfter = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); - int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + int aFreeInternalHeapSizeAfter = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before publishing System Topics: " + - to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " + - to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize)); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before publishing System Topics: " + to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " + to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize)); return allSendsSuccessed; } - -bool publishStaticData(int qos) { - bool allSendsSuccessed = false; - - if (!getMQTTisConnected()) { +bool publishStaticData(int qos) +{ + if (!getMQTTisConnected()) + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Unable to send Static Topics, we are not connected to the MQTT broker!"); return false; } LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing static MQTT topics..."); - int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + bool allSendsSuccessed = false; + int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); - allSendsSuccessed |= MQTTPublish(maintopic + "/" + "fwVersion", getFwVersion().c_str(), qos, retainFlag); - allSendsSuccessed |= MQTTPublish(maintopic + "/" + "MAC", getMac(), qos, retainFlag); - allSendsSuccessed |= MQTTPublish(maintopic + "/" + "IP", *getIPAddress(), qos, retainFlag); - allSendsSuccessed |= MQTTPublish(maintopic + "/" + "hostname", wlan_config.hostname, qos, retainFlag); + allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "fwVersion", getFwVersion().c_str(), qos, mqtt_controll_config.retainFlag); + allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "MAC", get_mac(), qos, mqtt_controll_config.retainFlag); + allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "IP", network_config.ipaddress, qos, mqtt_controll_config.retainFlag); + allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "hostname", network_config.hostname, qos, mqtt_controll_config.retainFlag); std::stringstream stream; - stream << std::fixed << std::setprecision(1) << roundInterval; // minutes - allSendsSuccessed |= MQTTPublish(maintopic + "/" + "interval", stream.str(), qos, retainFlag); + stream << std::fixed << std::setprecision(1) << mqtt_controll_config.roundInterval; // minutes + allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "interval", stream.str(), qos, mqtt_controll_config.retainFlag); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Successfully published all Static MQTT topics"); - int aFreeInternalHeapSizeAfter = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); - int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + int aFreeInternalHeapSizeAfter = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before Publishing Static Topics: " + - to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " + - to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize)); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before Publishing Static Topics: " + to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " + to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize)); return allSendsSuccessed; } - -esp_err_t scheduleSendingDiscovery_and_static_Topics(httpd_req_t *req) { +esp_err_t scheduleSendingDiscovery_and_static_Topics(httpd_req_t *req) +{ sendingOf_DiscoveryAndStaticTopics_scheduled = true; char msg[] = "MQTT Homeassistant Discovery and Static Topics scheduled"; - httpd_resp_send(req, msg, strlen(msg)); + httpd_resp_send(req, msg, strlen(msg)); return ESP_OK; } - -esp_err_t sendDiscovery_and_static_Topics(void) { - bool success = false; - - if (!sendingOf_DiscoveryAndStaticTopics_scheduled) { +esp_err_t sendDiscovery_and_static_Topics(void) +{ + if (!sendingOf_DiscoveryAndStaticTopics_scheduled) + { // Flag not set, nothing to do return ESP_OK; } - if (HomeassistantDiscovery) { + bool success = false; + + if (mqtt_controll_config.HomeAssistantDiscovery) + { success = MQTThomeassistantDiscovery(1); } success |= publishStaticData(1); - if (success) { // Success, clear the flag + if (success) + { + // Success, clear the flag sendingOf_DiscoveryAndStaticTopics_scheduled = false; return ESP_OK; } - else { + else + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "One or more MQTT topics failed to be published, will try sending them in the next round!"); /* Keep sendingOf_DiscoveryAndStaticTopics_scheduled set so we can retry after the next round */ return ESP_FAIL; } } - -void GotConnected(std::string maintopic, bool retainFlag) { +void GotConnected(std::string maintopic, bool retainFlag) +{ // Nothing to do } -void register_server_mqtt_uri(httpd_handle_t server) { - httpd_uri_t uri = { }; - uri.method = HTTP_GET; +std::string mqttServer_getMainTopic() +{ + return mqtt_controll_config.maintopic; +} - uri.uri = "/mqtt_publish_discovery"; +void mqtt_register_uri(httpd_handle_t server) +{ + httpd_uri_t uri = {}; + uri.method = HTTP_GET; + + uri.uri = "/mqtt_publish_discovery"; uri.handler = APPLY_BASIC_AUTH_FILTER(scheduleSendingDiscovery_and_static_Topics); - uri.user_ctx = (void*) ""; - httpd_register_uri_handler(server, &uri); + uri.user_ctx = (void *)""; + httpd_register_uri_handler(server, &uri); } - - -std::string getTimeUnit(void) { - return timeUnit; -} - - -void SetHomeassistantDiscoveryEnabled(bool enabled) { - HomeassistantDiscovery = enabled; -} - - -void setMqtt_Server_Retain(bool _retainFlag) { - retainFlag = _retainFlag; -} - -void mqttServer_setMainTopic( std::string _maintopic) { - maintopic = _maintopic; -} - -std::string mqttServer_getMainTopic() { - return maintopic; -} - -void mqttServer_setDmoticzInTopic( std::string _domoticzintopic) { - domoticzintopic = _domoticzintopic; -} - - -#endif //ENABLE_MQTT diff --git a/code/components/jomjol_mqtt/server_mqtt.h b/code/components/jomjol_mqtt/server_mqtt.h index 2754b404..cceadb2b 100644 --- a/code/components/jomjol_mqtt/server_mqtt.h +++ b/code/components/jomjol_mqtt/server_mqtt.h @@ -1,31 +1,19 @@ -#ifdef ENABLE_MQTT - #pragma once -#ifndef SERVERMQTT_H -#define SERVERMQTT_H +#ifndef SERVER_MQTT_H +#define SERVER_MQTT_H #include "ClassFlowDefineTypes.h" -void SetHomeassistantDiscoveryEnabled(bool enabled); -void mqttServer_setParameter(std::vector* _NUMBERS, int interval, float roundInterval); -void mqttServer_setMeterType(std::string meterType, std::string valueUnit, std::string timeUnit,std::string rateUnit); -void setMqtt_Server_Retain(bool SetRetainFlag); -void mqttServer_setMainTopic( std::string maintopic); -void mqttServer_setDmoticzInTopic( std::string domoticzintopic); - - +void mqttServer_setParameter(std::vector *_NUMBERS); std::string mqttServer_getMainTopic(); -void register_server_mqtt_uri(httpd_handle_t server); - bool publishSystemData(int qos); - -std::string getTimeUnit(void); void GotConnected(std::string maintopic, bool SetRetainFlag); esp_err_t sendDiscovery_and_static_Topics(void); std::string createNodeId(std::string &topic); -#endif //SERVERMQTT_H -#endif //ENABLE_MQTT \ No newline at end of file +void mqtt_register_uri(httpd_handle_t server); + +#endif // SERVER_MQTT_H diff --git a/code/components/jomjol_network/CMakeLists.txt b/code/components/jomjol_network/CMakeLists.txt new file mode 100644 index 00000000..3661f955 --- /dev/null +++ b/code/components/jomjol_network/CMakeLists.txt @@ -0,0 +1,7 @@ +FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) + +idf_component_register(SRCS ${app_sources} + INCLUDE_DIRS "." "../../include" + REQUIRES driver esp_eth esp_netif esp_wifi mdns nvs_flash jomjol_helper jomjol_mqtt wpa_supplicant) + + diff --git a/code/components/jomjol_wlan/basic_auth.cpp b/code/components/jomjol_network/basic_auth.cpp similarity index 75% rename from code/components/jomjol_wlan/basic_auth.cpp rename to code/components/jomjol_network/basic_auth.cpp index 09d46b54..947c01fa 100644 --- a/code/components/jomjol_wlan/basic_auth.cpp +++ b/code/components/jomjol_network/basic_auth.cpp @@ -1,24 +1,26 @@ #include "basic_auth.h" -#include "read_wlanini.h" +#include "read_network_config.h" #include #include - #define HTTPD_401 "401 UNAUTHORIZED" static const char *TAG = "HTTPAUTH"; -typedef struct { +typedef struct +{ const char *username; const char *password; } basic_auth_info_t; -basic_auth_info_t basic_auth_info = { NULL, NULL }; +basic_auth_info_t basic_auth_info = {NULL, NULL}; -void init_basic_auth() { - if (!wlan_config.http_username.empty() && !wlan_config.http_password.empty()) { - basic_auth_info.username = wlan_config.http_username.c_str(); - basic_auth_info.password = wlan_config.http_password.c_str(); +void init_basic_auth() +{ + if (!network_config.http_username.empty() && !network_config.http_password.empty()) + { + basic_auth_info.username = network_config.http_username.c_str(); + basic_auth_info.password = network_config.http_password.c_str(); } } @@ -29,7 +31,8 @@ static char *http_auth_basic(const char *username, const char *password) char *digest = NULL; size_t n = 0; asprintf(&user_info, "%s:%s", username, password); - if (!user_info) { + if (!user_info) + { ESP_LOGE(TAG, "No enough memory for user information"); return NULL; } @@ -38,9 +41,10 @@ static char *http_auth_basic(const char *username, const char *password) /* 6: The length of the "Basic " string * n: Number of bytes for a base64 encode format * 1: Number of bytes for a reserved which be used to fill zero - */ - digest = static_cast(calloc(1, 6 + n + 1)); - if (digest) { + */ + digest = static_cast(calloc(1, 6 + n + 1)); + if (digest) + { strcpy(digest, "Basic "); esp_crypto_base64_encode((unsigned char *)digest + 6, n, (size_t *)&out, (const unsigned char *)user_info, strlen(user_info)); } @@ -56,44 +60,58 @@ esp_err_t basic_auth_request_filter(httpd_req_t *req, esp_err_t original_handler char unauthorized[] = "You are not authorized to use this website!"; - if (basic_auth_info.username == NULL || basic_auth_info.password == NULL) { + if (basic_auth_info.username == NULL || basic_auth_info.password == NULL) + { ret = original_handler(req); - } else { + } + else + { buf_len = httpd_req_get_hdr_value_len(req, "Authorization") + 1; - if (buf_len > 1) { - buf = static_cast(calloc(1, buf_len)); - if (!buf) { + if (buf_len > 1) + { + buf = static_cast(calloc(1, buf_len)); + if (!buf) + { ESP_LOGE(TAG, "No enough memory for basic authorization"); return ESP_ERR_NO_MEM; } - if (httpd_req_get_hdr_value_str(req, "Authorization", buf, buf_len) == ESP_OK) { + if (httpd_req_get_hdr_value_str(req, "Authorization", buf, buf_len) == ESP_OK) + { ESP_LOGI(TAG, "Found header => Authorization: %s", buf); - } else { + } + else + { ESP_LOGE(TAG, "No auth value received"); } char *auth_credentials = http_auth_basic(basic_auth_info.username, basic_auth_info.password); - if (!auth_credentials) { + if (!auth_credentials) + { ESP_LOGE(TAG, "No enough memory for basic authorization credentials"); free(buf); return ESP_ERR_NO_MEM; } - if (strncmp(auth_credentials, buf, buf_len)) { + if (strncmp(auth_credentials, buf, buf_len)) + { ESP_LOGE(TAG, "Not authenticated"); httpd_resp_set_status(req, HTTPD_401); httpd_resp_set_type(req, HTTPD_TYPE_TEXT); httpd_resp_set_hdr(req, "Connection", "keep-alive"); httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"AIOTED\""); httpd_resp_send(req, unauthorized, strlen(unauthorized)); - } else { + } + else + { ESP_LOGI(TAG, "Authenticated calling http handler now!"); - ret=original_handler(req); + ret = original_handler(req); } free(auth_credentials); free(buf); - } else { + } + else + { ESP_LOGE(TAG, "No auth header received"); httpd_resp_set_status(req, HTTPD_401); httpd_resp_set_type(req, HTTPD_TYPE_TEXT); diff --git a/code/components/jomjol_wlan/basic_auth.h b/code/components/jomjol_network/basic_auth.h similarity index 59% rename from code/components/jomjol_wlan/basic_auth.h rename to code/components/jomjol_network/basic_auth.h index 51e021eb..2cc409a8 100644 --- a/code/components/jomjol_wlan/basic_auth.h +++ b/code/components/jomjol_network/basic_auth.h @@ -5,4 +5,4 @@ void init_basic_auth(); esp_err_t basic_auth_request_filter(httpd_req_t *req, esp_err_t original_handler(httpd_req_t *)); -#define APPLY_BASIC_AUTH_FILTER(method) [](httpd_req_t *req){ return basic_auth_request_filter(req, method); } +#define APPLY_BASIC_AUTH_FILTER(method) [](httpd_req_t *req) { return basic_auth_request_filter(req, method); } diff --git a/code/components/jomjol_network/connect_eth.cpp b/code/components/jomjol_network/connect_eth.cpp new file mode 100644 index 00000000..d4451b04 --- /dev/null +++ b/code/components/jomjol_network/connect_eth.cpp @@ -0,0 +1,420 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + * + * https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_eth.html#basic-ethernet-concepts + */ +#include "sdkconfig.h" + +#if (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500) + +#include "defines.h" +#include "Helper.h" + +#include "connect_eth.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "driver/gpio.h" +#include + +// define `gpio_pad_select_gpip` for newer versions of IDF +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)) +#include "esp_rom_gpio.h" +#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio +#endif + +#include "time_sntp.h" +#include "ClassLogFile.h" + +#include "statusled.h" +#include "read_network_config.h" + +#include "interface_mqtt.h" + +static const char *TAG = "ETH w5500"; + +static bool gpio_isr_svc_init_by_eth = false; // indicates that we initialized the GPIO ISR service + +esp_eth_handle_t my_w5500_handle = NULL; +esp_netif_t *my_w5500_netif = NULL; +esp_eth_netif_glue_handle_t my_w5500_netif_glue = NULL; + +static bool eth_initialized = false; +static bool eth_connected = false; +static bool eth_connection_uccessful = false; +static int eth_reconnect_cnt = 0; + +/** + * @brief SPI bus initialization (to be used by Ethernet SPI modules) + * + * @return + * - ESP_OK on success + */ +static esp_err_t spi_bus_init(void) +{ + esp_err_t retVal = ESP_OK; + + // Configure IO Pad as General Purpose IO, + // so that it can be connected to internal Matrix, + // then combined with one or more peripheral signals. + gpio_pad_select_gpio(ETH_SPI_EN); + + ESP_ERROR_CHECK(gpio_set_direction(ETH_SPI_EN, GPIO_MODE_OUTPUT)); + ESP_ERROR_CHECK(gpio_set_level(ETH_SPI_EN, 1)); + + vTaskDelay(pdMS_TO_TICKS(500)); + + if (ETH_SPI_INT0_GPIO != GPIO_NUM_NC) + { + // Install GPIO ISR handler to be able to service SPI Eth modules interrupts + ESP_LOGI(TAG, "spi_bus_init(): Install GPIO ISR handler..."); + retVal = gpio_install_isr_service(0); + + if (retVal == ESP_OK) + { + gpio_isr_svc_init_by_eth = true; + ESP_LOGI(TAG, "spi_bus_init(): GPIO ISR handler install successful"); + } + else if (retVal == ESP_ERR_INVALID_STATE) + { + ESP_LOGW(TAG, "spi_bus_init(): GPIO ISR handler has been already installed"); + retVal = ESP_OK; // ISR handler has been already installed so no issues + } + else + { + ESP_LOGE(TAG, "spi_bus_init(): GPIO ISR handler install failed"); + return retVal; + } + } + + // Init SPI bus + spi_bus_config_t buscfg = { + .mosi_io_num = ETH_SPI_MOSI_GPIO, + .miso_io_num = ETH_SPI_MISO_GPIO, + .sclk_io_num = ETH_SPI_SCLK_GPIO, + .quadwp_io_num = GPIO_NUM_NC, + .quadhd_io_num = GPIO_NUM_NC, + }; + + retVal = spi_bus_initialize(ETH_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO); + if (retVal != ESP_OK) + { + ESP_LOGE(TAG, "spi_bus_init(): SPI host #%d init failed", ETH_SPI_HOST); + } + + return retVal; +} + +static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_START) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Ethernet Started"); + // Typically nothing special here + } + else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_STOP) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Ethernet Stopped"); + eth_connected = false; + } + else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_CONNECTED) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Ethernet Link Up"); + + uint8_t mac_addr[6] = {0}; + /* we can get the ethernet driver handle from event data */ + esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data; + + ESP_ERROR_CHECK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr)); + ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); + eth_connected = true; // Not IP-ready until IP_EVENT_ETH_GOT_IP + } + else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_DISCONNECTED) + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Ethernet Link Down"); + eth_connected = false; + // Optionally, try to reconnect or handle fallback LED: + set_status_led(WLAN_CONN, 1, false); + eth_reconnect_cnt++; + } + else if (event_base == IP_EVENT && event_id == IP_EVENT_ETH_GOT_IP) + { + // We have a valid IP + eth_connection_uccessful = true; + eth_connected = true; + eth_reconnect_cnt = 0; + + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + + network_config.ipaddress = std::string(ip4addr_ntoa((const ip4_addr *)&event->ip_info.ip)); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Assigned IP: " + network_config.ipaddress); + + network_config.netmask = std::string(ip4addr_ntoa((const ip4_addr *)&event->ip_info.netmask)); + network_config.gateway = std::string(ip4addr_ntoa((const ip4_addr *)&event->ip_info.gw)); + + esp_netif_dns_info_t dnsInfo; + ESP_ERROR_CHECK(esp_netif_get_dns_info(event->esp_netif, ESP_NETIF_DNS_MAIN, &dnsInfo)); + network_config.dns = std::string(ip4addr_ntoa((const ip4_addr *)&dnsInfo.ip)); + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Assigned IP: " + network_config.ipaddress + ", Subnet: " + network_config.netmask + ", Gateway: " + network_config.gateway + ", DNS: " + network_config.dns); + + if (getMQTTisEnabled()) + { + vTaskDelay(500 / portTICK_PERIOD_MS); + MQTT_Init(); // Init when Ethernet is getting connected + } + + // Optionally set a status LED + set_status_led(WLAN_CONN, 0, true); // e.g., 0 means "ok" + } +} + +esp_err_t eth_init_W5500(void) +{ + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "W5500 Ethernet init..."); + + // Set log level for netif component to WARN level (default: INFO; only relevant for serial console) + // ******************************************** + esp_log_level_set("netif", ESP_LOG_WARN); + + esp_err_t retVal = esp_netif_init(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_netif_init: Error: " + std::to_string(retVal)); + return retVal; + } + + retVal = esp_event_loop_create_default(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_loop_create_default: Error: " + std::to_string(retVal)); + return retVal; + } + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "SPI init"); + retVal = spi_bus_init(); + if (retVal != ESP_OK) + { + ESP_LOGE(TAG, "Failed to init spi bus, error=0x%x", retVal); + return retVal; + } + + spi_device_interface_config_t devcfg = { + .mode = 0, // SPI mode 0 + .clock_speed_hz = ETH_SPI_CLOCK_MHZ * 1000 * 1000, + .spics_io_num = ETH_SPI_CS0_GPIO, + .queue_size = 30, + }; + + eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(ETH_SPI_HOST, &devcfg); + // Interrupt GPIO number, set -1 to not use interrupt and to poll rx status periodically + w5500_config.int_gpio_num = ETH_SPI_INT0_GPIO; + // Period in ms to poll rx status when interrupt mode is not used + if (gpio_isr_svc_init_by_eth == false) + { + w5500_config.poll_period_ms = ETH_SPI_POLLING0_MS; + } + + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config); + + // Update PHY config based on board specific configuration + eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); // apply default PHY configuration + phy_config.phy_addr = ETH_SPI_PHY_ADDR0; // alter the PHY address + phy_config.reset_gpio_num = ETH_SPI_PHY_RST0_GPIO; // alter the GPIO used for PHY reset + esp_eth_phy_t *phy = esp_eth_phy_new_w5500(&phy_config); + + esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Driver install..."); + retVal = esp_eth_driver_install(ð_config, &my_w5500_handle); + if (retVal != ESP_OK) + { + ESP_LOGE(TAG, "Failed to install Ethernet driver, error=0x%x", retVal); + return retVal; + } + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Driver installed"); + + uint8_t base_mac_addr[6]; + retVal = esp_efuse_mac_get_default(base_mac_addr); + if (retVal != ESP_OK) + { + ESP_LOGE(TAG, "Failed to get efuse base MAC, error=0x%x", retVal); + return retVal; + } + + uint8_t local_mac[6]; + retVal = esp_derive_local_mac(local_mac, base_mac_addr); + if (retVal != ESP_OK) + { + ESP_LOGE(TAG, "Failed to derive local MAC address from universal MAC address, error=0x%x", retVal); + return retVal; + } + + retVal = esp_eth_ioctl(my_w5500_handle, ETH_CMD_S_MAC_ADDR, local_mac); + if (retVal != ESP_OK) + { + ESP_LOGE(TAG, "Failed to set W5500 MAC, error=0x%x", retVal); + return retVal; + } + + esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); + my_w5500_netif = esp_netif_new(&netif_cfg); + my_w5500_netif_glue = esp_eth_new_netif_glue(my_w5500_handle); + + if (!network_config.ipaddress.empty() && !network_config.gateway.empty() && !network_config.netmask.empty()) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Manual interface config -> IP: " + network_config.ipaddress + ", Gateway: " + std::string(network_config.gateway) + ", Netmask: " + std::string(network_config.netmask)); + esp_netif_dhcpc_stop(my_w5500_netif); // Stop DHCP service + + esp_netif_ip_info_t ip_info; + int a, b, c, d; + string_to_ip4(network_config.ipaddress.c_str(), a, b, c, d); + IP4_ADDR(&ip_info.ip, a, b, c, d); // Set static IP address + + string_to_ip4(network_config.gateway.c_str(), a, b, c, d); + IP4_ADDR(&ip_info.gw, a, b, c, d); // Set gateway + + string_to_ip4(network_config.netmask.c_str(), a, b, c, d); + IP4_ADDR(&ip_info.netmask, a, b, c, d); // Set netmask + + esp_netif_set_ip_info(my_w5500_netif, &ip_info); // Set static IP configuration + + if (network_config.dns.empty()) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "No DNS server, use gateway"); + network_config.dns = network_config.gateway; + } + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Manual interface config -> DNS: " + network_config.dns); + } + + esp_netif_dns_info_t dns_info; + ip4_addr_t ip; + ip.addr = esp_ip4addr_aton(network_config.dns.c_str()); + ip_addr_set_ip4_u32(&dns_info.ip, ip.addr); + + retVal = esp_netif_set_dns_info(my_w5500_netif, ESP_NETIF_DNS_MAIN, &dns_info); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_netif_set_dns_info: Error: " + std::to_string(retVal)); + return retVal; + } + } + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Automatic interface config --> Use DHCP service"); + } + + // Register an event handler to the system event loop (legacy). + ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, ð_event_handler, NULL)); + + retVal = esp_netif_attach(my_w5500_netif, my_w5500_netif_glue); + if (retVal != ESP_OK) + { + ESP_LOGE(TAG, "Failed to attaches esp_netif instance to the io driver handle, error=0x%x", retVal); + return retVal; + } + + retVal = esp_eth_start(my_w5500_handle); + if (retVal != ESP_OK) + { + ESP_LOGE(TAG, "Failed to start Ethernet driver, error=0x%x", retVal); + return retVal; + } + + eth_initialized = true; + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "W5500 Ethernet init done"); + return ESP_OK; +} + +void eth_deinit_W5500(void) +{ + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "W5500 Ethernet deinit..."); + + esp_err_t retVal = ESP_OK; + eth_initialized = false; + + ESP_LOGD(TAG, "esp_eth_stop(my_w5500_handle)"); + ESP_ERROR_CHECK(esp_eth_stop(my_w5500_handle)); + + ESP_LOGD(TAG, "esp_eth_del_netif_glue(my_w5500_netif_glue)"); + ESP_ERROR_CHECK(esp_eth_del_netif_glue(my_w5500_netif_glue)); + + ESP_LOGD(TAG, "esp_netif_destroy(my_w5500_netif)"); + esp_netif_destroy(my_w5500_netif); + + ESP_LOGD(TAG, "esp_netif_deinit()"); + ESP_ERROR_CHECK(esp_netif_deinit()); + + esp_eth_mac_t *mac = NULL; + esp_eth_phy_t *phy = NULL; + + ESP_LOGD(TAG, "esp_eth_get_mac_instance(my_w5500_handle, &mac)"); + ESP_ERROR_CHECK(esp_eth_get_mac_instance(my_w5500_handle, &mac)); + + ESP_LOGD(TAG, "esp_eth_get_phy_instance(my_w5500_handle, &phy)"); + ESP_ERROR_CHECK(esp_eth_get_phy_instance(my_w5500_handle, &phy)); + + ESP_LOGD(TAG, "esp_eth_driver_uninstall(my_w5500_handle)"); + retVal = esp_eth_driver_uninstall(my_w5500_handle); + if (retVal != ESP_OK) + { + ESP_LOGE(TAG, "Ethernet driver %p uninstall failed", my_w5500_handle); + } + + ESP_LOGD(TAG, "spi_bus_free(ETH_SPI_HOST)"); + retVal = spi_bus_free(ETH_SPI_HOST); + if (retVal != ESP_OK) + { + ESP_LOGE(TAG, "spi_bus_free failed"); + } + + // We installed the GPIO ISR service so let's uninstall it too. + // BE CAREFUL HERE though since the service might be used by other functionality! + if (gpio_isr_svc_init_by_eth == true) + { + ESP_LOGW(TAG, "uninstalling GPIO ISR service!"); + gpio_uninstall_isr_service(); + } + + ESP_LOGD(TAG, "free(my_w5500_handle)"); + free(my_w5500_handle); + + ESP_LOGD(TAG, "esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, eth_event_handler)"); + ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, eth_event_handler)); + + ESP_LOGD(TAG, "esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler)"); + ESP_ERROR_CHECK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler)); + + ESP_LOGD(TAG, "esp_event_loop_delete_default()"); + ESP_ERROR_CHECK(esp_event_loop_delete_default()); + + ESP_LOGD(TAG, "gpio_set_level(ETH_SPI_EN, 0)"); + ESP_ERROR_CHECK(gpio_set_level(ETH_SPI_EN, 0)); + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "W5500 Ethernet deinit done"); +} + +bool getETHisConnected(void) +{ + return eth_connected; +} + +#endif // (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500) diff --git a/code/components/jomjol_network/connect_eth.h b/code/components/jomjol_network/connect_eth.h new file mode 100644 index 00000000..e1cb8087 --- /dev/null +++ b/code/components/jomjol_network/connect_eth.h @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + * + * https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_eth.html#basic-ethernet-concepts + */ +#include "sdkconfig.h" + +#if (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500) + +#pragma once + +#include +#include +#include + +#ifndef CONNECT_ETH_H +#define CONNECT_ETH_H + +// Initialize Lan or WLAN based on config file +esp_err_t eth_init_W5500(void); +void eth_deinit_W5500(void); + +bool getETHisConnected(void); + +#endif // CONNECT_ETH_H + +#endif // (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500) diff --git a/code/components/jomjol_network/connect_roaming.cpp b/code/components/jomjol_network/connect_roaming.cpp new file mode 100644 index 00000000..9c171d55 --- /dev/null +++ b/code/components/jomjol_network/connect_roaming.cpp @@ -0,0 +1,430 @@ +#include "connect_roaming.h" +#include "connect_wifi_sta.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "defines.h" +#include "Helper.h" + +#include "ClassLogFile.h" +#include "read_network_config.h" +#include "statusled.h" + +#include "../include/mdns.h" + +#if defined HEAP_TRACING_MAIN_WIFI +#include +#endif + +static const char *TAG = "WIFI ROAMING"; + +static bool wifi_sta_with_better_rssi = false; + +std::string get_auth_mode_name(const wifi_auth_mode_t auth_mode) +{ + std::string AuthModeNames[] = {"OPEN", "WEP", "WPA PSK", "WPA2 PSK", "WPA WPA2 PSK", "WPA2 ENTERPRISE", "WPA3 PSK", "WPA2 WPA3 PSK", "WAPI_PSK", "MAX"}; + return AuthModeNames[auth_mode]; +} + +#ifdef WLAN_USE_MESH_ROAMING +int rrm_ctx = 0; + +static inline uint32_t WPA_GET_LE32(const uint8_t *a) +{ + return ((uint32_t)a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]; +} + +#ifndef WLAN_EID_MEASURE_REPORT +#define WLAN_EID_MEASURE_REPORT 39 +#endif +#ifndef MEASURE_TYPE_LCI +#define MEASURE_TYPE_LCI 9 +#endif +#ifndef MEASURE_TYPE_LOCATION_CIVIC +#define MEASURE_TYPE_LOCATION_CIVIC 11 +#endif +#ifndef WLAN_EID_NEIGHBOR_REPORT +#define WLAN_EID_NEIGHBOR_REPORT 52 +#endif +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif + +#define MAX_NEIGHBOR_LEN 512 +static char *get_btm_neighbor_list(uint8_t *report, size_t report_len) +{ + size_t len = 0; + const uint8_t *data; + int ret = 0; + +/* + * Neighbor Report element (IEEE P802.11-REVmc/D5.0) + * BSSID[6] + * BSSID Information[4] + * Operating Class[1] + * Channel Number[1] + * PHY Type[1] + * Optional Subelements[variable] + */ +#define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1) + + if (!report || report_len == 0) + { + ESP_LOGD(TAG, "Roaming: RRM neighbor report is not valid"); + return NULL; + } + + char *buf = (char *)calloc(1, MAX_NEIGHBOR_LEN); + data = report; + + while (report_len >= 2 + NR_IE_MIN_LEN) + { + const uint8_t *nr; + char lci[256 * 2 + 1]; + char civic[256 * 2 + 1]; + uint8_t nr_len = data[1]; + const uint8_t *pos = data, *end; + + if (pos[0] != WLAN_EID_NEIGHBOR_REPORT || + nr_len < NR_IE_MIN_LEN) + { + ESP_LOGD(TAG, "Roaming CTRL: Invalid Neighbor Report element: id=%u len=%u", + data[0], nr_len); + ret = -1; + goto cleanup; + } + + if (2U + nr_len > report_len) + { + ESP_LOGD(TAG, "Roaming CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u", + data[0], report_len, nr_len); + ret = -1; + goto cleanup; + } + pos += 2; + end = pos + nr_len; + + nr = pos; + pos += NR_IE_MIN_LEN; + + lci[0] = '\0'; + civic[0] = '\0'; + while (end - pos > 2) + { + uint8_t s_id, s_len; + + s_id = *pos++; + s_len = *pos++; + if (s_len > end - pos) + { + ret = -1; + goto cleanup; + } + if (s_id == WLAN_EID_MEASURE_REPORT && s_len > 3) + { + /* Measurement Token[1] */ + /* Measurement Report Mode[1] */ + /* Measurement Type[1] */ + /* Measurement Report[variable] */ + switch (pos[2]) + { + case MEASURE_TYPE_LCI: + if (lci[0]) + break; + memcpy(lci, pos, s_len); + break; + case MEASURE_TYPE_LOCATION_CIVIC: + if (civic[0]) + break; + memcpy(civic, pos, s_len); + break; + } + } + + pos += s_len; + } + + ESP_LOGI(TAG, "Roaming: RMM neighbor report bssid=" MACSTR " info=0x%04lx op_class=%u chan=%u phy_type=%u%s%s%s%s", + MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN), + nr[ETH_ALEN + 4], nr[ETH_ALEN + 5], + nr[ETH_ALEN + 6], + lci[0] ? " lci=" : "", lci, + civic[0] ? " civic=" : "", civic); + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: RMM neighbor report BSSID: " + bssid_to_string((char *)nr) + ", Channel: " + std::to_string(nr[ETH_ALEN + 5])); + + /* neighbor start */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, " neighbor="); + /* bssid */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, MACSTR, MAC2STR(nr)); + /* , */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); + /* bssid info */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "0x%04lx", WPA_GET_LE32(nr + ETH_ALEN)); + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); + /* operating class */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 4]); + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); + /* channel number */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 5]); + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); + /* phy type */ + len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 6]); + /* optional elements, skip */ + + data = end; + report_len -= 2 + nr_len; + } + +cleanup: + if (ret < 0) + { + free(buf); + buf = NULL; + } + return buf; +} + +void neighbor_report_recv_cb(void *ctx, const uint8_t *report, size_t report_len) +{ + int *val = (int *)ctx; + uint8_t *pos = (uint8_t *)report; + int cand_list = 0; + int ret; + + if (!report) + { + ESP_LOGD(TAG, "Roaming: Neighbor report is null"); + return; + } + if (*val != rrm_ctx) + { + ESP_LOGE(TAG, "Roaming: rrm_ctx value didn't match, not initiated by us"); + return; + } + /* dump report info */ + ESP_LOGD(TAG, "Roaming: RRM neighbor report len=%d", report_len); + ESP_LOG_BUFFER_HEXDUMP(TAG, pos, report_len, ESP_LOG_DEBUG); + + /* create neighbor list */ + char *neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1); + + /* In case neighbor list is not present issue a scan and get the list from that */ + if (!neighbor_list) + { + /* issue scan */ + wifi_scan_config_t params; + memset(¶ms, 0, sizeof(wifi_scan_config_t)); + if (esp_wifi_scan_start(¶ms, true) < 0) + { + goto cleanup; + } + /* cleanup from net802.11 */ + uint16_t number = 1; + wifi_ap_record_t ap_records; + esp_wifi_scan_get_ap_records(&number, &ap_records); + cand_list = 1; + } + /* send AP btm query requesting to roam depending on candidate list of AP */ + // btm_query_reasons: https://github.com/espressif/esp-idf/blob/release/v4.4/components/wpa_supplicant/esp_supplicant/include/esp_wnm.h + ret = esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, neighbor_list, cand_list); // query reason 16 -> LOW RSSI --> (btm_query_reason)16 + ESP_LOGD(TAG, "neighbor_report_recv_cb retval - bss_transisition_query: %d", ret); + +cleanup: + if (neighbor_list) + { + free(neighbor_list); + } +} + +void esp_bss_rssi_low_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + int retval = -1; + wifi_event_bss_rssi_low_t *event = (wifi_event_bss_rssi_low_t *)event_data; + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming Event: RSSI " + std::to_string(event->rssi) + " < RSSI_Threshold " + std::to_string(network_config.rssi_threshold)); + + /* If RRM is supported, call RRM and then send BTM query to AP */ + if (esp_rrm_is_rrm_supported_connection() && esp_wnm_is_btm_supported_connection()) + { + /* Lets check channel conditions */ + rrm_ctx++; + + retval = esp_rrm_send_neighbor_rep_request(neighbor_report_recv_cb, &rrm_ctx); + ESP_LOGD(TAG, "esp_rrm_send_neighbor_rep_request retval: %d", retval); + if (retval == 0) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: RRM + BTM query sent"); + } + else + { + ESP_LOGD(TAG, "esp_rrm_send_neighbor_rep_request retval: %d", retval); + } + } + + /* If RRM is not supported or RRM request failed, send directly BTM query to AP */ + if (retval < 0 && esp_wnm_is_btm_supported_connection()) + { + // btm_query_reasons: https://github.com/espressif/esp-idf/blob/release/v4.4/components/wpa_supplicant/esp_supplicant/include/esp_wnm.h + retval = esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, NULL, 0); // query reason 16 -> LOW RSSI --> (btm_query_reason)16 + ESP_LOGD(TAG, "esp_wnm_send_bss_transition_mgmt_query retval: %d", retval); + if (retval == 0) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: BTM query sent"); + } + else + { + ESP_LOGD(TAG, "esp_wnm_send_bss_transition_mgmt_query retval: %d", retval); + } + } +} + +void printRoamingFeatureSupport(void) +{ + if (esp_rrm_is_rrm_supported_connection()) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Roaming: RRM (802.11k) supported by AP"); + } + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Roaming: RRM (802.11k) NOT supported by AP"); + } + + if (esp_wnm_is_btm_supported_connection()) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Roaming: BTM (802.11v) supported by AP"); + } + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Roaming: BTM (802.11v) NOT supported by AP"); + } +} + +#ifdef WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES +void wifi_roaming_query(void) +{ + /* Query only if WIFI is connected and feature is supported by AP */ + if (WIFIConnected && (esp_rrm_is_rrm_supported_connection() || esp_wnm_is_btm_supported_connection())) + { + /* Client is allowed to send query to AP for roaming request if RSSI is lower than threshold */ + /* Note 1: Set RSSI threshold funtion needs to be called to trigger WIFI_EVENT_STA_BSS_RSSI_LOW */ + /* Note 2: Additional querys will be sent after flow round is finshed --> server_tflite.cpp - function "task_autodoFlow" */ + /* Note 3: RSSI_Threshold = 0 --> Disable client query by application (WebUI parameter) */ + + if (network_config.rssi_threshold != 0 && get_WIFI_RSSI() != -127 && (get_WIFI_RSSI() < network_config.rssi_threshold)) + { + esp_wifi_set_rssi_threshold(network_config.rssi_threshold); + } + } +} +#endif // WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES +#endif // WLAN_USE_MESH_ROAMING + +#ifdef WLAN_USE_ROAMING_BY_SCANNING +void wifi_scan(void) +{ + wifi_scan_config_t wifi_scan_config; + memset(&wifi_scan_config, 0, sizeof(wifi_scan_config)); + + wifi_scan_config.ssid = (uint8_t *)network_config.ssid.c_str(); // only scan for configured SSID + wifi_scan_config.show_hidden = true; // scan also hidden SSIDs + wifi_scan_config.channel = 0; // scan all channels + + esp_wifi_scan_start(&wifi_scan_config, true); // not using event handler SCAN_DONE by purpose to keep SYS_EVENT heap smaller + // and the calling task task_autodoFlow is after scan is finish in wait state anyway + // Scan duration: ca. (120ms + 30ms) * Number of channels -> ca. 1,5 - 2s + + uint16_t max_number_of_ap_found = 10; // max. number of APs, value will be updated by function "esp_wifi_scan_get_ap_num" + esp_wifi_scan_get_ap_num(&max_number_of_ap_found); // get actual found APs + wifi_ap_record_t *wifi_ap_records = new wifi_ap_record_t[max_number_of_ap_found]; // Allocate necessary record datasets + if (wifi_ap_records == NULL) + { + esp_wifi_scan_get_ap_records(0, NULL); // free internal heap + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "wifi_scan: Failed to allocate heap for wifi_ap_records"); + return; + } + else + { + if (esp_wifi_scan_get_ap_records(&max_number_of_ap_found, wifi_ap_records) != ESP_OK) + { + // Retrieve results (and free internal heap) + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "wifi_scan: esp_wifi_scan_get_ap_records: Error retrieving datasets"); + delete[] wifi_ap_records; + return; + } + } + + wifi_ap_record_t currentAP; + esp_wifi_sta_get_ap_info(¤tAP); + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: Current AP BSSID=" + bssid_to_string((char *)currentAP.bssid)); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: Scan completed, APs found with configured SSID: " + std::to_string(max_number_of_ap_found)); + for (int i = 0; i < max_number_of_ap_found; i++) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: " + std::to_string(i + 1) + ": SSID=" + std::string((char *)wifi_ap_records[i].ssid) + ", BSSID=" + bssid_to_string((char *)wifi_ap_records[i].bssid) + ", RSSI=" + std::to_string(wifi_ap_records[i].rssi) + ", CH=" + std::to_string(wifi_ap_records[i].primary) + ", AUTH=" + get_auth_mode_name(wifi_ap_records[i].authmode)); + + // RSSI is better than actual RSSI + 5 --> Avoid switching to AP with roughly same RSSI + if (wifi_ap_records[i].rssi > (currentAP.rssi + 5) && (strcmp(bssid_to_string((char *)wifi_ap_records[i].bssid).c_str(), bssid_to_string((char *)currentAP.bssid).c_str()) != 0)) + { + wifi_sta_with_better_rssi = true; + } + } + + delete[] wifi_ap_records; +} + +void wifi_roaming_by_scanning(void) +{ +#ifdef HEAP_TRACING_MAIN_WIFI + ESP_LOGI(TAG, "---- HEAP_TRACING_MAIN_WIFI ----"); + ESP_ERROR_CHECK(heap_trace_start(HEAP_TRACE_LEAKS)); +#endif + + if (network_config.rssi_threshold != 0 && get_wifi_rssi() != -127 && (get_wifi_rssi() < network_config.rssi_threshold)) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: Start scan of all channels for SSID " + network_config.ssid); + wifi_scan(); + + if (wifi_sta_with_better_rssi) + { + wifi_sta_with_better_rssi = false; + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Roaming: AP with better RSSI in range, disconnecting to switch AP..."); + esp_wifi_disconnect(); + } + else + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: Scan completed, stay on current AP"); + } + } + +#ifdef HEAP_TRACING_MAIN_WIFI + ESP_ERROR_CHECK(heap_trace_stop()); + heap_trace_dump(); +#endif +} +#endif // WLAN_USE_ROAMING_BY_SCANNING diff --git a/code/components/jomjol_network/connect_roaming.h b/code/components/jomjol_network/connect_roaming.h new file mode 100644 index 00000000..9d85b212 --- /dev/null +++ b/code/components/jomjol_network/connect_roaming.h @@ -0,0 +1,43 @@ +#pragma once + +#ifndef CONNECT_ROAMING_H +#define CONNECT_ROAMING_H + +#include "sdkconfig.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "defines.h" +#include "Helper.h" + +std::string get_auth_mode_name(const wifi_auth_mode_t auth_mode); + +#if (defined WLAN_USE_MESH_ROAMING) +extern void esp_bss_rssi_low_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); +#endif + +#if (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES) +void wifi_roaming_query(void); +#endif + +#ifdef WLAN_USE_ROAMING_BY_SCANNING +void wifi_roaming_by_scanning(void); +#endif + +#endif // CONNECT_ROAMING_H diff --git a/code/components/jomjol_network/connect_wifi_ap.cpp b/code/components/jomjol_network/connect_wifi_ap.cpp new file mode 100644 index 00000000..8c30aa22 --- /dev/null +++ b/code/components/jomjol_network/connect_wifi_ap.cpp @@ -0,0 +1,167 @@ +#include "defines.h" +#include "Helper.h" + +#include "connect_wifi_ap.h" + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_mac.h" +#include "esp_wifi.h" + +#include "esp_log.h" +#include "nvs_flash.h" + +#include "stdio.h" +#include "time_sntp.h" + +#include "ClassLogFile.h" +#include "server_help.h" + +#include "statusled.h" +#include "server_ota.h" +#include "basic_auth.h" + +#include "lwip/err.h" +#include "lwip/sys.h" + +static const char *TAG = "WIFI AP"; + +esp_netif_t *my_ap_netif = NULL; +wifi_init_config_t my_ap_wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); +wifi_config_t my_ap_wifi_config = {}; + +static bool wifi_ap_initialized = false; +static bool wifi_ap_connected = false; +static bool wifi_ap_connection_successful = false; +static int wifi_ap_reconnect_cnt = 0; + +static void wifi_ap_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + if (event_id == WIFI_EVENT_AP_STACONNECTED) + { + wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *)event_data; + ESP_LOGI(TAG, "station " MACSTR " join, AID=%d", MAC2STR(event->mac), event->aid); + } + else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) + { + wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data; + ESP_LOGI(TAG, "station " MACSTR " leave, AID=%d", MAC2STR(event->mac), event->aid); + } +} + +esp_err_t wifi_init_ap(void) +{ + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Wifi AP init..."); + + esp_err_t retval = esp_netif_init(); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_netif_init: Error: " + std::to_string(retval)); + return retval; + } + + retval = esp_event_loop_create_default(); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_loop_create_default: Error: " + std::to_string(retval)); + return retval; + } + + my_ap_netif = esp_netif_create_default_wifi_ap(); + assert(my_ap_netif); + + retval = esp_wifi_init(&my_ap_wifi_init_config); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_init: Error: " + std::to_string(retval)); + return retval; + } + + // Register an instance of event handler to the default loop. + retval = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_ap_event_handler, NULL, NULL); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_handler_instance_register - WIFI_ANY: Error: " + std::to_string(retval)); + return retval; + } + + ////////////////////////////////////////////////////////////// + uint8_t ssid[32] = {}; + uint8_t password[64] = {}; + + strcpy((char *)ssid, ESP_WIFI_AP_SSID); + strcpy((char *)password, ESP_WIFI_AP_PASS); + + memcpy(&my_ap_wifi_config.ap.ssid, &ssid, sizeof(ssid)); + memcpy(&my_ap_wifi_config.ap.password, &password, sizeof(password)); + ////////////////////////////////////////////////////////////// + + my_ap_wifi_config.ap.channel = ESP_WIFI_AP_CHANNEL; + my_ap_wifi_config.ap.max_connection = ESP_WIFI_AP_MAX_STA_CONN; + my_ap_wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK; + + if (strlen(ESP_WIFI_AP_PASS) == 0) + { + my_ap_wifi_config.ap.authmode = WIFI_AUTH_OPEN; + } + + retval = esp_wifi_set_mode(WIFI_MODE_AP); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_set_mode: Error: " + std::to_string(retval)); + return retval; + } + + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &my_ap_wifi_config)); + retval = esp_wifi_set_config(WIFI_IF_AP, &my_ap_wifi_config); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_set_config: Error: " + std::to_string(retval)); + return retval; + } + + retval = esp_wifi_start(); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_start: Error: " + std::to_string(retval)); + return retval; + } + + ESP_LOGI(TAG, "started with SSID \"%s\", password: \"%s\", channel: %d. Connect to AP and open http://192.168.4.1", ESP_WIFI_AP_SSID, ESP_WIFI_AP_PASS, ESP_WIFI_AP_CHANNEL); + + return retval; +} + +void wifi_deinit_ap(void) +{ + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Wifi AP deinit..."); + + wifi_ap_initialized = false; + + ESP_LOGD(TAG, "esp_wifi_disconnect()"); + ESP_ERROR_CHECK(esp_wifi_disconnect()); + + ESP_LOGD(TAG, "esp_wifi_stop()"); + ESP_ERROR_CHECK(esp_wifi_stop()); + + ESP_LOGD(TAG, "esp_netif_destroy(my_ap_netif)"); + esp_netif_destroy(my_ap_netif); + + ESP_LOGD(TAG, "esp_netif_deinit()"); + ESP_ERROR_CHECK(esp_netif_deinit()); + + ESP_LOGD(TAG, "esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_ap_event_handler)"); + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_ap_event_handler)); + + ESP_LOGD(TAG, "esp_wifi_deinit()"); + ESP_ERROR_CHECK(esp_wifi_deinit()); + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Wifi AP deinit done"); +} + +bool get_wifi_ap_is_connected(void) +{ + return wifi_ap_connected; +} diff --git a/code/main/softAP.h b/code/components/jomjol_network/connect_wifi_ap.h similarity index 56% rename from code/main/softAP.h rename to code/components/jomjol_network/connect_wifi_ap.h index 8a1f2733..1f062bf1 100644 --- a/code/main/softAP.h +++ b/code/components/jomjol_network/connect_wifi_ap.h @@ -1,8 +1,9 @@ -#ifdef ENABLE_SOFTAP +#pragma once -#ifndef SOFTAP_H -#define SOFTAP_H +#ifndef CONNECT_WIFI_AP_H +#define CONNECT_WIFI_AP_H +#include #include #include #include @@ -12,12 +13,12 @@ #include "nvs_flash.h" #include "esp_netif.h" #include "esp_eth.h" -#include "protocol_examples_common.h" #include "esp_tls_crypto.h" #include -void CheckStartAPMode(); +esp_err_t wifi_init_ap(void); +void wifi_deinit_ap(void); -#endif //SOFTAP_H +bool get_wifi_ap_is_connected(void); -#endif //#ifdef ENABLE_SOFTAP \ No newline at end of file +#endif // CONNECT_WIFI_AP_H diff --git a/code/components/jomjol_network/connect_wifi_sta.cpp b/code/components/jomjol_network/connect_wifi_sta.cpp new file mode 100644 index 00000000..e4a1e589 --- /dev/null +++ b/code/components/jomjol_network/connect_wifi_sta.cpp @@ -0,0 +1,515 @@ +#include "defines.h" +#include "Helper.h" + +#include "connect_wifi_sta.h" + +#include +#include +#include +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "driver/gpio.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_eap_client.h" +#include "esp_wnm.h" +#include "esp_rrm.h" +#include "esp_mbo.h" +#include "esp_mac.h" +#include "esp_netif.h" +#include +#include "esp_log.h" +#include "nvs_flash.h" + +#include "lwip/err.h" +#include "lwip/sys.h" + +#include +#include "time_sntp.h" + +#include "interface_mqtt.h" + +#include "ClassLogFile.h" +#include "connect_roaming.h" +#include "read_network_config.h" +#include "statusled.h" + +#include "../include/mdns.h" + +static const char *TAG = "WIFI STA"; + +esp_netif_t *my_sta_netif = NULL; +wifi_init_config_t my_sta_wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); +wifi_config_t my_sta_wifi_config = {}; + +static bool wifi_sta_initialized = false; +static bool wifi_sta_connected = false; +static bool wifi_sta_connection_successful = false; +static int wifi_sta_reconnect_cnt = 0; + +static void wifi_sta_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "WIFI Started"); + esp_wifi_connect(); + } + else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_STOP) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "WIFI Stopped"); + wifi_sta_connected = false; + } + else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Connected to: " + network_config.ssid + ", RSSI: " + std::to_string(get_wifi_rssi())); + +#ifdef WLAN_USE_MESH_ROAMING + printRoamingFeatureSupport(); + +#ifdef WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES + // Avoid client triggered query during processing flow (reduce risk of heap shortage). + // Request will be triggered at the end of every round anyway + // wifi_roaming_query(); +#endif // WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES + +#endif // WLAN_USE_MESH_ROAMING + + wifi_sta_connected = true; + } + else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) + { + /* Disconnect reason: + * https://github.com/espressif/esp-idf/blob/d825753387c1a64463779bbd2369e177e5d59a79/components/esp_wifi/include/esp_wifi_types.h + */ + wifi_event_sta_disconnected_t *disconn = (wifi_event_sta_disconnected_t *)event_data; + if (disconn->reason == WIFI_REASON_ROAMING) + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ", Roaming 802.11kv)"); + // --> no reconnect neccessary, it should automatically reconnect to new AP + } + else + { + if (disconn->reason == WIFI_REASON_NO_AP_FOUND) + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ", No AP)"); + set_status_led(WLAN_CONN, 1, false); + } + else if (disconn->reason == WIFI_REASON_AUTH_EXPIRE || disconn->reason == WIFI_REASON_AUTH_FAIL || disconn->reason == WIFI_REASON_NOT_AUTHED || disconn->reason == WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT || + disconn->reason == WIFI_REASON_HANDSHAKE_TIMEOUT) + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ", Auth fail)"); + set_status_led(WLAN_CONN, 2, false); + } + else if (disconn->reason == WIFI_REASON_BEACON_TIMEOUT) + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ", Timeout)"); + set_status_led(WLAN_CONN, 3, false); + } + else + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ")"); + set_status_led(WLAN_CONN, 4, false); + } + wifi_sta_reconnect_cnt++; + esp_wifi_connect(); // Try to connect again + } + + if (wifi_sta_reconnect_cnt >= 10) + { + wifi_sta_reconnect_cnt = 0; + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Disconnected, multiple reconnect attempts failed (" + std::to_string(disconn->reason) + "), retrying after 5s"); + vTaskDelay(5000 / portTICK_PERIOD_MS); // Delay between the reconnections + } + + wifi_sta_connected = false; + } + else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) + { + wifi_sta_connection_successful = true; + wifi_sta_connected = true; + wifi_sta_reconnect_cnt = 0; + + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + + network_config.ipaddress = std::string(ip4addr_ntoa((const ip4_addr *)&event->ip_info.ip)); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Assigned IP: " + network_config.ipaddress); + + network_config.netmask = std::string(ip4addr_ntoa((const ip4_addr *)&event->ip_info.netmask)); + network_config.gateway = std::string(ip4addr_ntoa((const ip4_addr *)&event->ip_info.gw)); + + esp_netif_dns_info_t dnsInfo; + ESP_ERROR_CHECK(esp_netif_get_dns_info(event->esp_netif, ESP_NETIF_DNS_MAIN, &dnsInfo)); + network_config.dns = std::string(ip4addr_ntoa((const ip4_addr *)&dnsInfo.ip)); + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Assigned IP: " + network_config.ipaddress + ", Subnet: " + network_config.netmask + ", Gateway: " + network_config.gateway + ", DNS: " + network_config.dns); + + if (getMQTTisEnabled()) + { + vTaskDelay(500 / portTICK_PERIOD_MS); + MQTT_Init(); // Init when WIFI is getting connected + } + } +} + +esp_err_t wifi_init_sta(void) +{ + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Wifi STA init..."); + + // Set log level for wifi component to WARN level (default: INFO; only relevant for serial console) + // ******************************************** + esp_log_level_set("wifi", ESP_LOG_WARN); + + esp_err_t retval = esp_netif_init(); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_netif_init: Error: " + std::to_string(retval)); + return retval; + } + + retval = esp_event_loop_create_default(); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_loop_create_default: Error: " + std::to_string(retval)); + return retval; + } + + my_sta_netif = esp_netif_create_default_wifi_sta(); + assert(my_sta_netif); + + if (!network_config.ipaddress.empty() && !network_config.gateway.empty() && !network_config.netmask.empty()) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Manual interface config -> IP: " + network_config.ipaddress + ", Gateway: " + std::string(network_config.gateway) + ", Netmask: " + std::string(network_config.netmask)); + esp_netif_dhcpc_stop(my_sta_netif); // Stop DHCP service + + esp_netif_ip_info_t ip_info; + int a, b, c, d; + string_to_ip4(network_config.ipaddress.c_str(), a, b, c, d); + IP4_ADDR(&ip_info.ip, a, b, c, d); // Set static IP address + + string_to_ip4(network_config.gateway.c_str(), a, b, c, d); + IP4_ADDR(&ip_info.gw, a, b, c, d); // Set gateway + + string_to_ip4(network_config.netmask.c_str(), a, b, c, d); + IP4_ADDR(&ip_info.netmask, a, b, c, d); // Set netmask + + esp_netif_set_ip_info(my_sta_netif, &ip_info); // Set static IP configuration + + if (network_config.dns.empty()) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "No DNS server, use gateway"); + network_config.dns = network_config.gateway; + } + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Manual interface config -> DNS: " + network_config.dns); + } + + esp_netif_dns_info_t dns_info; + ip4_addr_t ip; + ip.addr = esp_ip4addr_aton(network_config.dns.c_str()); + ip_addr_set_ip4_u32(&dns_info.ip, ip.addr); + + retval = esp_netif_set_dns_info(my_sta_netif, ESP_NETIF_DNS_MAIN, &dns_info); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_netif_set_dns_info: Error: " + std::to_string(retval)); + return retval; + } + } + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Automatic interface config --> Use DHCP service"); + } + + retval = esp_wifi_init(&my_sta_wifi_init_config); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_init: Error: " + std::to_string(retval)); + return retval; + } + + // Register an instance of event handler to the default loop. + retval = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_sta_event_handler, NULL, NULL); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_handler_instance_register - WIFI_ANY: Error: " + std::to_string(retval)); + return retval; + } + + retval = esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_sta_event_handler, NULL, NULL); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_handler_instance_register - GOT_IP: Error: " + std::to_string(retval)); + return retval; + } + +#ifdef WLAN_USE_MESH_ROAMING + // Register an instance of event handler to the default loop. + retval = esp_event_handler_instance_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, &esp_bss_rssi_low_handler, NULL, NULL); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_handler_instance_register - BSS_RSSI_LOW: Error: " + std::to_string(retval)); + return retval; + } +#endif + + // ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + my_sta_wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; // Scan all channels instead of stopping after first match + my_sta_wifi_config.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL; // Sort by signal strength and keep up to 4 best APs + my_sta_wifi_config.sta.failure_retry_cnt = 3; // IDF version 5.0 will support this + +#ifdef WLAN_USE_MESH_ROAMING + my_sta_wifi_config.sta.rm_enabled = 1; // 802.11k (Radio Resource Management) + my_sta_wifi_config.sta.btm_enabled = 1; // 802.11v (BSS Transition Management) + my_sta_wifi_config.sta.mbo_enabled = 1; // Multiband Operation (better use of Wi-Fi network resources in roaming decisions) -> not activated to save heap + my_sta_wifi_config.sta.pmf_cfg.capable = 1; // 802.11w (Protected Management Frame, activated by default if other device also advertizes PMF capability) + my_sta_wifi_config.sta.ft_enabled = 1; // 802.11r (BSS Fast Transition) -> Upcoming IDF version 5.0 will support 11r +#endif + + if (!network_config.username.empty()) + { + strcpy((char *)my_sta_wifi_config.sta.ssid, (const char *)network_config.ssid.c_str()); + } + else + { + strcpy((char *)my_sta_wifi_config.sta.ssid, (const char *)network_config.ssid.c_str()); + strcpy((char *)my_sta_wifi_config.sta.password, (const char *)network_config.password.c_str()); + } + + retval = esp_wifi_set_mode(WIFI_MODE_STA); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_set_mode: Error: " + std::to_string(retval)); + return retval; + } + + retval = esp_wifi_set_config(WIFI_IF_STA, &my_sta_wifi_config); + if (retval != ESP_OK) + { + if (retval == ESP_ERR_WIFI_PASSWORD) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_set_config: SSID password invalid! Error: " + std::to_string(retval)); + } + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_set_config: Error: " + std::to_string(retval)); + } + return retval; + } + + if ((!network_config.username.empty()) && (!network_config.eapid.empty()) && (!network_config.password.empty())) + { + retval = esp_eap_client_set_identity((const unsigned char *)network_config.eapid.c_str(), (int)strlen(network_config.eapid.c_str())); + if (retval != ESP_OK) + { + if (retval == ESP_ERR_INVALID_ARG) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_eap_client_set_identity: Invalid argument (len <= 0 or len >= 128)! Error: " + std::to_string(retval)); + } + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_eap_client_set_identity: Error: " + std::to_string(retval)); + } + return retval; + } + + retval = esp_eap_client_set_username((const unsigned char *)network_config.username.c_str(), (int)strlen(network_config.username.c_str())); + if (retval != ESP_OK) + { + if (retval == ESP_ERR_INVALID_ARG) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_eap_client_set_username: Invalid argument (len <= 0 or len >= 128)! Error: " + std::to_string(retval)); + } + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_eap_client_set_username: Error: " + std::to_string(retval)); + } + return retval; + } + + retval = esp_eap_client_set_password((const unsigned char *)network_config.password.c_str(), (int)strlen(network_config.password.c_str())); + if (retval != ESP_OK) + { + if (retval == ESP_ERR_INVALID_ARG) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_eap_client_set_password: Invalid argument (len <= 0)! Error: " + std::to_string(retval)); + } + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_eap_client_set_password: Error: " + std::to_string(retval)); + } + return retval; + } + +#ifdef WIFI_USE_DEFAULT_CERT_BUNDLE + retval = esp_eap_client_use_default_cert_bundle(true); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_eap_client_use_default_cert_bundle: Error: " + std::to_string(retval)); + return retval; + } +#endif + + retval = esp_wifi_sta_enterprise_enable(); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_sta_enterprise_enable: Error: " + std::to_string(retval)); + return retval; + } + } + + retval = esp_wifi_start(); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_start: Error: " + std::to_string(retval)); + return retval; + } + + if (!network_config.hostname.empty()) + { + retval = esp_netif_set_hostname(my_sta_netif, network_config.hostname.c_str()); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to set hostname! Error: " + std::to_string(retval)); + } + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Set hostname to: " + network_config.hostname); + } + // initialize mDNS service + retval = mdns_init(); + if (retval != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mdns_init failed! Error: " + std::to_string(retval)); + } + else + { + // set mdns hostname + mdns_hostname_set(network_config.hostname.c_str()); + } + } + + wifi_sta_initialized = true; + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init successful"); + return ESP_OK; +} + +void wifi_deinit_sta(void) +{ + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Wifi STA deinit..."); + + wifi_sta_initialized = false; + + ESP_LOGD(TAG, "esp_wifi_disconnect()"); + ESP_ERROR_CHECK(esp_wifi_disconnect()); + + ESP_LOGD(TAG, "esp_wifi_stop()"); + ESP_ERROR_CHECK(esp_wifi_stop()); + + ESP_LOGD(TAG, "esp_netif_destroy(my_sta_netif)"); + esp_netif_destroy(my_sta_netif); + + ESP_LOGD(TAG, "esp_netif_deinit()"); + ESP_ERROR_CHECK(esp_netif_deinit()); + + ESP_LOGD(TAG, "esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler)"); + ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_sta_event_handler)); + + ESP_LOGD(TAG, "esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler)"); + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_sta_event_handler)); + +#ifdef WLAN_USE_MESH_ROAMING + ESP_LOGD(TAG, "esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, esp_bss_rssi_low_handler)"); + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, esp_bss_rssi_low_handler)); +#endif + + ESP_LOGD(TAG, "esp_wifi_deinit()"); + ESP_ERROR_CHECK(esp_wifi_deinit()); + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Wifi STA deinit done"); +} + +std::string wifi_scan_ap(void) +{ + std::string data = ""; + wifi_scan_config_t my_wifi_scan_config = {}; + memset(&my_wifi_scan_config, 0, sizeof(my_wifi_scan_config)); + + my_wifi_scan_config.show_hidden = true; // scan also hidden SSIDs + my_wifi_scan_config.channel = 0; // scan all channels + + esp_wifi_scan_start(&my_wifi_scan_config, true); + + uint16_t max_number_of_ap_found = 10; + esp_wifi_scan_get_ap_num(&max_number_of_ap_found); + wifi_ap_record_t *wifi_ap_records = new wifi_ap_record_t[max_number_of_ap_found]; + + if (wifi_ap_records == NULL) + { + esp_wifi_scan_get_ap_records(0, NULL); // free internal heap + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "wifi_scan: Failed to allocate heap for wifi_ap_records"); + data = data + "\"wifi_scan\"" + string(": {"); + data = data + "}"; + return data; + } + else + { + if (esp_wifi_scan_get_ap_records(&max_number_of_ap_found, wifi_ap_records) != ESP_OK) + { + // Retrieve results (and free internal heap) + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "wifi_scan: esp_wifi_scan_get_ap_records: Error retrieving datasets"); + delete[] wifi_ap_records; + data = data + "\"wifi_scan\"" + string(": {"); + data = data + "}"; + return data; + } + } + + wifi_ap_record_t currentAP; + esp_wifi_sta_get_ap_info(¤tAP); + + for (int i = 0; i < max_number_of_ap_found; i++) + { + data = data + "\"wifi" + std::to_string(i) + string("\": {"); + + data = data + "\"SSID\": \"" + std::string((char *)wifi_ap_records[i].ssid) + "\", " + "\"BSSID\": \"" + bssid_to_string((char *)wifi_ap_records[i].bssid) + "\", " + "\"RSSI\": \"" + std::to_string(wifi_ap_records[i].rssi) + "\", " + "\"CH\": \"" + + std::to_string(wifi_ap_records[i].primary) + "\", " + "\"AUTH\": \"" + get_auth_mode_name(wifi_ap_records[i].authmode) + "\""; + + if (i == max_number_of_ap_found - 1) + { + data = data + "}"; + } + else + { + data = data + "},\n"; + } + } + + delete[] wifi_ap_records; + return data; +} + +bool get_wifi_sta_is_connected(void) +{ + return wifi_sta_connected; +} + +int get_wifi_rssi(void) +{ + wifi_ap_record_t ap; + if (esp_wifi_sta_get_ap_info(&ap) == ESP_OK) + { + return ap.rssi; + } + else + { + return -127; // Return -127 if no info available e.g. not connected + } +} diff --git a/code/components/jomjol_network/connect_wifi_sta.h b/code/components/jomjol_network/connect_wifi_sta.h new file mode 100644 index 00000000..74ae725c --- /dev/null +++ b/code/components/jomjol_network/connect_wifi_sta.h @@ -0,0 +1,28 @@ +#pragma once + +#ifndef CONNECT_WIFI_STA_H +#define CONNECT_WIFI_STA_H + +#include +#include +#include +#include +#include +#include +#include +#include "nvs_flash.h" +#include "esp_netif.h" +#include "esp_eth.h" +#include "esp_tls_crypto.h" +#include + +int wifi_init_sta(void); +void wifi_deinit_sta(void); + +bool get_wifi_sta_is_connected(void); + +int get_wifi_rssi(void); + +std::string wifi_scan_ap(void); + +#endif // CONNECT_WIFI_STA_H diff --git a/code/components/jomjol_network/network_init.cpp b/code/components/jomjol_network/network_init.cpp new file mode 100644 index 00000000..fa1263c3 --- /dev/null +++ b/code/components/jomjol_network/network_init.cpp @@ -0,0 +1,272 @@ +#include "defines.h" +#include "Helper.h" + +#include "network_init.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "time_sntp.h" + +#include "server_main.h" +#include "server_remote.h" +#include "server_file.h" +#include "server_ota.h" +#include "server_camera.h" +#include "server_mqtt.h" + +#include "read_network_config.h" + +#include "connect_wifi_ap.h" +#include "connect_wifi_sta.h" +#if (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500) +#include "connect_eth.h" +#endif + +#include "MainFlowControl.h" +#include "ClassLogFile.h" + +#include "basic_auth.h" + +#include "statusled.h" + +static const char *TAG = "NETWORK INIT"; + +// httpd_handle_t my_httpd_server = NULL; + +esp_err_t init_network(void) +{ + esp_err_t retVal = ESP_OK; + TickType_t xDelay = 500 / portTICK_PERIOD_MS; + + // network_config.connection_type = NETWORK_CONNECTION_WIFI_AP_SETUP; + // network_config.connection_type = NETWORK_CONNECTION_WIFI_AP; + network_config.connection_type = NETWORK_CONNECTION_WIFI_STA; + // network_config.connection_type = NETWORK_CONNECTION_DISCONNECT; + + // Read Network parameter and start it + // ******************************************** + int iNetworkStatus = LoadNetworkFromFile(NETWORK_CONFIG_FILE); + + // Network config available (0) or SSID/password not configured (-2) + if (file_exists(CONFIG_FILE) && ((iNetworkStatus == 0) || (iNetworkStatus == -2))) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Network config loaded, init Network..."); + + if (network_config.connection_type == NETWORK_CONNECTION_WIFI_AP_SETUP) + { + retVal = wifi_init_ap(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Wifi AP init failed. Device init aborted!"); + set_status_led(WLAN_INIT, 3, true); + return retVal; + } + + vTaskDelay(xDelay); + + retVal = init_remote_server(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Remote Server init failed. Device init aborted!"); + set_status_led(WLAN_INIT, 3, true); + return retVal; + } + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Wifi AP Setup Initialized"); + } + else if (network_config.connection_type == NETWORK_CONNECTION_WIFI_AP) + { + retVal = wifi_init_ap(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Wifi AP init failed. Device init aborted!"); + set_status_led(WLAN_INIT, 3, true); + return retVal; + } + + vTaskDelay(xDelay); + + retVal = init_webserver(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Wifi AP Webserver init failed. Device init aborted!"); + set_status_led(WLAN_INIT, 3, true); + return retVal; + } + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Wifi AP Webserver Initialized"); + } + else if (network_config.connection_type == NETWORK_CONNECTION_WIFI_STA) + { + if (iNetworkStatus == -2) + { + network_config.connection_type = NETWORK_CONNECTION_WIFI_AP_SETUP; + + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SSID or password not configured!"); + set_status_led(WLAN_INIT, 2, true); + + retVal = wifi_init_ap(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Wifi AP init failed. Device init aborted!"); + set_status_led(WLAN_INIT, 3, true); + return retVal; + } + + vTaskDelay(xDelay); + + retVal = init_remote_server(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Remote Server init failed. Device init aborted!"); + set_status_led(WLAN_INIT, 3, true); + return retVal; + } + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Wifi AP Setup Initialized"); + } + else + { + retVal = wifi_init_sta(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Wifi STA init failed. Device init aborted!"); + set_status_led(WLAN_INIT, 3, true); + return retVal; + } + + vTaskDelay(xDelay); + + retVal = init_webserver(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Wifi STA Webserver init failed. Device init aborted!"); + set_status_led(WLAN_INIT, 3, true); + return retVal; + } + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Wifi STA Webserver Initialized"); + } + } +#if (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500) + else if (network_config.connection_type == NETWORK_CONNECTION_ETH) + { + retVal = eth_init_W5500(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Ethernet init failed. Device init aborted!"); + set_status_led(WLAN_INIT, 3, true); + return retVal; + } + + vTaskDelay(xDelay); + + retVal = init_webserver(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Ethernet Webserver init failed. Device init aborted!"); + set_status_led(WLAN_INIT, 3, true); + return retVal; + } + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Ethernet Webserver Initialized"); + } +#endif // (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500) + else if (network_config.connection_type == NETWORK_CONNECTION_DISCONNECT) + { + // esp_wifi_deinit(); + // esp_wifi_set_mode(WIFI_MODE_NULL); + // esp_wifi_stop(); + return ESP_OK; + } + } + // network.ini not available (-1) and config.ini not available + else + { + network_config.connection_type = NETWORK_CONNECTION_WIFI_AP_SETUP; + + retVal = wifi_init_ap(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Wifi AP init failed. Device init aborted!"); + set_status_led(WLAN_INIT, 3, true); + return retVal; + } + + vTaskDelay(xDelay); + + retVal = init_remote_server(); + if (retVal != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Remote Server init failed. Device init aborted!"); + set_status_led(WLAN_INIT, 3, true); + return retVal; + } + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Wifi AP Setup Initialized"); + } + + init_basic_auth(); + + ESP_LOGD(TAG, "main: sleep for: %ldms", (long)xDelay * CONFIG_FREERTOS_HZ / portTICK_PERIOD_MS); + vTaskDelay(xDelay); + + return retVal; +} + +esp_err_t init_webserver(void) +{ + // Start webserver + register handler + // ******************************************** + ESP_LOGD(TAG, "starting servers"); + // my_httpd_server = start_webserver(); + httpd_handle_t my_httpd_server = start_webserver(); + if (my_httpd_server == NULL) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "start webserver failed"); + return ESP_FAIL; + } + + camera_register_uri(my_httpd_server); + main_flow_register_uri(my_httpd_server); + file_server_register_uri(my_httpd_server, "/sdcard"); + ota_register_uri(my_httpd_server); + mqtt_register_uri(my_httpd_server); + + gpio_handler_create(my_httpd_server); + + ESP_LOGD(TAG, "Before reg main server uri"); + webserver_register_uri(my_httpd_server, "/sdcard"); + + return ESP_OK; +} + +esp_err_t init_remote_server(void) +{ + // Start ap server + register handler + // ******************************************** + ESP_LOGD(TAG, "starting ap servers"); + // my_httpd_server = start_remote_webserver(); + httpd_handle_t my_httpd_server = start_webserver(); + if (my_httpd_server == NULL) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "start remote server failed"); + return ESP_FAIL; + } + + ESP_LOGD(TAG, "Before reg ap server uri"); + remote_webserver_register_uri(my_httpd_server); + + return ESP_OK; +} diff --git a/code/components/jomjol_network/network_init.h b/code/components/jomjol_network/network_init.h new file mode 100644 index 00000000..f12bc573 --- /dev/null +++ b/code/components/jomjol_network/network_init.h @@ -0,0 +1,25 @@ +#pragma once + +#ifndef NETWORK_INIT_H +#define NETWORK_INIT_H + +#include +#include +#include +#include +#include +#include +#include "nvs_flash.h" +#include "esp_netif.h" +#include "esp_eth.h" + +#include + +// extern httpd_handle_t my_httpd_server; + +esp_err_t init_network(void); + +esp_err_t init_webserver(void); +esp_err_t init_remote_server(void); + +#endif // NETWORK_INIT_H diff --git a/code/components/jomjol_network/read_network_config.cpp b/code/components/jomjol_network/read_network_config.cpp new file mode 100644 index 00000000..682c618a --- /dev/null +++ b/code/components/jomjol_network/read_network_config.cpp @@ -0,0 +1,502 @@ +#include "defines.h" + +#include +#include +#include +#include +#include +#include + +#include "read_network_config.h" +#include "Helper.h" +#include "connect_wifi_sta.h" + +#include "esp_log.h" +#include "ClassLogFile.h" + +static const char *TAG = "NETWORK CONFIG"; + +struct network_config_t network_config = {}; +struct network_config_t network_config_temp = {}; + +int LoadNetworkFromFile(std::string filename) +{ + filename = format_filename(filename); + FILE *pFile = fopen(filename.c_str(), "r"); + if (pFile == NULL) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "unable to open wlan.ini (read)!"); + return -1; + } + + ESP_LOGD(TAG, "LoadNetworkFromFile: wlan.ini opened"); + + std::string line = ""; + char temp_bufer[256]; + if (fgets(temp_bufer, sizeof(temp_bufer), pFile) == NULL) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "wlan.ini opened, but empty or content not readable!"); + fclose(pFile); + return -1; + } + else + { + line = std::string(temp_bufer); + } + + int _fix_ipaddress = 3; + + std::vector splitted; + + while ((line.size() > 0) || !(feof(pFile))) + { + if (line[0] != ';') + { + splitted = split_line(line); + + if (splitted.size() > 1) + { + std::string _param = to_upper(splitted[0]); + + if (_param == "CONNECTIONTYPE") + { + network_config.connection_type = splitted[1]; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Connection Type: " + network_config.connection_type); + } + + else if (_param == "SSID") + { + network_config.ssid = splitted[1]; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "SSID: " + network_config.ssid); + } + + else if (_param == "EAPID") + { + network_config.eapid = decrypt_pw_string(splitted[1]); + if (network_config.eapid.empty()) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "EPA-ID: eapid_not_set"); + } +#ifndef __HIDE_PASSWORD + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "EPA-ID: " + network_config.eapid); + } +#else + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "EPA-ID: eapid_hiden"); + } +#endif + } + + else if (_param == "PASSWORD") + { + network_config.password = decrypt_pw_string(splitted[1]); + if (network_config.password.empty()) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Password: wifi_pw_not_set"); + } +#ifndef __HIDE_PASSWORD + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Password: " + network_config.password); + } +#else + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Password: wifi_pw_hiden"); + } +#endif + } + + else if (_param == "HOSTNAME") + { + network_config.hostname = splitted[1]; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Hostname: " + network_config.hostname); + } + + else if (_param == "IP") + { + network_config.ipaddress = splitted[1]; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "IP-Address: " + network_config.ipaddress); + + _fix_ipaddress--; + } + + else if (_param == "GATEWAY") + { + network_config.gateway = splitted[1]; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Gateway: " + network_config.gateway); + + _fix_ipaddress--; + } + + else if (_param == "NETMASK") + { + network_config.netmask = splitted[1]; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Netmask: " + network_config.netmask); + + _fix_ipaddress--; + } + + else if (_param == "DNS") + { + network_config.dns = splitted[1]; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "DNS: " + network_config.dns); + } + + else if (_param == "HTTP_AUTH") + { + network_config.http_auth = alphanumeric_to_boolean(splitted[1]); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_AUTH: " + std::to_string(network_config.http_auth)); + } + + else if (_param == "HTTP_USERNAME") + { + network_config.http_username = splitted[1]; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_USERNAME: " + network_config.http_username); + } + + else if (_param == "HTTP_PASSWORD") + { + network_config.http_password = decrypt_pw_string(splitted[1]); + if (network_config.http_password.empty()) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_Password: http_pw_not_set"); + } +#ifndef __HIDE_PASSWORD + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_PASSWORD: " + network_config.http_password); + } +#else + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_Password: http_pw_hiden"); + } +#endif + } + +#if (defined WLAN_USE_ROAMING_BY_SCANNING || (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES)) + else if (_param == "RSSITHRESHOLD") + { + network_config.rssi_threshold = atoi(splitted[1].c_str()); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "RSSIThreshold: " + std::to_string(network_config.rssi_threshold)); + } +#endif + } + } + + /* read next line */ + if (fgets(temp_bufer, sizeof(temp_bufer), pFile) == NULL) + { + line = ""; + } + else + { + line = std::string(temp_bufer); + } + } + fclose(pFile); + + memcpy(&network_config_temp, &network_config, sizeof(network_config)); + + /* Check if SSID is empty (mandatory parameter) */ +#if (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500) + if ((network_config.connection_type.empty()) || ((network_config.connection_type != NETWORK_CONNECTION_WIFI_AP_SETUP) && (network_config.connection_type != NETWORK_CONNECTION_WIFI_AP) && + (network_config.connection_type != NETWORK_CONNECTION_WIFI_STA) && (network_config.connection_type != NETWORK_CONNECTION_ETH) && (network_config.connection_type != NETWORK_CONNECTION_DISCONNECT))) +#else + if ((network_config.connection_type.empty()) || ((network_config.connection_type != NETWORK_CONNECTION_WIFI_AP_SETUP) && (network_config.connection_type != NETWORK_CONNECTION_WIFI_AP) && + (network_config.connection_type != NETWORK_CONNECTION_WIFI_STA) && (network_config.connection_type != NETWORK_CONNECTION_DISCONNECT))) +#endif + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection Type empty. It set to WiFi AP setup!"); + network_config.connection_type = NETWORK_CONNECTION_WIFI_AP_SETUP; + } + + /* Check if SSID is empty (mandatory parameter) */ + if (network_config.ssid.empty() && network_config.connection_type == NETWORK_CONNECTION_WIFI_STA) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SSID empty!"); + return -2; + } + + if (_fix_ipaddress == 0) + { + network_config.fix_ipaddress_used = true; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "fix ipaddress used"); + } + else + { + network_config.fix_ipaddress_used = false; + } + + return 0; +} + +bool ChangeHostName(std::string filename, std::string _newhostname) +{ + if (_newhostname == network_config.hostname) + { + return false; + } + + filename = format_filename(filename); + FILE *pFile = fopen(filename.c_str(), "r"); + + if (pFile == NULL) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeHostName: unable to open wlan.ini (read)"); + return ESP_FAIL; + } + + ESP_LOGD(TAG, "ChangeHostName: wlan.ini opened"); + + char temp_bufer[256]; + std::string line = ""; + + if (fgets(temp_bufer, sizeof(temp_bufer), pFile) == NULL) + { + line = ""; + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeHostName: wlan.ini opened, but empty or content not readable"); + fclose(pFile); + return ESP_FAIL; + } + else + { + line = std::string(temp_bufer); + } + + bool found = false; + std::vector splitted; + std::vector new_file; + + while ((line.size() > 0) || !(feof(pFile))) + { + splitted = split_line(line); + + if (splitted.size() > 1) + { + std::string _param = to_upper(splitted[0]); + + if (to_upper(splitted[0]) == "HOSTNAME" || to_upper(splitted[0]) == ";HOSTNAME") + { + line = "hostname = \"" + _newhostname + "\"\n"; + found = true; + } + + new_file.push_back(line); + + if (fgets(temp_bufer, sizeof(temp_bufer), pFile) == NULL) + { + line = ""; + } + else + { + line = std::string(temp_bufer); + } + } + } + + if (!found) + { + line = "\n;++++++++++++++++++++++++++++++++++\n"; + line += "; Hostname: Name of device in network\n"; + line += "; This parameter can be configured via WebUI configuration\n"; + line += "; Default: \"watermeter\", if nothing is configured\n\n"; + line = "hostname = \"" + _newhostname + "\"\n"; + new_file.push_back(line); + } + fclose(pFile); + + pFile = fopen(filename.c_str(), "w+"); + if (pFile == NULL) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeHostName: unable to open wlan.ini (write)"); + return false; + } + + for (int i = 0; i < new_file.size(); ++i) + { + fputs(new_file[i].c_str(), pFile); + } + fclose(pFile); + + ESP_LOGD(TAG, "ChangeHostName done"); + + return true; +} + +#if (defined WLAN_USE_ROAMING_BY_SCANNING || (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES)) +bool ChangeRSSIThreshold(std::string filename, int _newrssithreshold) +{ + if (network_config.rssi_threshold == _newrssithreshold) + { + return false; + } + + filename = format_filename(filename); + FILE *pFile = fopen(filename.c_str(), "r"); + + if (pFile == NULL) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeRSSIThreshold: unable to open wlan.ini (read)"); + fclose(pFile); + return ESP_FAIL; + } + + ESP_LOGD(TAG, "ChangeRSSIThreshold: wlan.ini opened"); + + char temp_bufer[256]; + std::string line = ""; + + if (fgets(temp_bufer, sizeof(temp_bufer), pFile) == NULL) + { + line = ""; + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeRSSIThreshold: wlan.ini opened, but empty or content not readable"); + fclose(pFile); + return ESP_FAIL; + } + else + { + line = std::string(temp_bufer); + } + + bool found = false; + std::vector splitted; + std::vector new_file; + + while ((line.size() > 0) || !(feof(pFile))) + { + splitted = split_line(line); + + if (splitted.size() > 1) + { + std::string _param = to_upper(splitted[0]); + + if (_param == "RSSITHRESHOLD" || _param == ";RSSITHRESHOLD") + { + line = "RSSIThreshold = " + to_string(_newrssithreshold) + "\n"; + found = true; + } + + new_file.push_back(line); + + if (fgets(temp_bufer, sizeof(temp_bufer), pFile) == NULL) + { + line = ""; + } + else + { + line = std::string(temp_bufer); + } + } + } + + if (!found) + { + line = "\n;++++++++++++++++++++++++++++++++++\n"; + line += "; WIFI Roaming:\n"; + line += "; Network assisted roaming protocol is activated by default\n"; + line += "; AP / mesh system needs to support roaming protocol 802.11k/v\n"; + line += ";\n"; + line += "; Optional feature (usually not neccessary):\n"; + line += "; RSSI Threshold for client requested roaming query (RSSI < RSSIThreshold)\n"; + line += "; Note: This parameter can be configured via WebUI configuration\n"; + line += "; Default: 0 = Disable client requested roaming query\n\n"; + line += "RSSIThreshold = " + to_string(_newrssithreshold) + "\n"; + new_file.push_back(line); + } + + fclose(pFile); + + pFile = fopen(filename.c_str(), "w+"); + if (pFile == NULL) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeRSSIThreshold: unable to open wlan.ini (write)"); + return false; + } + + for (int i = 0; i < new_file.size(); ++i) + { + fputs(new_file[i].c_str(), pFile); + } + + fclose(pFile); + + ESP_LOGD(TAG, "ChangeRSSIThreshold done"); + + return true; +} +#endif + +esp_err_t GetAuthWebUIConfig(std::string filename) +{ + filename = format_filename(filename); + FILE *pFile = fopen(filename.c_str(), "r"); + + if (pFile == NULL) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "GetAuthWebUIConfig: unable to open wlan.ini (read)"); + return ESP_FAIL; + } + + ESP_LOGD(TAG, "GetAuthWebUIConfig: wlan.ini opened"); + + char temp_bufer[256]; + std::string line = ""; + + if (fgets(temp_bufer, sizeof(temp_bufer), pFile) == NULL) + { + line = ""; + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "GetAuthWebUIConfig: wlan.ini opened, but empty or content not readable"); + fclose(pFile); + return ESP_FAIL; + } + else + { + line = std::string(temp_bufer); + } + + std::vector splitted; + + while ((line.size() > 0) || !(feof(pFile))) + { + splitted = split_line(line); + + if (splitted.size() > 1) + { + std::string _param = to_upper(splitted[0]); + + if ((_param == "HTTP_AUTH") || (_param == ";HTTP_AUTH")) + { + network_config.http_auth = alphanumeric_to_boolean(splitted[1]); + } + + else if ((_param == "HTTP_USERNAME") || (_param == ";HTTP_USERNAME")) + { + network_config.http_username = splitted[1]; + } + + else if ((_param == "HTTP_PASSWORD") || (_param == ";HTTP_PASSWORD")) + { + network_config.http_password = decrypt_pw_string(splitted[1]); + } + } + + /* read next line */ + if (fgets(temp_bufer, sizeof(temp_bufer), pFile) == NULL) + { + line = ""; + } + else + { + line = std::string(temp_bufer); + } + } + + fclose(pFile); + + return ESP_OK; +} diff --git a/code/components/jomjol_network/read_network_config.h b/code/components/jomjol_network/read_network_config.h new file mode 100644 index 00000000..6702f4b0 --- /dev/null +++ b/code/components/jomjol_network/read_network_config.h @@ -0,0 +1,49 @@ +#pragma once + +#ifndef READ_NETWORK_CONFIG_H +#define READ_NETWORK_CONFIG_H + +#include +#include + +#include "defines.h" + +struct network_config_t +{ + std::string connection_type = ""; + + std::string ssid = ""; + std::string eapid = ""; + std::string username = ""; + std::string password = ""; + std::string hostname = "watermeter"; // Default: watermeter + std::string ipaddress = ""; + std::string gateway = ""; + std::string netmask = ""; + std::string dns = ""; + int rssi_threshold = 0; // Default: 0 -> ROAMING disabled + + bool fix_ipaddress_used = false; + + bool http_auth = false; + std::string http_username = ""; + std::string http_password = ""; +}; +extern struct network_config_t network_config; +extern struct network_config_t network_config_temp; + +#define NETWORK_CONNECTION_WIFI_AP_SETUP "Wifi_AP_Setup" +#define NETWORK_CONNECTION_WIFI_AP "Wifi_AP" +#define NETWORK_CONNECTION_WIFI_STA "Wifi_STA" +#if (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500) +#define NETWORK_CONNECTION_ETH "Ethernet" +#endif // (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500) +#define NETWORK_CONNECTION_DISCONNECT "Disconnect" + +int LoadNetworkFromFile(std::string filename); + +bool ChangeHostName(std::string filename, std::string _newhostname); +bool ChangeRSSIThreshold(std::string filename, int _newrssithreshold); +esp_err_t GetAuthWebUIConfig(std::string filename); + +#endif // READ_NETWORK_CONFIG_H diff --git a/code/components/jomjol_network/server_camera.cpp b/code/components/jomjol_network/server_camera.cpp new file mode 100644 index 00000000..1b6dff87 --- /dev/null +++ b/code/components/jomjol_network/server_camera.cpp @@ -0,0 +1,184 @@ +#include "server_camera.h" + +#include +#include +#include + +#include "defines.h" + +#include "esp_camera.h" +#include "ClassControllCamera.h" +#include "MainFlowControl.h" + +#include "ClassLogFile.h" +#include "basic_auth.h" + +static const char *TAG = "server_cam"; + +esp_err_t handler_lightOn(httpd_req_t *req) +{ + if (Camera.get_camera_init_successful()) + { + Camera.set_flash_light_on_off(true); + const char *resp_str = (const char *)req->user_ctx; + httpd_resp_send(req, resp_str, strlen(resp_str)); + } + else + { + httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lighton not available!"); + return ESP_ERR_NOT_FOUND; + } + + return ESP_OK; +} + +esp_err_t handler_lightOff(httpd_req_t *req) +{ + if (Camera.get_camera_init_successful()) + { + Camera.set_flash_light_on_off(false); + const char *resp_str = (const char *)req->user_ctx; + httpd_resp_send(req, resp_str, strlen(resp_str)); + } + else + { + httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lightoff not available!"); + return ESP_ERR_NOT_FOUND; + } + + return ESP_OK; +} + +esp_err_t handler_capture(httpd_req_t *req) +{ + if (Camera.get_camera_init_successful()) + { + return Camera.capture_to_http(req); + } + else + { + httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture not available!"); + return ESP_ERR_NOT_FOUND; + } +} + +esp_err_t handler_capture_with_light(httpd_req_t *req) +{ + if (Camera.get_camera_init_successful()) + { + char _query[100]; + char _delay[10]; + int delay = 2500; + + if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) + { + ESP_LOGD(TAG, "Query: %s", _query); + + if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK) + { + std::string _delay_ = std::string(_delay); + if (is_string_numeric(_delay_)) + { + delay = std::atoi(_delay); + if (delay < 0) + { + delay = 0; + } + } + } + } + + return Camera.capture_to_http(req, delay); + } + else + { + httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture_with_flashlight not available!"); + return ESP_ERR_NOT_FOUND; + } +} + +esp_err_t handler_capture_save_to_file(httpd_req_t *req) +{ + if (Camera.get_camera_init_successful()) + { + char _query[100]; + char _delay[10]; + int delay = 0; + char filename[100]; + std::string fn = "/sdcard/"; + + if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) + { + ESP_LOGD(TAG, "Query: %s", _query); + + if (httpd_query_key_value(_query, "filename", filename, 100) == ESP_OK) + { + fn.append(filename); + } + else + { + fn.append("noname.jpg"); + } + + if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK) + { + std::string _delay_ = std::string(_delay); + if (is_string_numeric(_delay_)) + { + delay = std::atoi(_delay); + if (delay < 0) + { + delay = 0; + } + } + } + } + else + { + fn.append("noname.jpg"); + } + + esp_err_t result = Camera.capture_to_file(fn, delay); + + const char *resp_str = (const char *)fn.c_str(); + httpd_resp_send(req, resp_str, strlen(resp_str)); + + return result; + } + else + { + httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /save not available!"); + return ESP_ERR_NOT_FOUND; + } +} + +void camera_register_uri(httpd_handle_t server) +{ + httpd_uri_t camuri = {}; + camuri.method = HTTP_GET; + + camuri.uri = "/lighton"; + camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_lightOn); + camuri.user_ctx = (void *)"Light On"; + httpd_register_uri_handler(server, &camuri); + + camuri.uri = "/lightoff"; + camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_lightOff); + camuri.user_ctx = (void *)"Light Off"; + httpd_register_uri_handler(server, &camuri); + + camuri.uri = "/capture"; + camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_capture); + camuri.user_ctx = NULL; + httpd_register_uri_handler(server, &camuri); + + camuri.uri = "/capture_with_flashlight"; + camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_capture_with_light); + camuri.user_ctx = NULL; + httpd_register_uri_handler(server, &camuri); + + camuri.uri = "/save"; + camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_capture_save_to_file); + camuri.user_ctx = NULL; + httpd_register_uri_handler(server, &camuri); +} diff --git a/code/components/jomjol_network/server_camera.h b/code/components/jomjol_network/server_camera.h new file mode 100644 index 00000000..58c59ee5 --- /dev/null +++ b/code/components/jomjol_network/server_camera.h @@ -0,0 +1,11 @@ +#pragma once + +#ifndef SERVER_CAMERA_H +#define SERVER_CAMERA_H + +#include +#include + +void camera_register_uri(httpd_handle_t server); + +#endif // SERVER_CAMERA_H diff --git a/code/main/server_main.cpp b/code/components/jomjol_network/server_main.cpp similarity index 53% rename from code/main/server_main.cpp rename to code/components/jomjol_network/server_main.cpp index a38a7e12..0aa9b74f 100644 --- a/code/main/server_main.cpp +++ b/code/components/jomjol_network/server_main.cpp @@ -1,44 +1,163 @@ +#include "defines.h" + #include "server_main.h" +#include #include +#include +#include +#include +#include #include "server_help.h" #include "ClassLogFile.h" #include "time_sntp.h" -#include "connect_wlan.h" -#include "read_wlanini.h" +#include "connect_wifi_sta.h" +#include "read_network_config.h" -#include "version.h" - -#include "esp_wifi.h" -#include +#include "../main/version.h" #include "MainFlowControl.h" -#include "esp_log.h" #include "basic_auth.h" -#include "esp_chip_info.h" - -#include #include "Helper.h" -httpd_handle_t server = NULL; -std::string starttime = ""; - static const char *TAG = "MAIN SERVER"; -/* An HTTP GET handler */ -esp_err_t info_get_handler(httpd_req_t *req) -{ -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("info_get_handler - Start"); -#endif +std::string webserver_start_time = ""; - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "info_get_handler"); +esp_err_t main_handler(httpd_req_t *req) +{ + char filepath[50]; + ESP_LOGD(TAG, "uri: %s\n", req->uri); + int _pos; + esp_err_t res; + + char *base_path = (char *)req->user_ctx; + std::string filetosend(base_path); + + const char *filename = get_path_from_uri(filepath, base_path, req->uri - 1, sizeof(filepath)); + ESP_LOGD(TAG, "1 uri: %s, filename: %s, filepath: %s", req->uri, filename, filepath); + + if ((strcmp(req->uri, "/") == 0)) + { + filetosend = filetosend + "/html/index.html"; + } + else + { + filetosend = filetosend + "/html" + std::string(req->uri); + _pos = filetosend.find("?"); + if (_pos > -1) + { + filetosend = filetosend.substr(0, _pos); + } + } + + if (filetosend == "/sdcard/html/index.html") + { + // Initialization failed with crritical errors! + if (is_set_system_statusflag(SYSTEM_STATUS_PSRAM_BAD) || is_set_system_statusflag(SYSTEM_STATUS_CAM_BAD) || + is_set_system_statusflag(SYSTEM_STATUS_SDCARD_CHECK_BAD) || is_set_system_statusflag(SYSTEM_STATUS_FOLDER_CHECK_BAD)) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "We have a critical error, not serving main page!"); + + char buf[20]; + std::string message = "

AI on the Edge Device

We have one or more critical errors:
"; + + for (int i = 0; i < 32; i++) + { + if (is_set_system_statusflag((SystemStatusFlag_t)(1 << i))) + { + snprintf(buf, sizeof(buf), "0x%08X", 1 << i); + message += std::string(buf) + "
"; + } + } + + message += "
Please check logs with log viewer and/or jomjol.github.io/AI-on-the-edge-device-docs/Error-Codes for more information!"; + message += "

"; + message += " "; + message += " "; + message += " "; + httpd_resp_send(req, message.c_str(), message.length()); + return ESP_OK; + } + else if (isSetupModusActive()) + { + ESP_LOGD(TAG, "System is in setup mode --> index.html --> setup.html"); + filetosend = "/sdcard/html/setup.html"; + } + } + + ESP_LOGD(TAG, "Filename: %s", filename); + ESP_LOGD(TAG, "File requested: %s", filetosend.c_str()); + + if (!filename) + { + ESP_LOGE(TAG, "Filename is too long"); + /* Respond with 414 Error */ + httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long"); + return ESP_FAIL; + } + + res = send_file(req, filetosend); + /* Respond with an empty chunk to signal HTTP response completion */ + httpd_resp_send_chunk(req, NULL, 0); + + if (res != ESP_OK) + { + return res; + } + + return ESP_OK; +} + +esp_err_t start_time_handler(httpd_req_t *req) +{ + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + httpd_resp_send(req, webserver_start_time.c_str(), webserver_start_time.length()); + return ESP_OK; +} + +esp_err_t sysinfo_handler(httpd_req_t *req) +{ + std::string zw; + std::string cputemp = std::to_string((int)read_tempsensor()); + std::string gitversion = libfive_git_version(); + std::string buildtime = build_time(); + std::string gitbranch = libfive_git_branch(); + std::string gittag = libfive_git_version(); + std::string gitrevision = libfive_git_revision(); + std::string htmlversion = getHTMLversion(); + char freeheapmem[11]; + sprintf(freeheapmem, "%lu", (long)get_heapsize()); + + zw = string("[{") + + "\"firmware\": \"" + gitversion + "\"," + + "\"buildtime\": \"" + buildtime + "\"," + + "\"gitbranch\": \"" + gitbranch + "\"," + + "\"gittag\": \"" + gittag + "\"," + + "\"gitrevision\": \"" + gitrevision + "\"," + + "\"html\": \"" + htmlversion + "\"," + + "\"cputemp\": \"" + cputemp + "\"," + + "\"hostname\": \"" + network_config.hostname + "\"," + + "\"IPv4\": \"" + network_config.ipaddress + "\"," + + "\"freeHeapMem\": \"" + freeheapmem + "\"" + + "}]"; + + httpd_resp_set_type(req, "application/json"); + httpd_resp_send(req, zw.c_str(), zw.length()); + + return ESP_OK; +} + +/* An HTTP GET handler */ +esp_err_t info_handler(httpd_req_t *req) +{ + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "info_handler"); char _query[200]; - char _valuechar[30]; + char _valuechar[30]; std::string _task; if (httpd_req_get_url_query_str(req, _query, 200) != ESP_OK) @@ -61,137 +180,133 @@ esp_err_t info_get_handler(httpd_req_t *req) if (_task.compare("GitBranch") == 0) { httpd_resp_sendstr(req, libfive_git_branch()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("GitTag") == 0) { httpd_resp_sendstr(req, libfive_git_version()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("GitRevision") == 0) { httpd_resp_sendstr(req, libfive_git_revision()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("BuildTime") == 0) { httpd_resp_sendstr(req, build_time()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("FirmwareVersion") == 0) { httpd_resp_sendstr(req, getFwVersion().c_str()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("HTMLVersion") == 0) { httpd_resp_sendstr(req, getHTMLversion().c_str()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("Hostname") == 0) { - std::string zw; - zw = std::string(wlan_config.hostname); + std::string zw = std::string(network_config.hostname); httpd_resp_sendstr(req, zw.c_str()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("IP") == 0) { - std::string *zw; - zw = getIPAddress(); - httpd_resp_sendstr(req, zw->c_str()); - return ESP_OK; + std::string zw = std::string(network_config.ipaddress); + httpd_resp_sendstr(req, zw.c_str()); + return ESP_OK; } else if (_task.compare("SSID") == 0) { - std::string *zw; - zw = getSSID(); - httpd_resp_sendstr(req, zw->c_str()); - return ESP_OK; + std::string zw = std::string(network_config.ssid); + httpd_resp_sendstr(req, zw.c_str()); + return ESP_OK; } else if (_task.compare("FlowStatus") == 0) { - std::string zw; - zw = std::string("FlowStatus"); + std::string zw = std::string("FlowStatus"); httpd_resp_sendstr(req, zw.c_str()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("Round") == 0) { - char formated[10] = ""; + char formated[10] = ""; snprintf(formated, sizeof(formated), "%d", getCountFlowRounds()); httpd_resp_sendstr(req, formated); - return ESP_OK; + return ESP_OK; } else if (_task.compare("SDCardPartitionSize") == 0) { std::string zw; - zw = getSDCardPartitionSize(); + zw = get_sd_card_partition_size(); httpd_resp_sendstr(req, zw.c_str()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("SDCardFreePartitionSpace") == 0) { std::string zw; - zw = getSDCardFreePartitionSpace(); + zw = get_sd_card_free_partition_space(); httpd_resp_sendstr(req, zw.c_str()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("SDCardPartitionAllocationSize") == 0) { std::string zw; - zw = getSDCardPartitionAllocationSize(); + zw = get_sd_card_partition_allocation_size(); httpd_resp_sendstr(req, zw.c_str()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("SDCardManufacturer") == 0) { std::string zw; - zw = getSDCardManufacturer(); + zw = get_sd_card_manufacturer(); httpd_resp_sendstr(req, zw.c_str()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("SDCardName") == 0) { std::string zw; - zw = getSDCardName(); + zw = get_sd_card_name(); httpd_resp_sendstr(req, zw.c_str()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("SDCardCapacity") == 0) { std::string zw; - zw = getSDCardCapacity(); + zw = get_sd_card_capacity(); httpd_resp_sendstr(req, zw.c_str()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("SDCardSectorSize") == 0) { std::string zw; - zw = getSDCardSectorSize(); + zw = get_sd_card_sector_size(); httpd_resp_sendstr(req, zw.c_str()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("ChipCores") == 0) { esp_chip_info_t chipInfo; esp_chip_info(&chipInfo); httpd_resp_sendstr(req, to_string(chipInfo.cores).c_str()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("ChipRevision") == 0) { esp_chip_info_t chipInfo; esp_chip_info(&chipInfo); httpd_resp_sendstr(req, to_string(chipInfo.revision).c_str()); - return ESP_OK; + return ESP_OK; } else if (_task.compare("ChipFeatures") == 0) { esp_chip_info_t chipInfo; esp_chip_info(&chipInfo); httpd_resp_sendstr(req, to_string(chipInfo.features).c_str()); - return ESP_OK; + return ESP_OK; } else { @@ -203,152 +318,40 @@ esp_err_t info_get_handler(httpd_req_t *req) return ESP_OK; } - -esp_err_t starttime_get_handler(httpd_req_t *req) -{ - httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - - httpd_resp_send(req, starttime.c_str(), starttime.length()); - - return ESP_OK; -} - - -esp_err_t hello_main_handler(httpd_req_t *req) -{ -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("hello_main_handler - Start"); -#endif - - char filepath[50]; - ESP_LOGD(TAG, "uri: %s\n", req->uri); - int _pos; - esp_err_t res; - - char *base_path = (char*) req->user_ctx; - std::string filetosend(base_path); - - const char *filename = get_path_from_uri(filepath, base_path, - req->uri - 1, sizeof(filepath)); - ESP_LOGD(TAG, "1 uri: %s, filename: %s, filepath: %s", req->uri, filename, filepath); - - if ((strcmp(req->uri, "/") == 0)) - { - { - filetosend = filetosend + "/html/index.html"; - } - } - else - { - filetosend = filetosend + "/html" + std::string(req->uri); - _pos = filetosend.find("?"); - if (_pos > -1){ - filetosend = filetosend.substr(0, _pos); - } - } - - if (filetosend == "/sdcard/html/index.html") { - if (isSetSystemStatusFlag(SYSTEM_STATUS_PSRAM_BAD) || // Initialization failed with crritical errors! - isSetSystemStatusFlag(SYSTEM_STATUS_CAM_BAD) || - isSetSystemStatusFlag(SYSTEM_STATUS_SDCARD_CHECK_BAD) || - isSetSystemStatusFlag(SYSTEM_STATUS_FOLDER_CHECK_BAD)) - { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "We have a critical error, not serving main page!"); - - char buf[20]; - std::string message = "

AI on the Edge Device

We have one or more critical errors:
"; - - for (int i = 0; i < 32; i++) { - if (isSetSystemStatusFlag((SystemStatusFlag_t)(1<"; - } - } - - message += "
Please check logs with log viewer and/or jomjol.github.io/AI-on-the-edge-device-docs/Error-Codes for more information!"; - message += "

"; - message += " "; - message += " "; - message += " "; - httpd_resp_send(req, message.c_str(), message.length()); - return ESP_OK; - } - else if (isSetupModusActive()) { - ESP_LOGD(TAG, "System is in setup mode --> index.html --> setup.html"); - filetosend = "/sdcard/html/setup.html"; - } - } - - ESP_LOGD(TAG, "Filename: %s", filename); - - ESP_LOGD(TAG, "File requested: %s", filetosend.c_str()); - - if (!filename) { - ESP_LOGE(TAG, "Filename is too long"); - /* Respond with 414 Error */ - httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long"); - return ESP_FAIL; - } - - res = send_file(req, filetosend); - /* Respond with an empty chunk to signal HTTP response completion */ - httpd_resp_send_chunk(req, NULL, 0); - - if (res != ESP_OK) - return res; - - /* Respond with an empty chunk to signal HTTP response completion */ -// httpd_resp_sendstr(req, ""); -// httpd_resp_send_chunk(req, NULL, 0); - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("hello_main_handler - Stop"); -#endif - - return ESP_OK; -} - - esp_err_t img_tmp_handler(httpd_req_t *req) { char filepath[50]; ESP_LOGD(TAG, "uri: %s", req->uri); - char *base_path = (char*) req->user_ctx; + char *base_path = (char *)req->user_ctx; std::string filetosend(base_path); - const char *filename = get_path_from_uri(filepath, base_path, - req->uri + sizeof("/img_tmp/") - 1, sizeof(filepath)); + const char *filename = get_path_from_uri(filepath, base_path, req->uri + sizeof("/img_tmp/") - 1, sizeof(filepath)); ESP_LOGD(TAG, "1 uri: %s, filename: %s, filepath: %s", req->uri, filename, filepath); filetosend = filetosend + "/img_tmp/" + std::string(filename); ESP_LOGD(TAG, "File to upload: %s", filetosend.c_str()); - esp_err_t res = send_file(req, filetosend); + esp_err_t res = send_file(req, filetosend); if (res != ESP_OK) + { return res; + } /* Respond with an empty chunk to signal HTTP response completion */ httpd_resp_send_chunk(req, NULL, 0); return ESP_OK; } - esp_err_t img_tmp_virtual_handler(httpd_req_t *req) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("img_tmp_virtual_handler - Start"); - #endif - char filepath[50]; - ESP_LOGD(TAG, "uri: %s", req->uri); - char *base_path = (char*) req->user_ctx; + char *base_path = (char *)req->user_ctx; std::string filetosend(base_path); - const char *filename = get_path_from_uri(filepath, base_path, - req->uri + sizeof("/img_tmp/") - 1, sizeof(filepath)); + const char *filename = get_path_from_uri(filepath, base_path, req->uri + sizeof("/img_tmp/") - 1, sizeof(filepath)); ESP_LOGD(TAG, "1 uri: %s, filename: %s, filepath: %s", req->uri, filename, filepath); filetosend = std::string(filename); @@ -356,164 +359,128 @@ esp_err_t img_tmp_virtual_handler(httpd_req_t *req) // Serve raw.jpg if (filetosend == "raw.jpg") - return GetRawJPG(req); + { + return GetRawJPG(req); + } // Serve alg.jpg, alg_roi.jpg or digit and analog ROIs if (ESP_OK == GetJPG(filetosend, req)) + { return ESP_OK; - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("img_tmp_virtual_handler - Done"); - #endif + } // File was not served already --> serve with img_tmp_handler return img_tmp_handler(req); } - -esp_err_t sysinfo_handler(httpd_req_t *req) +esp_err_t wifi_scan_handler(httpd_req_t *req) { - std::string zw; - std::string cputemp = std::to_string((int)temperatureRead()); - std::string gitversion = libfive_git_version(); - std::string buildtime = build_time(); - std::string gitbranch = libfive_git_branch(); - std::string gittag = libfive_git_version(); - std::string gitrevision = libfive_git_revision(); - std::string htmlversion = getHTMLversion(); - char freeheapmem[11]; - sprintf(freeheapmem, "%lu", (long) getESPHeapSize()); - - zw = string("[{") + - "\"firmware\": \"" + gitversion + "\"," + - "\"buildtime\": \"" + buildtime + "\"," + - "\"gitbranch\": \"" + gitbranch + "\"," + - "\"gittag\": \"" + gittag + "\"," + - "\"gitrevision\": \"" + gitrevision + "\"," + - "\"html\": \"" + htmlversion + "\"," + - "\"cputemp\": \"" + cputemp + "\"," + - "\"hostname\": \"" + *getHostname() + "\"," + - "\"IPv4\": \"" + *getIPAddress() + "\"," + - "\"freeHeapMem\": \"" + freeheapmem + "\"" + - "}]"; + std::string data = string("{\n"); + data = data + wifi_scan_ap() + "\n"; + data = data + string("}"); httpd_resp_set_type(req, "application/json"); - httpd_resp_send(req, zw.c_str(), zw.length()); + httpd_resp_send(req, data.c_str(), data.length()); + + // Respond with an empty chunk to signal HTTP response completion + httpd_resp_send_chunk(req, NULL, 0); return ESP_OK; } - -void register_server_main_uri(httpd_handle_t server, const char *base_path) -{ - httpd_uri_t info_get_handle = { - .uri = "/info", // Match all URIs of type /path/to/file - .method = HTTP_GET, - .handler = APPLY_BASIC_AUTH_FILTER(info_get_handler), - .user_ctx = (void*) base_path // Pass server data as context - }; - httpd_register_uri_handler(server, &info_get_handle); - - httpd_uri_t sysinfo_handle = { - .uri = "/sysinfo", // Match all URIs of type /path/to/file - .method = HTTP_GET, - .handler = APPLY_BASIC_AUTH_FILTER(sysinfo_handler), - .user_ctx = (void*) base_path // Pass server data as context - }; - httpd_register_uri_handler(server, &sysinfo_handle); - - httpd_uri_t starttime_tmp_handle = { - .uri = "/starttime", // Match all URIs of type /path/to/file - .method = HTTP_GET, - .handler = APPLY_BASIC_AUTH_FILTER(starttime_get_handler), - .user_ctx = NULL // Pass server data as context - }; - httpd_register_uri_handler(server, &starttime_tmp_handle); - - httpd_uri_t img_tmp_handle = { - .uri = "/img_tmp/*", // Match all URIs of type /path/to/file - .method = HTTP_GET, - .handler = APPLY_BASIC_AUTH_FILTER(img_tmp_virtual_handler), - .user_ctx = (void*) base_path // Pass server data as context - }; - httpd_register_uri_handler(server, &img_tmp_handle); - - httpd_uri_t main_rest_handle = { - .uri = "/*", // Match all URIs of type /path/to/file - .method = HTTP_GET, - .handler = APPLY_BASIC_AUTH_FILTER(hello_main_handler), - .user_ctx = (void*) base_path // Pass server data as context - }; - httpd_register_uri_handler(server, &main_rest_handle); - -} - - httpd_handle_t start_webserver(void) { - httpd_handle_t server = NULL; - httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + httpd_handle_t my_webserver_httpd_handle = NULL; + httpd_config_t my_webserver_httpd_config = HTTPD_DEFAULT_CONFIG(); - config.task_priority = tskIDLE_PRIORITY+3; // previously -> 2022-12-11: tskIDLE_PRIORITY+1; 2021-09-24: tskIDLE_PRIORITY+5 - config.stack_size = 12288; // previously -> 2023-01-02: 32768 - config.core_id = 1; // previously -> 2023-01-02: 0, 2022-12-11: tskNO_AFFINITY; - config.server_port = 80; - config.ctrl_port = 32768; - config.max_open_sockets = 5; //20210921 --> previously 7 - config.max_uri_handlers = 41; // Make sure this fits all URI handlers. Memory usage in bytes: 6*max_uri_handlers - config.max_resp_headers = 8; - config.backlog_conn = 5; - config.lru_purge_enable = true; // this cuts old connections if new ones are needed. - config.recv_wait_timeout = 5; // default: 5 20210924 --> previously 30 - config.send_wait_timeout = 5; // default: 5 20210924 --> previously 30 - config.global_user_ctx = NULL; - config.global_user_ctx_free_fn = NULL; - config.global_transport_ctx = NULL; - config.global_transport_ctx_free_fn = NULL; - config.open_fn = NULL; - config.close_fn = NULL; -// config.uri_match_fn = NULL; - config.uri_match_fn = httpd_uri_match_wildcard; + my_webserver_httpd_config.task_priority = tskIDLE_PRIORITY + 3; // previously -> 2022-12-11: tskIDLE_PRIORITY+1; 2021-09-24: tskIDLE_PRIORITY+5 + my_webserver_httpd_config.stack_size = 12288; // previously -> 2023-01-02: 32768 + my_webserver_httpd_config.core_id = 1; // previously -> 2023-01-02: 0, 2022-12-11: tskNO_AFFINITY; + my_webserver_httpd_config.server_port = 80; + my_webserver_httpd_config.ctrl_port = 32768; + my_webserver_httpd_config.max_open_sockets = 5; // 20210921 --> previously 7 + my_webserver_httpd_config.max_uri_handlers = 42; // Make sure this fits all URI handlers. Memory usage in bytes: 6*max_uri_handlers + my_webserver_httpd_config.max_resp_headers = 8; + my_webserver_httpd_config.backlog_conn = 5; + my_webserver_httpd_config.lru_purge_enable = true; // this cuts old connections if new ones are needed. + my_webserver_httpd_config.recv_wait_timeout = 5; // default: 5 20210924 --> previously 30 + my_webserver_httpd_config.send_wait_timeout = 5; // default: 5 20210924 --> previously 30 + my_webserver_httpd_config.global_user_ctx = NULL; + my_webserver_httpd_config.global_user_ctx_free_fn = NULL; + my_webserver_httpd_config.global_transport_ctx = NULL; + my_webserver_httpd_config.global_transport_ctx_free_fn = NULL; + my_webserver_httpd_config.open_fn = NULL; + my_webserver_httpd_config.close_fn = NULL; + // my_webserver_httpd_config.uri_match_fn = NULL; + my_webserver_httpd_config.uri_match_fn = httpd_uri_match_wildcard; - starttime = getCurrentTimeString("%Y%m%d-%H%M%S"); + webserver_start_time = getCurrentTimeString("%Y%m%d-%H%M%S"); // Start the httpd server - ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port); - if (httpd_start(&server, &config) == ESP_OK) { + ESP_LOGI(TAG, "Starting server on port: '%d'", my_webserver_httpd_config.server_port); + if (httpd_start(&my_webserver_httpd_handle, &my_webserver_httpd_config) == ESP_OK) + { // Set URI handlers ESP_LOGI(TAG, "Registering URI handlers"); - return server; + return my_webserver_httpd_handle; } ESP_LOGI(TAG, "Error starting server!"); return NULL; } - void stop_webserver(httpd_handle_t server) { httpd_stop(server); } - -void disconnect_handler(void* arg, esp_event_base_t event_base, - int32_t event_id, void* event_data) +void webserver_register_uri(httpd_handle_t server, const char *base_path) { - httpd_handle_t* server = (httpd_handle_t*) arg; - if (*server) { - ESP_LOGI(TAG, "Stopping webserver"); - stop_webserver(*server); - *server = NULL; - } -} + httpd_uri_t info_handle = { + .uri = "/info", // Match all URIs of type /path/to/file + .method = HTTP_GET, + .handler = APPLY_BASIC_AUTH_FILTER(info_handler), + .user_ctx = (void *)base_path // Pass server data as context + }; + httpd_register_uri_handler(server, &info_handle); + httpd_uri_t sysinfo_handle = { + .uri = "/sysinfo", // Match all URIs of type /path/to/file + .method = HTTP_GET, + .handler = APPLY_BASIC_AUTH_FILTER(sysinfo_handler), + .user_ctx = (void *)base_path // Pass server data as context + }; + httpd_register_uri_handler(server, &sysinfo_handle); -void connect_handler(void* arg, esp_event_base_t event_base, - int32_t event_id, void* event_data) -{ - httpd_handle_t* server = (httpd_handle_t*) arg; - if (*server == NULL) { - ESP_LOGI(TAG, "Starting webserver"); - *server = start_webserver(); - } + httpd_uri_t wifi_scan_handle = { + .uri = "/wifiscan", // Match all URIs of type /path/to/file + .method = HTTP_GET, + .handler = APPLY_BASIC_AUTH_FILTER(wifi_scan_handler), + .user_ctx = (void *)base_path // Pass server data as context + }; + httpd_register_uri_handler(server, &wifi_scan_handle); + + httpd_uri_t start_time_handle = { + .uri = "/starttime", // Match all URIs of type /path/to/file + .method = HTTP_GET, + .handler = APPLY_BASIC_AUTH_FILTER(start_time_handler), + .user_ctx = NULL // Pass server data as context + }; + httpd_register_uri_handler(server, &start_time_handle); + + httpd_uri_t img_tmp_handle = { + .uri = "/img_tmp/*", // Match all URIs of type /path/to/file + .method = HTTP_GET, + .handler = APPLY_BASIC_AUTH_FILTER(img_tmp_virtual_handler), + .user_ctx = (void *)base_path // Pass server data as context + }; + httpd_register_uri_handler(server, &img_tmp_handle); + + httpd_uri_t main_rest_handle = { + .uri = "/*", // Match all URIs of type /path/to/file + .method = HTTP_GET, + .handler = APPLY_BASIC_AUTH_FILTER(main_handler), + .user_ctx = (void *)base_path // Pass server data as context + }; + httpd_register_uri_handler(server, &main_rest_handle); } diff --git a/code/main/server_main.h b/code/components/jomjol_network/server_main.h similarity index 53% rename from code/main/server_main.h rename to code/components/jomjol_network/server_main.h index ccf4c7cb..c50f6241 100644 --- a/code/main/server_main.h +++ b/code/components/jomjol_network/server_main.h @@ -1,23 +1,26 @@ +#pragma once + #ifndef SERVER_MAIN_H #define SERVER_MAIN_H +#include "defines.h" + #include #include #include #include #include #include -#include "nvs_flash.h" -#include "esp_netif.h" -#include "esp_eth.h" +#include +#include +#include #include "server_GPIO.h" #include -extern httpd_handle_t server; - httpd_handle_t start_webserver(void); +void stop_webserver(httpd_handle_t server); -void register_server_main_uri(httpd_handle_t server, const char *base_path); +void webserver_register_uri(httpd_handle_t server, const char *base_path); -#endif +#endif // SERVER_MAIN_H diff --git a/code/main/softAP.cpp b/code/components/jomjol_network/server_remote.cpp similarity index 58% rename from code/main/softAP.cpp rename to code/components/jomjol_network/server_remote.cpp index e1aab5f2..3c2e4237 100644 --- a/code/main/softAP.cpp +++ b/code/components/jomjol_network/server_remote.cpp @@ -1,102 +1,40 @@ -#ifdef ENABLE_SOFTAP -//if ENABLE_SOFTAP = disabled, set CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n in sdkconfig.defaults to save 28k of flash -#include "../../include/defines.h" +#include "defines.h" +#include "server_remote.h" +#include "connect_wifi_ap.h" -#include "softAP.h" - -/* WiFi softAP Example - This example code is in the Public Domain (or CC0 licensed, at your option.) - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ #include #include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_mac.h" -#include "esp_wifi.h" +#include +#include +#include +#include -#include "esp_log.h" -#include "nvs_flash.h" +#include +#include -#include "stdio.h" +#include + +#include +#include + +#include "time_sntp.h" #include "ClassLogFile.h" #include "server_help.h" -#include "defines.h" #include "Helper.h" #include "statusled.h" #include "server_ota.h" #include "basic_auth.h" -#include "lwip/err.h" -#include "lwip/sys.h" +static const char *TAG = "REMOTE SERVER"; -/* The examples use WiFi configuration that you can set via project configuration menu. - If you'd rather not, just change the below entries to strings with - the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" -*/ +std::string remote_webserver_start_time = ""; -bool isConfigINI = false; -bool isWlanINI = false; +bool is_config_ini = false; +bool is_wlan_ini = false; -static const char *TAG = "WIFI AP"; - - -static void wifi_event_handler(void* arg, esp_event_base_t event_base, - int32_t event_id, void* event_data) -{ - if (event_id == WIFI_EVENT_AP_STACONNECTED) { - wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data; - ESP_LOGI(TAG, "station " MACSTR " join, AID=%d", - MAC2STR(event->mac), event->aid); - } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) { - wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data; - ESP_LOGI(TAG, "station " MACSTR " leave, AID=%d", - MAC2STR(event->mac), event->aid); - } -} - - -void wifi_init_softAP(void) -{ - ESP_ERROR_CHECK(esp_netif_init()); - ESP_ERROR_CHECK(esp_event_loop_create_default()); - esp_netif_create_default_wifi_ap(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, - ESP_EVENT_ANY_ID, - &wifi_event_handler, - NULL, - NULL)); - - wifi_config_t wifi_config = { }; - - strcpy((char*)wifi_config.ap.ssid, (const char*) EXAMPLE_ESP_WIFI_SSID); - strcpy((char*)wifi_config.ap.password, (const char*) EXAMPLE_ESP_WIFI_PASS); - wifi_config.ap.channel = EXAMPLE_ESP_WIFI_CHANNEL; - wifi_config.ap.max_connection = EXAMPLE_MAX_STA_CONN; - wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK; - - if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) { - wifi_config.ap.authmode = WIFI_AUTH_OPEN; - } - - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config)); - ESP_ERROR_CHECK(esp_wifi_start()); - - ESP_LOGI(TAG, "started with SSID \"%s\", password: \"%s\", channel: %d. Connect to AP and open http://192.168.4.1", - EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL); -} - - -void SendHTTPResponse(httpd_req_t *req) +void remote_send_http_response(httpd_req_t *req) { std::string message = "

AI-on-the-edge - BASIC SETUP

This is an access point with a minimal server to setup the minimum required files and information on the device and the SD-card. "; message += "This mode is always started if one of the following files is missing: /wlan.ini or the /config/config.ini.

"; @@ -104,9 +42,9 @@ void SendHTTPResponse(httpd_req_t *req) message += "Please follow the below instructions.

"; httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str())); - isWlanINI = FileExists(WLAN_CONFIG_FILE); + is_wlan_ini = file_exists(NETWORK_CONFIG_FILE); - if (!isConfigINI) + if (!is_config_ini) { message = "

1. Upload initial configuration to sd-card

"; message += "The configuration file config.ini is missing and most propably the full configuration and html folder on the sd-card. "; @@ -129,7 +67,8 @@ void SendHTTPResponse(httpd_req_t *req) httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str())); return; } - if (!isWlanINI) + + if (!is_wlan_ini) { message = "

2. WLAN access credentials

"; message += ""; @@ -139,19 +78,9 @@ void SendHTTPResponse(httpd_req_t *req) message += "

ATTENTION:

Be sure about the WLAN settings. They cannot be reset afterwards. If ssid or password is wrong, you need to take out the sd-card and manually change them in \"wlan.ini\"!

"; httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str())); -// message = "

"; -// message += ""; -// message += ""; -// message += ""; -// message += ""; -// message += ""; -// httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str())); - - message = ""; message += ""; httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str())); return; @@ -167,173 +96,190 @@ void SendHTTPResponse(httpd_req_t *req) httpd_resp_send_chunk(req, message.c_str(), strlen(message.c_str())); } - -esp_err_t test_handler(httpd_req_t *req) +esp_err_t remote_test_handler(httpd_req_t *req) { - SendHTTPResponse(req); + remote_send_http_response(req); httpd_resp_send_chunk(req, NULL, 0); return ESP_OK; } - -esp_err_t reboot_handlerAP(httpd_req_t *req) +esp_err_t remote_reboot_handler(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_ota_update - Start"); -#endif LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update."); doRebootOTA(); return ESP_OK; } - -esp_err_t config_ini_handler(httpd_req_t *req) +esp_err_t remote_config_ini_handler(httpd_req_t *req) { -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_ota_update - Start"); -#endif - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "config_ini_handler"); char _query[400]; - char _valuechar[100]; + char _valuechar[100]; std::string fn = "/sdcard/firmware/"; std::string _task = ""; std::string ssid = ""; std::string pwd = ""; - std::string hn = ""; // hostname + std::string hn = ""; // hostname std::string ip = ""; - std::string gw = ""; // gateway - std::string nm = ""; // netmask + std::string gw = ""; // gateway + std::string nm = ""; // netmask std::string dns = ""; - std::string rssithreshold = ""; //rssi threshold for WIFI roaming + std::string rssithreshold = ""; // rssi threshold for WIFI roaming std::string text = ""; - if (httpd_req_get_url_query_str(req, _query, 400) == ESP_OK) { ESP_LOGD(TAG, "Query: %s", _query); - + if (httpd_query_key_value(_query, "ssid", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "ssid is found: %s", _valuechar); - ssid = UrlDecode(std::string(_valuechar)); + ssid = url_decode(std::string(_valuechar)); } if (httpd_query_key_value(_query, "pwd", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "pwd is found: %s", _valuechar); - pwd = UrlDecode(std::string(_valuechar)); + pwd = url_decode(std::string(_valuechar)); } if (httpd_query_key_value(_query, "ssid", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "ssid is found: %s", _valuechar); - ssid = UrlDecode(std::string(_valuechar)); + ssid = url_decode(std::string(_valuechar)); } if (httpd_query_key_value(_query, "hn", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "hostname is found: %s", _valuechar); - hn = UrlDecode(std::string(_valuechar)); + hn = url_decode(std::string(_valuechar)); } if (httpd_query_key_value(_query, "ip", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "ip is found: %s", _valuechar); - ip = UrlDecode(std::string(_valuechar)); + ip = url_decode(std::string(_valuechar)); } if (httpd_query_key_value(_query, "gw", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "gateway is found: %s", _valuechar); - gw = UrlDecode(std::string(_valuechar)); + gw = url_decode(std::string(_valuechar)); } if (httpd_query_key_value(_query, "nm", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "netmask is found: %s", _valuechar); - nm = UrlDecode(std::string(_valuechar)); + nm = url_decode(std::string(_valuechar)); } if (httpd_query_key_value(_query, "dns", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "dns is found: %s", _valuechar); - dns = UrlDecode(std::string(_valuechar)); + dns = url_decode(std::string(_valuechar)); } if (httpd_query_key_value(_query, "rssithreshold", _valuechar, 100) == ESP_OK) { ESP_LOGD(TAG, "rssithreshold is found: %s", _valuechar); - rssithreshold = UrlDecode(std::string(_valuechar)); + rssithreshold = url_decode(std::string(_valuechar)); } } - FILE* configfilehandle = fopen(WLAN_CONFIG_FILE, "w"); + FILE *configfilehandle = fopen(NETWORK_CONFIG_FILE, "w"); - text = ";++++++++++++++++++++++++++++++++++\n"; + text = ";++++++++++++++++++++++++++++++++++\n"; text += "; AI on the edge - WLAN configuration\n"; text += "; ssid: Name of WLAN network (mandatory), e.g. \"WLAN-SSID\"\n"; text += "; password: Password of WLAN network (mandatory), e.g. \"PASSWORD\"\n\n"; fputs(text.c_str(), configfilehandle); - + if (ssid.length()) + { ssid = "ssid = \"" + ssid + "\"\n"; + } else + { ssid = "ssid = \"\"\n"; + } fputs(ssid.c_str(), configfilehandle); if (pwd.length()) + { pwd = "password = \"" + pwd + "\"\n"; + } else + { pwd = "password = \"\"\n"; + } fputs(pwd.c_str(), configfilehandle); - text = "\n;++++++++++++++++++++++++++++++++++\n"; + text = "\n;++++++++++++++++++++++++++++++++++\n"; text += "; Hostname: Name of device in network\n"; text += "; This parameter can be configured via WebUI configuration\n"; text += "; Default: \"watermeter\", if nothing is configured\n\n"; fputs(text.c_str(), configfilehandle); if (hn.length()) + { hn = "hostname = \"" + hn + "\"\n"; + } else + { hn = ";hostname = \"watermeter\"\n"; + } fputs(hn.c_str(), configfilehandle); - text = "\n;++++++++++++++++++++++++++++++++++\n"; + text = "\n;++++++++++++++++++++++++++++++++++\n"; text += "; Fixed IP: If you like to use fixed IP instead of DHCP (default), the following\n"; text += "; parameters needs to be configured: ip, gateway, netmask are mandatory, dns optional\n\n"; fputs(text.c_str(), configfilehandle); if (ip.length()) + { ip = "ip = \"" + ip + "\"\n"; + } else + { ip = ";ip = \"xxx.xxx.xxx.xxx\"\n"; + } fputs(ip.c_str(), configfilehandle); if (gw.length()) + { gw = "gateway = \"" + gw + "\"\n"; + } else + { gw = ";gateway = \"xxx.xxx.xxx.xxx\"\n"; + } fputs(gw.c_str(), configfilehandle); if (nm.length()) + { nm = "netmask = \"" + nm + "\"\n"; + } else + { nm = ";netmask = \"xxx.xxx.xxx.xxx\"\n"; + } fputs(nm.c_str(), configfilehandle); - text = "\n;++++++++++++++++++++++++++++++++++\n"; + text = "\n;++++++++++++++++++++++++++++++++++\n"; text += "; DNS server (optional, if no DNS is configured, gateway address will be used)\n\n"; fputs(text.c_str(), configfilehandle); if (dns.length()) + { dns = "dns = \"" + dns + "\"\n"; + } else + { dns = ";dns = \"xxx.xxx.xxx.xxx\"\n"; + } fputs(dns.c_str(), configfilehandle); - text = "\n;++++++++++++++++++++++++++++++++++\n"; + text = "\n;++++++++++++++++++++++++++++++++++\n"; text += "; WIFI Roaming:\n"; text += "; Network assisted roaming protocol is activated by default\n"; text += "; AP / mesh system needs to support roaming protocol 802.11k/v\n"; @@ -345,9 +291,13 @@ esp_err_t config_ini_handler(httpd_req_t *req) fputs(text.c_str(), configfilehandle); if (rssithreshold.length()) + { rssithreshold = "RSSIThreshold = " + rssithreshold + "\n"; + } else + { rssithreshold = "RSSIThreshold = 0\n"; + } fputs(rssithreshold.c_str(), configfilehandle); fflush(configfilehandle); @@ -355,44 +305,42 @@ esp_err_t config_ini_handler(httpd_req_t *req) std::string zw = "ota without parameter - should not be the case!"; httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - httpd_resp_send(req, zw.c_str(), zw.length()); + httpd_resp_send(req, zw.c_str(), zw.length()); ESP_LOGD(TAG, "end config.ini"); return ESP_OK; } - -esp_err_t upload_post_handlerAP(httpd_req_t *req) +esp_err_t remote_upload_post_handler(httpd_req_t *req) { - printf("Start des Post Handlers\n"); - MakeDir("/sdcard/config"); - MakeDir("/sdcard/firmware"); - MakeDir("/sdcard/html"); - MakeDir("/sdcard/img_tmp"); - MakeDir("/sdcard/log"); - MakeDir("/sdcard/demo"); - printf("Nach Start des Post Handlers\n"); + printf("Starting the post handler\n"); + make_dir("/sdcard/config"); + make_dir("/sdcard/firmware"); + make_dir("/sdcard/html"); + make_dir("/sdcard/img_tmp"); + make_dir("/sdcard/log"); + make_dir("/sdcard/demo"); + printf("After starting the post handler\n"); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "upload_post_handlerAP"); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "remote_upload_post_handler"); char filepath[FILE_PATH_MAX]; FILE *fd = NULL; - const char *filename = get_path_from_uri(filepath, "/sdcard", - req->uri + sizeof("/upload") - 1, sizeof(filepath)); - if (!filename) { + const char *filename = get_path_from_uri(filepath, "/sdcard", req->uri + sizeof("/upload") - 1, sizeof(filepath)); + if (!filename) + { httpd_resp_send_err(req, HTTPD_414_URI_TOO_LONG, "Filename too long"); return ESP_FAIL; } printf("filepath: %s, filename: %s\n", filepath, filename); - DeleteFile(std::string(filepath)); - - + delete_file(std::string(filepath)); fd = fopen(filepath, "w"); - if (!fd) { + if (!fd) + { ESP_LOGE(TAG, "Failed to create file: %s", filepath); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create file"); return ESP_FAIL; @@ -407,13 +355,13 @@ esp_err_t upload_post_handlerAP(httpd_req_t *req) printf("remaining: %d\n", remaining); - - - while (remaining > 0) { - + while (remaining > 0) + { ESP_LOGI(TAG, "Remaining size: %d", remaining); - if ((received = httpd_req_recv(req, buf, MIN(remaining, 1024))) <= 0) { - if (received == HTTPD_SOCK_ERR_TIMEOUT) { + if ((received = httpd_req_recv(req, buf, MIN(remaining, 1024))) <= 0) + { + if (received == HTTPD_SOCK_ERR_TIMEOUT) + { continue; } @@ -425,7 +373,8 @@ esp_err_t upload_post_handlerAP(httpd_req_t *req) return ESP_FAIL; } - if (received && (received != fwrite(buf, 1, received, fd))) { + if (received && (received != fwrite(buf, 1, received, fd))) + { fclose(fd); unlink(filepath); @@ -436,15 +385,15 @@ esp_err_t upload_post_handlerAP(httpd_req_t *req) remaining -= received; } - fclose(fd); - isConfigINI = true; - FILE* pfile = fopen("/sdcard/update.txt", "w"); - std::string _s_zw= "/sdcard" + std::string(filename); + fclose(fd); + is_config_ini = true; + + FILE *pfile = fopen("/sdcard/update.txt", "w"); + std::string _s_zw = "/sdcard" + std::string(filename); fwrite(_s_zw.c_str(), strlen(_s_zw.c_str()), 1, pfile); fclose(pfile); - ESP_LOGI(TAG, "File reception complete"); httpd_resp_set_hdr(req, "Location", "/test"); httpd_resp_set_status(req, "303 See Other"); @@ -456,74 +405,66 @@ esp_err_t upload_post_handlerAP(httpd_req_t *req) return ESP_OK; } - -httpd_handle_t start_webserverAP(void) +httpd_handle_t start_remote_webserver(void) { - httpd_config_t config = HTTPD_DEFAULT_CONFIG(); - config.uri_match_fn = httpd_uri_match_wildcard; - httpd_handle_t server = NULL; - if (httpd_start(&server, &config) == ESP_OK) { - // Do something + httpd_handle_t my_remote_webserver_httpd_handle = NULL; + httpd_config_t my_remote_webserver_httpd_config = HTTPD_DEFAULT_CONFIG(); + + remote_webserver_start_time = getCurrentTimeString("%Y%m%d-%H%M%S"); + + my_remote_webserver_httpd_config.uri_match_fn = httpd_uri_match_wildcard; + + // Start the httpd server + ESP_LOGI(TAG, "Starting server on port: '%d'", my_remote_webserver_httpd_config.server_port); + if (httpd_start(&my_remote_webserver_httpd_handle, &my_remote_webserver_httpd_config) == ESP_OK) + { + // Set URI handlers + ESP_LOGI(TAG, "Registering URI handlers"); + return my_remote_webserver_httpd_handle; } + ESP_LOGI(TAG, "Error starting ap server!"); + return NULL; +} + +void stop_remote_webserver(httpd_handle_t server) +{ + httpd_stop(server); +} + +httpd_handle_t remote_webserver_register_uri(httpd_handle_t server) +{ httpd_uri_t reboot_handle = { - .uri = "/reboot", // Match all URIs of type /path/to/file - .method = HTTP_GET, - .handler = APPLY_BASIC_AUTH_FILTER(reboot_handlerAP), - .user_ctx = NULL // Pass server data as context + .uri = "/reboot", // Match all URIs of type /path/to/file + .method = HTTP_GET, + .handler = APPLY_BASIC_AUTH_FILTER(remote_reboot_handler), + .user_ctx = NULL // Pass server data as context }; httpd_register_uri_handler(server, &reboot_handle); httpd_uri_t config_ini_handle = { - .uri = "/config", // Match all URIs of type /path/to/file - .method = HTTP_GET, - .handler = APPLY_BASIC_AUTH_FILTER(config_ini_handler), - .user_ctx = NULL // Pass server data as context + .uri = "/config", // Match all URIs of type /path/to/file + .method = HTTP_GET, + .handler = APPLY_BASIC_AUTH_FILTER(remote_config_ini_handler), + .user_ctx = NULL // Pass server data as context }; httpd_register_uri_handler(server, &config_ini_handle); /* URI handler for uploading files to server */ httpd_uri_t file_uploadAP = { - .uri = "/upload/*", // Match all URIs of type /upload/path/to/file - .method = HTTP_POST, - .handler = APPLY_BASIC_AUTH_FILTER(upload_post_handlerAP), - .user_ctx = NULL // Pass server data as context + .uri = "/upload/*", // Match all URIs of type /upload/path/to/file + .method = HTTP_POST, + .handler = APPLY_BASIC_AUTH_FILTER(remote_upload_post_handler), + .user_ctx = NULL // Pass server data as context }; httpd_register_uri_handler(server, &file_uploadAP); httpd_uri_t test_uri = { - .uri = "*", - .method = HTTP_GET, - .handler = APPLY_BASIC_AUTH_FILTER(test_handler), - .user_ctx = NULL - }; + .uri = "*", + .method = HTTP_GET, + .handler = APPLY_BASIC_AUTH_FILTER(remote_test_handler), + .user_ctx = NULL}; httpd_register_uri_handler(server, &test_uri); return NULL; } - - -void CheckStartAPMode() -{ - isConfigINI = FileExists(CONFIG_FILE); - isWlanINI = FileExists(WLAN_CONFIG_FILE); - - if (!isConfigINI) - ESP_LOGW(TAG, "config.ini not found!"); - - if (!isWlanINI) - ESP_LOGW(TAG, "wlan.ini not found!"); - - if (!isConfigINI || !isWlanINI) - { - ESP_LOGI(TAG, "Starting access point for remote configuration"); - StatusLED(AP_OR_OTA, 2, true); - wifi_init_softAP(); - start_webserverAP(); - while(1) { // wait until reboot within task_do_Update_ZIP - vTaskDelay(1000 / portTICK_PERIOD_MS); - } - } -} - -#endif //#ifdef ENABLE_SOFTAP diff --git a/code/components/jomjol_network/server_remote.h b/code/components/jomjol_network/server_remote.h new file mode 100644 index 00000000..389832d9 --- /dev/null +++ b/code/components/jomjol_network/server_remote.h @@ -0,0 +1,25 @@ +#pragma once + +#ifndef SERVER_REMOTE_H +#define SERVER_REMOTE_H + +#include "defines.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +httpd_handle_t start_remote_webserver(void); +void stop_remote_webserver(httpd_handle_t server); + +httpd_handle_t remote_webserver_register_uri(httpd_handle_t server); + +#endif // SERVER_REMOTE_H diff --git a/code/components/jomjol_tfliteclass/CMakeLists.txt b/code/components/jomjol_tfliteclass/CMakeLists.txt index 7f56336f..3faacc29 100644 --- a/code/components/jomjol_tfliteclass/CMakeLists.txt +++ b/code/components/jomjol_tfliteclass/CMakeLists.txt @@ -1,7 +1,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." + INCLUDE_DIRS "." "../../include" REQUIRES jomjol_image_proc jomjol_logfile jomjol_flowcontroll jomjol_helper) diff --git a/code/components/jomjol_tfliteclass/CTfLiteClass.cpp b/code/components/jomjol_tfliteclass/CTfLiteClass.cpp index bf022d7a..b2a2aa8a 100644 --- a/code/components/jomjol_tfliteclass/CTfLiteClass.cpp +++ b/code/components/jomjol_tfliteclass/CTfLiteClass.cpp @@ -1,75 +1,138 @@ +#include "defines.h" + #include "CTfLiteClass.h" #include "ClassLogFile.h" #include "Helper.h" #include "psram.h" #include "esp_log.h" -#include "../../include/defines.h" #include -// #define DEBUG_DETAIL_ON - - static const char *TAG = "TFLITE"; - -void CTfLiteClass::MakeStaticResolver() +CTfLiteClass::CTfLiteClass() { - resolver.AddFullyConnected(); - resolver.AddReshape(); - resolver.AddSoftmax(); - resolver.AddConv2D(); - resolver.AddMaxPool2D(); - resolver.AddQuantize(); - resolver.AddMul(); - resolver.AddAdd(); - resolver.AddLeakyRelu(); - resolver.AddDequantize(); + model = nullptr; + modelfile = NULL; + interpreter = nullptr; + input = nullptr; + output = nullptr; + kTensorArenaSize = TENSOR_ARENA_SIZE; + tensor_arena = (uint8_t *)psram_get_shared_tensor_arena_memory(); } +CTfLiteClass::~CTfLiteClass() +{ + delete interpreter; + psram_free_shared_tensor_arena_and_model_memory(); +} + +bool CTfLiteClass::MakeStaticResolver(void) +{ + if (resolver.AddFullyConnected() != kTfLiteOk) + { + ESP_LOGE(TAG, "load AddFullyConnected() failed"); + return false; + } + if (resolver.AddReshape() != kTfLiteOk) + { + ESP_LOGE(TAG, "load AddReshape() failed"); + return false; + } + if (resolver.AddSoftmax() != kTfLiteOk) + { + ESP_LOGE(TAG, "load AddSoftmax() failed"); + return false; + } + if (resolver.AddConv2D() != kTfLiteOk) + { + ESP_LOGE(TAG, "load AddConv2D() failed"); + return false; + } + if (resolver.AddMaxPool2D() != kTfLiteOk) + { + ESP_LOGE(TAG, "load AddMaxPool2D() failed"); + return false; + } + if (resolver.AddQuantize() != kTfLiteOk) + { + ESP_LOGE(TAG, "load AddQuantize() failed"); + return false; + } + if (resolver.AddMul() != kTfLiteOk) + { + ESP_LOGE(TAG, "load AddMul() failed"); + return false; + } + if (resolver.AddAdd() != kTfLiteOk) + { + ESP_LOGE(TAG, "load AddAdd() failed"); + return false; + } + if (resolver.AddLeakyRelu() != kTfLiteOk) + { + ESP_LOGE(TAG, "load AddLeakyRelu() failed"); + return false; + } + if (resolver.AddDequantize() != kTfLiteOk) + { + ESP_LOGE(TAG, "load AddDequantize() failed"); + return false; + } + + return true; +} float CTfLiteClass::GetOutputValue(int nr) { - TfLiteTensor* output2 = this->interpreter->output(0); + TfLiteTensor *output2 = interpreter->output(0); - int numeroutput = output2->dims->data[1]; - if ((nr+1) > numeroutput) - return -1000; + int numer_output = output2->dims->data[1]; + if ((nr + 1) > numer_output) + { + return -1000; + } - return output2->data.f[nr]; + return output2->data.f[nr]; } - int CTfLiteClass::GetClassFromImageBasis(CImageBasis *rs) { - if (!LoadInputImageBasis(rs)) - return -1000; + if (!LoadInputImageBasis(rs)) + { + return -1000; + } - Invoke(); + Invoke(); - return GetOutClassification(); + return GetOutClassification(); } - int CTfLiteClass::GetOutClassification(int _von, int _bis) { - TfLiteTensor* output2 = interpreter->output(0); + TfLiteTensor *output2 = interpreter->output(0); float zw_max; float zw; int zw_class; if (output2 == NULL) + { return -1; + } int numeroutput = output2->dims->data[1]; - //ESP_LOGD(TAG, "number output neurons: %d", numeroutput); + // ESP_LOGD(TAG, "number output neurons: %d", numeroutput); if (_bis == -1) - _bis = numeroutput -1; + { + _bis = numeroutput - 1; + } if (_von == -1) + { _von = 0; + } if (_bis >= numeroutput) { @@ -84,165 +147,189 @@ int CTfLiteClass::GetOutClassification(int _von, int _bis) zw = output2->data.f[i]; if (zw > zw_max) { - zw_max = zw; - zw_class = i; + zw_max = zw; + zw_class = i; } } return (zw_class - _von); } - void CTfLiteClass::GetInputDimension(bool silent = false) { - TfLiteTensor* input2 = this->interpreter->input(0); + TfLiteTensor *input2 = interpreter->input(0); int numdim = input2->dims->size; - if (!silent) ESP_LOGD(TAG, "NumDimension: %d", numdim); + if (!silent) + { + ESP_LOGD(TAG, "NumDimension: %d", numdim); + } int sizeofdim; for (int j = 0; j < numdim; ++j) { sizeofdim = input2->dims->data[j]; - if (!silent) ESP_LOGD(TAG, "SizeOfDimension %d: %d", j, sizeofdim); - if (j == 1) im_height = sizeofdim; - if (j == 2) im_width = sizeofdim; - if (j == 3) im_channel = sizeofdim; + if (!silent) + { + ESP_LOGD(TAG, "SizeOfDimension %d: %d", j, sizeofdim); + } + if (j == 1) + { + im_height = sizeofdim; + } + if (j == 2) + { + im_width = sizeofdim; + } + if (j == 3) + { + im_channel = sizeofdim; + } } } - int CTfLiteClass::ReadInputDimenstion(int _dim) { if (_dim == 0) + { return im_width; + } if (_dim == 1) + { return im_height; + } if (_dim == 2) + { return im_channel; + } return -1; } - int CTfLiteClass::GetAnzOutPut(bool silent) { - TfLiteTensor* output2 = this->interpreter->output(0); + TfLiteTensor *output2 = interpreter->output(0); int numdim = output2->dims->size; - if (!silent) ESP_LOGD(TAG, "NumDimension: %d", numdim); + if (!silent) + { + ESP_LOGD(TAG, "NumDimension: %d", numdim); + } int sizeofdim; for (int j = 0; j < numdim; ++j) { sizeofdim = output2->dims->data[j]; - if (!silent) ESP_LOGD(TAG, "SizeOfDimension %d: %d", j, sizeofdim); + if (!silent) + { + ESP_LOGD(TAG, "SizeOfDimension %d: %d", j, sizeofdim); + } } - float fo; // Process the inference results. int numeroutput = output2->dims->data[1]; for (int i = 0; i < numeroutput; ++i) { - fo = output2->data.f[i]; - if (!silent) ESP_LOGD(TAG, "Result %d: %f", i, fo); + fo = output2->data.f[i]; + if (!silent) + { + ESP_LOGD(TAG, "Result %d: %f", i, fo); + } } return numeroutput; } - void CTfLiteClass::Invoke() { - if (interpreter != nullptr) - interpreter->Invoke(); + if (interpreter != nullptr) + { + interpreter->Invoke(); + } } - bool CTfLiteClass::LoadInputImageBasis(CImageBasis *rs) { - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CTfLiteClass::LoadInputImageBasis - Start"); - #endif + unsigned int w = rs->width; + unsigned int h = rs->height; + unsigned char red, green, blue; + // ESP_LOGD(TAG, "Image: %s size: %d x %d\n", _fn.c_str(), w, h); - unsigned int w = rs->width; - unsigned int h = rs->height; - unsigned char red, green, blue; -// ESP_LOGD(TAG, "Image: %s size: %d x %d\n", _fn.c_str(), w, h); + input_i = 0; + float *input_data_ptr = (interpreter->input(0))->data.f; - input_i = 0; - float* input_data_ptr = (interpreter->input(0))->data.f; - - for (int y = 0; y < h; ++y) - for (int x = 0; x < w; ++x) - { - red = rs->GetPixelColor(x, y, 0); - green = rs->GetPixelColor(x, y, 1); - blue = rs->GetPixelColor(x, y, 2); - *(input_data_ptr) = (float) red; - input_data_ptr++; - *(input_data_ptr) = (float) green; - input_data_ptr++; - *(input_data_ptr) = (float) blue; - input_data_ptr++; - } - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CTfLiteClass::LoadInputImageBasis - done"); - #endif - - return true; -} - - - -bool CTfLiteClass::MakeAllocate() -{ - MakeStaticResolver(); - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CTLiteClass::Alloc start"); - #endif - - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CTfLiteClass::MakeAllocate"); - this->interpreter = new tflite::MicroInterpreter(this->model, resolver, this->tensor_arena, this->kTensorArenaSize); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trying to load the model. If it crashes here, it ist most likely due to a corrupted model!"); - - if (this->interpreter) + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) { - TfLiteStatus allocate_status = this->interpreter->AllocateTensors(); - if (allocate_status != kTfLiteOk) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "AllocateTensors() failed"); + red = rs->GetPixelColor(x, y, 0); + green = rs->GetPixelColor(x, y, 1); + blue = rs->GetPixelColor(x, y, 2); - this->GetInputDimension(); - return false; - } - } - else - { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "new tflite::MicroInterpreter failed"); - LogFile.WriteHeapInfo("CTfLiteClass::MakeAllocate-new tflite::MicroInterpreter failed"); - return false; + *(input_data_ptr) = (float)red; + input_data_ptr++; + + *(input_data_ptr) = (float)green; + input_data_ptr++; + + *(input_data_ptr) = (float)blue; + input_data_ptr++; } + } - - #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CTLiteClass::Alloc done"); - #endif - - return true; + return true; } - -void CTfLiteClass::GetInputTensorSize() +bool CTfLiteClass::MakeAllocate(void) { -#ifdef DEBUG_DETAIL_ON - float *zw = this->input; - int test = sizeof(zw); - ESP_LOGD(TAG, "Input Tensor Dimension: %d", test); -#endif -} + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CTfLiteClass::MakeAllocate"); + if (!MakeStaticResolver()) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CTfLiteClass::MakeAllocate - resolver could not be loaded!"); + return false; + } + + if (!model) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CTfLiteClass::MakeAllocate - no model loaded!"); + return false; + } + + if (model->version() != TFLITE_SCHEMA_VERSION) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "The selected model does not match the tflite schema version!"); + return false; + } + + if (!tensor_arena) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CTfLiteClass::MakeAllocate - tensor_arena not allocate"); + return false; + } + + interpreter = new tflite::MicroInterpreter(model, resolver, tensor_arena, kTensorArenaSize); + // LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trying to load the model. If it crashes here, it ist most likely due to a corrupted model!"); + + if (interpreter) + { + TfLiteStatus allocate_status = interpreter->AllocateTensors(); + if (allocate_status != kTfLiteOk) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "AllocateTensors() failed"); + GetInputDimension(); + return false; + } + } + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "new tflite::MicroInterpreter failed"); + LogFile.WriteHeapInfo("CTfLiteClass::MakeAllocate-new tflite::MicroInterpreter failed"); + return false; + } + + return true; +} long CTfLiteClass::GetFileSize(std::string filename) { @@ -256,98 +343,69 @@ long CTfLiteClass::GetFileSize(std::string filename) rc = stat(filename.c_str(), &stat_buf); fclose(pFile); } - - return rc == 0 ? stat_buf.st_size : -1; + + return (rc == 0 ? stat_buf.st_size : -1); } - -bool CTfLiteClass::ReadFileToModel(std::string _fn) +bool CTfLiteClass::ReadFileToModel(std::string filename) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CTfLiteClass::ReadFileToModel: " + _fn); - - long size = GetFileSize(_fn); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CTfLiteClass::ReadFileToModel: " + filename); - if (size == -1) + long size = GetFileSize(filename); + if (size == -1) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Model file doesn't exist: " + filename + "!"); + return false; + } + else if (size > MAX_MODEL_SIZE) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Unable to load model '" + filename + "'! It does not fit in the reserved shared memory in PSRAM!"); + return false; + } + + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Loading Model " + filename + " /size: " + std::to_string(size) + " bytes..."); + + modelfile = (unsigned char *)psram_get_shared_model_memory(); + if (modelfile != NULL) + { + FILE *pFile = fopen(filename.c_str(), "rb"); // previously only "rb + if (pFile != NULL) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Model file doesn't exist: " + _fn + "!"); - return false; + fread(modelfile, 1, size, pFile); + fclose(pFile); + + return true; } - else if(size > MAX_MODEL_SIZE) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Unable to load model '" + _fn + "'! It does not fit in the reserved shared memory in PSRAM!"); - return false; - } - - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Loading Model " + _fn + " /size: " + std::to_string(size) + " bytes..."); - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CTLiteClass::Alloc modelfile start"); -#endif - - modelfile = (unsigned char*)psram_get_shared_model_memory(); - - if (modelfile != NULL) + else { - FILE *pFile = fopen(_fn.c_str(), "rb"); // previously only "rb - - if (pFile != NULL) - { - fread(modelfile, 1, size, pFile); - fclose(pFile); - -#ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("CTLiteClass::Alloc modelfile successful"); -#endif - - return true; - } - else - { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CTfLiteClass::ReadFileToModel: Model does not exist"); - return false; - } - } - else - { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CTfLiteClass::ReadFileToModel: Can't allocate enough memory: " + std::to_string(size)); - LogFile.WriteHeapInfo("CTfLiteClass::ReadFileToModel"); - - return false; - } -} - - -bool CTfLiteClass::LoadModel(std::string _fn) -{ - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CTfLiteClass::LoadModel"); - - if (!ReadFileToModel(_fn.c_str())) { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CTfLiteClass::ReadFileToModel: Model does not exist"); return false; } + } + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CTfLiteClass::ReadFileToModel: Can't allocate enough memory: " + std::to_string(size)); + LogFile.WriteHeapInfo("CTfLiteClass::ReadFileToModel"); - model = tflite::GetModel(modelfile); - - if(model == nullptr) - return false; - - return true; + return false; + } } - -CTfLiteClass::CTfLiteClass() +bool CTfLiteClass::LoadModel(std::string filename) { - this->model = nullptr; - this->modelfile = NULL; - this->interpreter = nullptr; - this->input = nullptr; - this->output = nullptr; - this->kTensorArenaSize = TENSOR_ARENA_SIZE; - this->tensor_arena = (uint8_t*)psram_get_shared_tensor_arena_memory(); + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CTfLiteClass::LoadModel"); + + if (!ReadFileToModel(filename.c_str())) + { + return false; + } + + model = tflite::GetModel(modelfile); + + if (model == nullptr) + { + return false; + } + + return true; } - - -CTfLiteClass::~CTfLiteClass() -{ - delete this->interpreter; - - psram_free_shared_tensor_arena_and_model_memory(); -} diff --git a/code/components/jomjol_tfliteclass/CTfLiteClass.h b/code/components/jomjol_tfliteclass/CTfLiteClass.h index 1063735f..3b06640c 100644 --- a/code/components/jomjol_tfliteclass/CTfLiteClass.h +++ b/code/components/jomjol_tfliteclass/CTfLiteClass.h @@ -12,46 +12,42 @@ #include "CImageBasis.h" - class CTfLiteClass { - protected: - tflite::MicroMutableOpResolver<10> resolver; - const tflite::Model* model; - tflite::MicroInterpreter* interpreter; - TfLiteTensor* output = nullptr; +protected: + tflite::MicroMutableOpResolver<10> resolver; + const tflite::Model *model; + tflite::MicroInterpreter *interpreter; + TfLiteTensor *output = nullptr; - int kTensorArenaSize; - uint8_t *tensor_arena; + int kTensorArenaSize; + uint8_t *tensor_arena; - unsigned char *modelfile = NULL; + unsigned char *modelfile = NULL; + float *input; + int input_i; + int im_height, im_width, im_channel; - float* input; - int input_i; - int im_height, im_width, im_channel; + long GetFileSize(std::string filename); + bool ReadFileToModel(std::string filename); + bool MakeStaticResolver(void); - long GetFileSize(std::string filename); - bool ReadFileToModel(std::string _fn); - void MakeStaticResolver(); +public: + CTfLiteClass(); + ~CTfLiteClass(); + bool LoadModel(std::string filename); + bool MakeAllocate(void); + bool LoadInputImageBasis(CImageBasis *rs); + void Invoke(void); + int GetAnzOutPut(bool silent = true); + int GetOutClassification(int _von = -1, int _bis = -1); - public: - CTfLiteClass(); - ~CTfLiteClass(); - bool LoadModel(std::string _fn); - bool MakeAllocate(); - void GetInputTensorSize(); - bool LoadInputImageBasis(CImageBasis *rs); - void Invoke(); - int GetAnzOutPut(bool silent = true); - int GetOutClassification(int _von = -1, int _bis = -1); + int GetClassFromImageBasis(CImageBasis *rs); - int GetClassFromImageBasis(CImageBasis *rs); - std::string GetStatusFlow(); - - float GetOutputValue(int nr); - void GetInputDimension(bool silent); - int ReadInputDimenstion(int _dim); + float GetOutputValue(int nr); + void GetInputDimension(bool silent); + int ReadInputDimenstion(int _dim); }; -#endif //CTFLITECLASS_H \ No newline at end of file +#endif // CTFLITECLASS_H diff --git a/code/components/jomjol_time_sntp/CMakeLists.txt b/code/components/jomjol_time_sntp/CMakeLists.txt index 4988e2a7..7d790c1c 100644 --- a/code/components/jomjol_time_sntp/CMakeLists.txt +++ b/code/components/jomjol_time_sntp/CMakeLists.txt @@ -1,7 +1,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." - REQUIRES esp_netif esp-tflite-micro jomjol_logfile jomjol_configfile) + INCLUDE_DIRS "." "../../include" + REQUIRES esp_netif esp-tflite-micro jomjol_logfile jomjol_flowcontroll jomjol_helper) diff --git a/code/components/jomjol_time_sntp/time_sntp.cpp b/code/components/jomjol_time_sntp/time_sntp.cpp index 736003fc..fdc0d735 100644 --- a/code/components/jomjol_time_sntp/time_sntp.cpp +++ b/code/components/jomjol_time_sntp/time_sntp.cpp @@ -1,29 +1,30 @@ +#include "defines.h" +#include "Helper.h" + #include "time_sntp.h" #include #include #include + #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" + #include "esp_system.h" #include "esp_log.h" #include "esp_attr.h" #include "esp_sleep.h" #include "esp_netif_sntp.h" -#include "../../include/defines.h" - +#include "ClassFlow.h" #include "ClassLogFile.h" -#include "configFile.h" -#include "Helper.h" - - static const char *TAG = "SNTP"; static std::string timeZone = ""; -static std::string timeServer = "undefined"; +static std::string timeServer = ""; + static bool useNtp = true; static bool timeWasNotSetAtBoot = false; static bool timeWasNotSetAtBoot_PrintStartBlock = false; @@ -34,9 +35,7 @@ static std::string getServerName(void); int LocalTimeToUTCOffsetSeconds; - - -std::string ConvertTimeToString(time_t _time, const char * frm) +std::string ConvertTimeToString(time_t _time, const char *frm) { struct tm timeinfo; char strftime_buf[64]; @@ -47,8 +46,7 @@ std::string ConvertTimeToString(time_t _time, const char * frm) return result; } - -std::string getCurrentTimeString(const char * frm) +std::string getCurrentTimeString(const char *frm) { time_t now; struct tm timeinfo; @@ -61,38 +59,41 @@ std::string getCurrentTimeString(const char * frm) return result; } - void time_sync_notification_cb(struct timeval *tv) { - if (timeWasNotSetAtBoot_PrintStartBlock) { + if (timeWasNotSetAtBoot_PrintStartBlock) + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "================================================="); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "==================== Start ======================"); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "== Logs before time sync -> log_1970-01-01.txt =="); timeWasNotSetAtBoot_PrintStartBlock = false; } - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Time is synced with NTP Server " + - getServerName() + ": " + getCurrentTimeString("%Y-%m-%d %H:%M:%S")); -} + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Time is synced with NTP Server " + getServerName() + ": " + getCurrentTimeString("%Y-%m-%d %H:%M:%S")); +} bool time_manual_reset_sync(void) { sntp_restart(); -// sntp_init(); + // sntp_init(); int retry = 0; const int retry_count = 10; - while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) { + + while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) + { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Waiting for system time to be set... " + std::to_string(retry) + "/" + std::to_string(retry_count)); vTaskDelay(2000 / portTICK_PERIOD_MS); } + if (retry >= retry_count) + { return false; + } LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Waiting for system time successfull with " + std::to_string(retry) + "/" + std::to_string(retry_count)); return true; } - int getUTCOffsetSeconds(std::string &zeitzone) { int offset = 0; @@ -102,7 +103,7 @@ int getUTCOffsetSeconds(std::string &zeitzone) time_t now; struct tm timeinfo; - time (&now); + time(&now); localtime_r(&now, &timeinfo); char buffer[80]; strftime(buffer, 80, "%z", &timeinfo); @@ -111,68 +112,70 @@ int getUTCOffsetSeconds(std::string &zeitzone) if (zeitzone.length() == 5) { if (zeitzone[0] == '-') - vorzeichen = -1; + { + vorzeichen = -1; + } stunden = stoi(zeitzone.substr(1, 2)); minuten = stoi(zeitzone.substr(3, 2)); offset = ((stunden * 60) + minuten) * 60; } + return offset; } - void setTimeZone(std::string _tzstring) { setenv("TZ", _tzstring.c_str(), 1); - tzset(); + tzset(); _tzstring = "Time zone set to " + _tzstring; LogFile.WriteToFile(ESP_LOG_INFO, TAG, _tzstring); std::string zeitzone; LocalTimeToUTCOffsetSeconds = getUTCOffsetSeconds(zeitzone); -// std::string zw = std::to_string(LocalTimeToUTCOffsetSeconds); + // std::string zw = std::to_string(LocalTimeToUTCOffsetSeconds); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "time zone: " + zeitzone + " Delta to UTC: " + std::to_string(LocalTimeToUTCOffsetSeconds) + " seconds"); } - - -std::string getNtpStatusText(sntp_sync_status_t status) { - if (status == SNTP_SYNC_STATUS_COMPLETED) { +std::string getNtpStatusText(sntp_sync_status_t status) +{ + if (status == SNTP_SYNC_STATUS_COMPLETED) + { return "Synchronized"; } - else if (status == SNTP_SYNC_STATUS_IN_PROGRESS) { + else if (status == SNTP_SYNC_STATUS_IN_PROGRESS) + { return "In Progress"; } - else { // SNTP_SYNC_STATUS_RESET + else + { + // SNTP_SYNC_STATUS_RESET return "Reset"; } } - -bool getTimeIsSet(void) { +bool getTimeIsSet(void) +{ time_t now; struct tm timeinfo; time(&now); localtime_r(&now, &timeinfo); // Is time set? If not, tm_year will be (1970 - 1900). - if ((timeinfo.tm_year < (2022 - 1900))) { + if ((timeinfo.tm_year < (2022 - 1900))) + { return false; } - else { + else + { return true; } } -/*void restartNtpClient(void) { -// sntp_restart(); - // obtain_time(); -}*/ - - -bool getUseNtp(void) { +bool getUseNtp(void) +{ return useNtp; } @@ -181,100 +184,108 @@ bool getTimeWasNotSetAtBoot(void) return timeWasNotSetAtBoot; } - -std::string getServerName(void) { +std::string getServerName(void) +{ char buf[100]; - if (sntp_getservername(0)){ - snprintf(buf, sizeof(buf), "%s", sntp_getservername(0)); + if (esp_sntp_getservername(0)) + { + snprintf(buf, sizeof(buf), "%s", esp_sntp_getservername(0)); return std::string(buf); } - else { // we have either IPv4 or IPv6 address - ip_addr_t const *ip = sntp_getserver(0); - if (ipaddr_ntoa_r(ip, buf, sizeof(buf)) != NULL) { + else + { + // we have either IPv4 or IPv6 address + ip_addr_t const *ip = esp_sntp_getserver(0); + if (ipaddr_ntoa_r(ip, buf, sizeof(buf)) != NULL) + { return std::string(buf); } } + return ""; } - /** * Load the TimeZone and TimeServer from the config file and initialize the NTP client */ -bool setupTime() { - time_t now; - struct tm timeinfo; - char strftime_buf[64]; - - ConfigFile configFile = ConfigFile(CONFIG_FILE); - - if (!configFile.ConfigFileExists()){ +bool setupTime() +{ + FILE *pFile = fopen(CONFIG_FILE, "r"); + if (pFile == NULL) + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "No ConfigFile defined - exit setupTime()!"); return false; } + ClassFlow classFlow; + std::string aktparamgraph = ""; + while (classFlow.GetNextParagraph(pFile, aktparamgraph)) + { + if ((to_upper(aktparamgraph).compare("[SYSTEM]") == 0) || (to_upper(aktparamgraph).compare(";[SYSTEM]") == 0)) + { + break; + } + } + + if ((to_upper(aktparamgraph).compare("[SYSTEM]") != 0) && (to_upper(aktparamgraph).compare(";[SYSTEM]") != 0)) + { + fclose(pFile); + return false; + } + + time_t now; + struct tm timeinfo; + char strftime_buf[64]; + std::vector splitted; - std::string line = ""; - bool disabledLine = false; - bool eof = false; - /* Load config from config file */ - while ((!configFile.GetNextParagraph(line, disabledLine, eof) || - (line.compare("[System]") != 0)) && !eof) {} - if (eof) { - return false; - } + while (classFlow.getNextLine(pFile, &aktparamgraph) && !classFlow.isNewParagraph(aktparamgraph)) + { + splitted = split_line(aktparamgraph); - if (disabledLine) { - return false; - } + if (splitted.size() > 1) + { + std::string _param = to_upper(splitted[0]); - while (configFile.getNextLine(&line, disabledLine, eof) && - !configFile.isNewParagraph(line)) { - splitted = ZerlegeZeile(line, "="); - - if (toUpper(splitted[0]) == "TIMEZONE") { - if (splitted.size() <= 1) { // parameter part is empty - timeZone = ""; - } - else { + if (_param == "TIMEZONE") + { timeZone = splitted[1]; } - } - if (toUpper(splitted[0]) == "TIMESERVER") { - if (splitted.size() <= 1) { // Key has no value => we use this to show it as disabled - timeServer = ""; - } - else { + else if (_param == "TIMESERVER") + { timeServer = splitted[1]; } } } - + fclose(pFile); /* Setup NTP Server and Timezone */ - if (timeServer == "undefined") { + if (timeServer == "undefined") + { timeServer = "pool.ntp.org"; LogFile.WriteToFile(ESP_LOG_INFO, TAG, "TimeServer not defined, using default: " + timeServer); } - else if (timeServer == "") { + else if (timeServer == "") + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "TimeServer config empty, disabling NTP"); useNtp = false; } - else { + else + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "TimeServer: " + timeServer); } - - if (timeZone == "") { + + if (timeZone == "") + { timeZone = "CET-1CEST,M3.5.0,M10.5.0/3"; LogFile.WriteToFile(ESP_LOG_INFO, TAG, "TimeZone not set, using default: " + timeZone); } - - if (useNtp) { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Configuring NTP Client..."); + if (useNtp) + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Configuring NTP Client..."); esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(timeServer.c_str()); config.sync_cb = time_sync_notification_cb; esp_netif_sntp_init(&config); @@ -282,7 +293,7 @@ bool setupTime() { setTimeZone(timeZone); } - /* The RTC keeps the time after a restart (Except on Power On or Pin Reset) + /* The RTC keeps the time after a restart (Except on Power On or Pin Reset) * There should only be a minor correction through NTP */ // Get current time from RTC @@ -290,13 +301,15 @@ bool setupTime() { localtime_r(&now, &timeinfo); strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%d %H:%M:%S", &timeinfo); - - if (getTimeIsSet()) { + if (getTimeIsSet()) + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Time is already set: " + std::string(strftime_buf)); } - else { + else + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "The local time is unknown, starting with " + std::string(strftime_buf)); - if (useNtp) { + if (useNtp) + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Once the NTP server provides a time, we will switch to that one"); timeWasNotSetAtBoot = true; timeWasNotSetAtBoot_PrintStartBlock = true; @@ -305,5 +318,3 @@ bool setupTime() { return true; } - - diff --git a/code/components/jomjol_time_sntp/time_sntp.h b/code/components/jomjol_time_sntp/time_sntp.h index 9b5d8796..1d703897 100644 --- a/code/components/jomjol_time_sntp/time_sntp.h +++ b/code/components/jomjol_time_sntp/time_sntp.h @@ -1,24 +1,24 @@ #pragma once -#ifndef TIMESNTP_H -#define TIMESNTP_H - +#ifndef TIME_SNTP_H +#define TIME_SNTP_H #include #include #include + #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" + #include "esp_system.h" #include "esp_log.h" #include "esp_attr.h" #include "esp_sleep.h" #include "esp_sntp.h" -std::string getCurrentTimeString(const char * frm); -std::string ConvertTimeToString(time_t _time, const char * frm); - +std::string getCurrentTimeString(const char *frm); +std::string ConvertTimeToString(time_t _time, const char *frm); bool getTimeIsSet(void); bool getTimeWasNotSetAtBoot(void); @@ -30,5 +30,4 @@ bool time_manual_reset_sync(void); extern int LocalTimeToUTCOffsetSeconds; - -#endif //TIMESNTP_H \ No newline at end of file +#endif // TIME_SNTP_H diff --git a/code/components/jomjol_webhook/CMakeLists.txt b/code/components/jomjol_webhook/CMakeLists.txt index 10271c11..b726c765 100644 --- a/code/components/jomjol_webhook/CMakeLists.txt +++ b/code/components/jomjol_webhook/CMakeLists.txt @@ -1,7 +1,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." - REQUIRES esp_http_client jomjol_logfile jomjol_flowcontroll json) + INCLUDE_DIRS "." "../../include" + REQUIRES esp_http_client jomjol_logfile jomjol_flowcontroll json jomjol_helper) diff --git a/code/components/jomjol_webhook/interface_webhook.cpp b/code/components/jomjol_webhook/interface_webhook.cpp index 54d4c48e..4d801bc1 100644 --- a/code/components/jomjol_webhook/interface_webhook.cpp +++ b/code/components/jomjol_webhook/interface_webhook.cpp @@ -1,4 +1,5 @@ -#ifdef ENABLE_WEBHOOK +#include "defines.h" + #include "interface_webhook.h" #include "esp_log.h" @@ -6,11 +7,9 @@ #include "ClassLogFile.h" #include "esp_http_client.h" #include "time_sntp.h" -#include "../../include/defines.h" #include #include - static const char *TAG = "WEBHOOK"; std::string _webhookURI; @@ -26,7 +25,7 @@ void WebhookInit(std::string _uri, std::string _apiKey) _lastTimestamp = 0L; } -bool WebhookPublish(std::vector* numbers) +bool WebhookPublish(std::vector *numbers) { bool numbersWithError = false; cJSON *jsonArray = cJSON_CreateArray(); @@ -36,7 +35,7 @@ bool WebhookPublish(std::vector* numbers) string timezw = ""; char buffer[80]; time_t &lastPreValue = (*numbers)[i]->timeStampLastPreValue; - struct tm* timeinfo = localtime(&lastPreValue); + struct tm *timeinfo = localtime(&lastPreValue); _lastTimestamp = static_cast(lastPreValue); strftime(buffer, 80, PREVALUE_TIME_FORMAT_OUTPUT, timeinfo); timezw = std::string(buffer); @@ -51,10 +50,11 @@ bool WebhookPublish(std::vector* numbers) cJSON_AddStringToObject(json, "rate", (*numbers)[i]->ReturnRateValue.c_str()); cJSON_AddStringToObject(json, "changeAbsolute", (*numbers)[i]->ReturnChangeAbsolute.c_str()); cJSON_AddStringToObject(json, "error", (*numbers)[i]->ErrorMessageText.c_str()); - + cJSON_AddItemToArray(jsonArray, json); - if ((*numbers)[i]->ErrorMessage) { + if ((*numbers)[i]->ErrorMessage) + { numbersWithError = true; } } @@ -71,8 +71,7 @@ bool WebhookPublish(std::vector* numbers) .method = HTTP_METHOD_POST, .event_handler = http_event_handler, .buffer_size = MAX_HTTP_OUTPUT_BUFFER, - .user_data = response_buffer - }; + .user_data = response_buffer}; esp_http_client_handle_t http_client = esp_http_client_init(&http_config); @@ -83,21 +82,25 @@ bool WebhookPublish(std::vector* numbers) esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(http_client)); - if(err == ESP_OK) { + if (err == ESP_OK) + { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP request was performed"); int status_code = esp_http_client_get_status_code(http_client); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP status code: " + std::to_string(status_code)); - } else { + } + else + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "HTTP request failed"); - } + } esp_http_client_cleanup(http_client); cJSON_Delete(jsonArray); free(jsonString); - return numbersWithError; + return numbersWithError; } -void WebhookUploadPic(ImageData *Img) { +void WebhookUploadPic(ImageData *Img) +{ LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Starting WebhookUploadPic"); std::string fullURI = _webhookURI + "?timestamp=" + std::to_string(_lastTimestamp); @@ -108,8 +111,7 @@ void WebhookUploadPic(ImageData *Img) { .method = HTTP_METHOD_PUT, .event_handler = http_event_handler, .buffer_size = MAX_HTTP_OUTPUT_BUFFER, - .user_data = response_buffer - }; + .user_data = response_buffer}; esp_http_client_handle_t http_client = esp_http_client_init(&http_config); @@ -120,11 +122,14 @@ void WebhookUploadPic(ImageData *Img) { err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(http_client)); - if (err == ESP_OK) { + if (err == ESP_OK) + { LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP PUT request was performed successfully"); int status_code = esp_http_client_get_status_code(http_client); LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP status code: " + std::to_string(status_code)); - } else { + } + else + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "HTTP PUT request failed"); } @@ -133,38 +138,35 @@ void WebhookUploadPic(ImageData *Img) { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "WebhookUploadPic finished"); } - static esp_err_t http_event_handler(esp_http_client_event_t *evt) { - switch(evt->event_id) + switch (evt->event_id) { - case HTTP_EVENT_ERROR: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered"); - break; - case HTTP_EVENT_ON_CONNECTED: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client connected"); - ESP_LOGI(TAG, "HTTP Client Connected"); - break; - case HTTP_EVENT_HEADERS_SENT: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client sent all request headers"); - break; - case HTTP_EVENT_ON_HEADER: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Header: key=" + std::string(evt->header_key) + ", value=" + std::string(evt->header_value)); - break; - case HTTP_EVENT_ON_DATA: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client data recevied: len=" + std::to_string(evt->data_len)); - break; - case HTTP_EVENT_ON_FINISH: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client finished"); - break; - case HTTP_EVENT_DISCONNECTED: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Disconnected"); - break; - case HTTP_EVENT_REDIRECT: - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Redirect"); - break; + case HTTP_EVENT_ERROR: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered"); + break; + case HTTP_EVENT_ON_CONNECTED: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client connected"); + ESP_LOGI(TAG, "HTTP Client Connected"); + break; + case HTTP_EVENT_HEADERS_SENT: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client sent all request headers"); + break; + case HTTP_EVENT_ON_HEADER: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Header: key=" + std::string(evt->header_key) + ", value=" + std::string(evt->header_value)); + break; + case HTTP_EVENT_ON_DATA: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client data recevied: len=" + std::to_string(evt->data_len)); + break; + case HTTP_EVENT_ON_FINISH: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client finished"); + break; + case HTTP_EVENT_DISCONNECTED: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Disconnected"); + break; + case HTTP_EVENT_REDIRECT: + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Redirect"); + break; } return ESP_OK; } - -#endif //ENABLE_WEBHOOK diff --git a/code/components/jomjol_webhook/interface_webhook.h b/code/components/jomjol_webhook/interface_webhook.h index d7033be0..07fe8d7f 100644 --- a/code/components/jomjol_webhook/interface_webhook.h +++ b/code/components/jomjol_webhook/interface_webhook.h @@ -1,6 +1,5 @@ -#ifdef ENABLE_WEBHOOK - #pragma once + #ifndef INTERFACE_WEBHOOK_H #define INTERFACE_WEBHOOK_H @@ -10,8 +9,7 @@ #include void WebhookInit(std::string _webhookURI, std::string _apiKey); -bool WebhookPublish(std::vector* numbers); +bool WebhookPublish(std::vector *numbers); void WebhookUploadPic(ImageData *Img); -#endif //INTERFACE_WEBHOOK_H -#endif //ENABLE_WEBHOOK \ No newline at end of file +#endif // INTERFACE_WEBHOOK_H diff --git a/code/components/jomjol_wlan/CMakeLists.txt b/code/components/jomjol_wlan/CMakeLists.txt deleted file mode 100644 index 53953cb5..00000000 --- a/code/components/jomjol_wlan/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) - -idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." - REQUIRES esp_wifi nvs_flash jomjol_helper jomjol_mqtt wpa_supplicant) - - diff --git a/code/components/jomjol_wlan/connect_wlan.cpp b/code/components/jomjol_wlan/connect_wlan.cpp deleted file mode 100644 index 106f81b8..00000000 --- a/code/components/jomjol_wlan/connect_wlan.cpp +++ /dev/null @@ -1,720 +0,0 @@ -#include "connect_wlan.h" - -#include -#include -#include -#include -#include -#include - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" - -#include "driver/gpio.h" -#include "esp_system.h" -#include "esp_wifi.h" -#include "esp_wnm.h" -#include "esp_rrm.h" -#include "esp_mbo.h" -#include "esp_mac.h" -#include "esp_netif.h" -#include -#include "esp_log.h" -#include "nvs_flash.h" - -#include "lwip/err.h" -#include "lwip/sys.h" -#ifdef ENABLE_MQTT - #include "interface_mqtt.h" -#endif //ENABLE_MQTT - -#include "ClassLogFile.h" -#include "read_wlanini.h" -#include "Helper.h" -#include "statusled.h" - -#include "../../include/defines.h" - -#if (ESP_IDF_VERSION_MAJOR >= 5) -#include "soc/periph_defs.h" -#include "esp_private/periph_ctrl.h" -#include "soc/gpio_sig_map.h" -#include "soc/gpio_periph.h" -#include "soc/io_mux_reg.h" -#include "esp_rom_gpio.h" -#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio -#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c) -#define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d) -#define ets_delay_us(a) esp_rom_delay_us(a) -#endif - -#include "../esp-protocols/components/mdns/include/mdns.h" - - -static const char *TAG = "WIFI"; - -static bool APWithBetterRSSI = false; -static bool WIFIConnected = false; -static int WIFIReconnectCnt = 0; - -esp_netif_t *my_sta; - - -void strinttoip4(const char *ip, int &a, int &b, int &c, int &d) { - std::string zw = std::string(ip); - std::stringstream s(zw); - char ch; //to temporarily store the '.' - s >> a >> ch >> b >> ch >> c >> ch >> d; -} - - -std::string BssidToString(const char* c) { - char cBssid[25]; - sprintf(cBssid, "%02x:%02x:%02x:%02x:%02x:%02x", c[0], c[1], c[2], c[3], c[4], c[5]); - return std::string(cBssid); -} - - -#ifdef WLAN_USE_MESH_ROAMING -/* rrm ctx */ -int rrm_ctx = 0; - -static inline uint32_t WPA_GET_LE32(const uint8_t *a) -{ - return ((uint32_t) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]; -} - - -#ifndef WLAN_EID_MEASURE_REPORT -#define WLAN_EID_MEASURE_REPORT 39 -#endif -#ifndef MEASURE_TYPE_LCI -#define MEASURE_TYPE_LCI 9 -#endif -#ifndef MEASURE_TYPE_LOCATION_CIVIC -#define MEASURE_TYPE_LOCATION_CIVIC 11 -#endif -#ifndef WLAN_EID_NEIGHBOR_REPORT -#define WLAN_EID_NEIGHBOR_REPORT 52 -#endif -#ifndef ETH_ALEN -#define ETH_ALEN 6 -#endif - - -#define MAX_NEIGHBOR_LEN 512 -static char * get_btm_neighbor_list(uint8_t *report, size_t report_len) -{ - size_t len = 0; - const uint8_t *data; - int ret = 0; - - /* - * Neighbor Report element (IEEE P802.11-REVmc/D5.0) - * BSSID[6] - * BSSID Information[4] - * Operating Class[1] - * Channel Number[1] - * PHY Type[1] - * Optional Subelements[variable] - */ - #define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1) - - if (!report || report_len == 0) { - ESP_LOGD(TAG, "Roaming: RRM neighbor report is not valid"); - return NULL; - } - - char *buf = (char*) calloc(1, MAX_NEIGHBOR_LEN); - data = report; - - while (report_len >= 2 + NR_IE_MIN_LEN) { - const uint8_t *nr; - char lci[256 * 2 + 1]; - char civic[256 * 2 + 1]; - uint8_t nr_len = data[1]; - const uint8_t *pos = data, *end; - - if (pos[0] != WLAN_EID_NEIGHBOR_REPORT || - nr_len < NR_IE_MIN_LEN) { - ESP_LOGD(TAG, "Roaming CTRL: Invalid Neighbor Report element: id=%u len=%u", - data[0], nr_len); - ret = -1; - goto cleanup; - } - - if (2U + nr_len > report_len) { - ESP_LOGD(TAG, "Roaming CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u", - data[0], report_len, nr_len); - ret = -1; - goto cleanup; - } - pos += 2; - end = pos + nr_len; - - nr = pos; - pos += NR_IE_MIN_LEN; - - lci[0] = '\0'; - civic[0] = '\0'; - while (end - pos > 2) { - uint8_t s_id, s_len; - - s_id = *pos++; - s_len = *pos++; - if (s_len > end - pos) { - ret = -1; - goto cleanup; - } - if (s_id == WLAN_EID_MEASURE_REPORT && s_len > 3) { - /* Measurement Token[1] */ - /* Measurement Report Mode[1] */ - /* Measurement Type[1] */ - /* Measurement Report[variable] */ - switch (pos[2]) { - case MEASURE_TYPE_LCI: - if (lci[0]) - break; - memcpy(lci, pos, s_len); - break; - case MEASURE_TYPE_LOCATION_CIVIC: - if (civic[0]) - break; - memcpy(civic, pos, s_len); - break; - } - } - - pos += s_len; - } - - ESP_LOGI(TAG, "Roaming: RMM neighbor report bssid=" MACSTR - " info=0x%x op_class=%u chan=%u phy_type=%u%s%s%s%s", - MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN), - nr[ETH_ALEN + 4], nr[ETH_ALEN + 5], - nr[ETH_ALEN + 6], - lci[0] ? " lci=" : "", lci, - civic[0] ? " civic=" : "", civic); - - - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: RMM neighbor report BSSID: " + BssidToString((char*)nr) + - ", Channel: " + std::to_string(nr[ETH_ALEN + 5])); - - /* neighbor start */ - len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, " neighbor="); - /* bssid */ - len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, MACSTR, MAC2STR(nr)); - /* , */ - len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); - /* bssid info */ - len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "0x%04x", WPA_GET_LE32(nr + ETH_ALEN)); - len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); - /* operating class */ - len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 4]); - len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); - /* channel number */ - len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 5]); - len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, ","); - /* phy type */ - len += snprintf(buf + len, MAX_NEIGHBOR_LEN - len, "%u", nr[ETH_ALEN + 6]); - /* optional elements, skip */ - - data = end; - report_len -= 2 + nr_len; - } - -cleanup: - if (ret < 0) { - free(buf); - buf = NULL; - } - return buf; -} - - -void neighbor_report_recv_cb(void *ctx, const uint8_t *report, size_t report_len) -{ - int *val = (int*) ctx; - uint8_t *pos = (uint8_t *)report; - int cand_list = 0; - int ret; - - if (!report) { - ESP_LOGD(TAG, "Roaming: Neighbor report is null"); - return; - } - if (*val != rrm_ctx) { - ESP_LOGE(TAG, "Roaming: rrm_ctx value didn't match, not initiated by us"); - return; - } - /* dump report info */ - ESP_LOGD(TAG, "Roaming: RRM neighbor report len=%d", report_len); - ESP_LOG_BUFFER_HEXDUMP(TAG, pos, report_len, ESP_LOG_DEBUG); - - /* create neighbor list */ - char *neighbor_list = get_btm_neighbor_list(pos + 1, report_len - 1); - - /* In case neighbor list is not present issue a scan and get the list from that */ - if (!neighbor_list) { - /* issue scan */ - wifi_scan_config_t params; - memset(¶ms, 0, sizeof(wifi_scan_config_t)); - if (esp_wifi_scan_start(¶ms, true) < 0) { - goto cleanup; - } - /* cleanup from net802.11 */ - uint16_t number = 1; - wifi_ap_record_t ap_records; - esp_wifi_scan_get_ap_records(&number, &ap_records); - cand_list = 1; - } - /* send AP btm query requesting to roam depending on candidate list of AP */ - // btm_query_reasons: https://github.com/espressif/esp-idf/blob/release/v4.4/components/wpa_supplicant/esp_supplicant/include/esp_wnm.h - ret = esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, neighbor_list, cand_list); // query reason 16 -> LOW RSSI --> (btm_query_reason)16 - ESP_LOGD(TAG, "neighbor_report_recv_cb retval - bss_transisition_query: %d", ret); - -cleanup: - if (neighbor_list) - free(neighbor_list); -} - - -static void esp_bss_rssi_low_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - int retval = -1; - wifi_event_bss_rssi_low_t *event = (wifi_event_bss_rssi_low_t*) event_data; - - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming Event: RSSI " + std::to_string(event->rssi) + - " < RSSI_Threshold " + std::to_string(wlan_config.rssi_threshold)); - - /* If RRM is supported, call RRM and then send BTM query to AP */ - if (esp_rrm_is_rrm_supported_connection() && esp_wnm_is_btm_supported_connection()) - { - /* Lets check channel conditions */ - rrm_ctx++; - - retval = esp_rrm_send_neighbor_rep_request(neighbor_report_recv_cb, &rrm_ctx); - ESP_LOGD(TAG, "esp_rrm_send_neighbor_rep_request retval: %d", retval); - if (retval == 0) - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: RRM + BTM query sent"); - else - ESP_LOGD(TAG, "esp_rrm_send_neighbor_rep_request retval: %d", retval); - } - - /* If RRM is not supported or RRM request failed, send directly BTM query to AP */ - if (retval < 0 && esp_wnm_is_btm_supported_connection()) - { - // btm_query_reasons: https://github.com/espressif/esp-idf/blob/release/v4.4/components/wpa_supplicant/esp_supplicant/include/esp_wnm.h - retval = esp_wnm_send_bss_transition_mgmt_query(REASON_FRAME_LOSS, NULL, 0); // query reason 16 -> LOW RSSI --> (btm_query_reason)16 - ESP_LOGD(TAG, "esp_wnm_send_bss_transition_mgmt_query retval: %d", retval); - if (retval == 0) - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: BTM query sent"); - else - ESP_LOGD(TAG, "esp_wnm_send_bss_transition_mgmt_query retval: %d", retval); - } -} - - -void printRoamingFeatureSupport(void) -{ - if (esp_rrm_is_rrm_supported_connection()) - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Roaming: RRM (802.11k) supported by AP"); - else - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Roaming: RRM (802.11k) NOT supported by AP"); - - if (esp_wnm_is_btm_supported_connection()) - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Roaming: BTM (802.11v) supported by AP"); - else - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Roaming: BTM (802.11v) NOT supported by AP"); -} - - -#ifdef WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES -void wifiRoamingQuery(void) -{ - /* Query only if WIFI is connected and feature is supported by AP */ - if (WIFIConnected && (esp_rrm_is_rrm_supported_connection() || esp_wnm_is_btm_supported_connection())) { - /* Client is allowed to send query to AP for roaming request if RSSI is lower than threshold */ - /* Note 1: Set RSSI threshold funtion needs to be called to trigger WIFI_EVENT_STA_BSS_RSSI_LOW */ - /* Note 2: Additional querys will be sent after flow round is finshed --> server_tflite.cpp - function "task_autodoFlow" */ - /* Note 3: RSSI_Threshold = 0 --> Disable client query by application (WebUI parameter) */ - - if (wlan_config.rssi_threshold != 0 && get_WIFI_RSSI() != -127 && (get_WIFI_RSSI() < wlan_config.rssi_threshold)) - esp_wifi_set_rssi_threshold(wlan_config.rssi_threshold); - } -} -#endif // WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES -#endif // WLAN_USE_MESH_ROAMING - - -#ifdef WLAN_USE_ROAMING_BY_SCANNING -std::string getAuthModeName(const wifi_auth_mode_t auth_mode) -{ - std::string AuthModeNames[] = {"OPEN", "WEP", "WPA PSK", "WPA2 PSK", "WPA WPA2 PSK", "WPA2 ENTERPRISE", - "WPA3 PSK", "WPA2 WPA3 PSK", "WAPI_PSK", "MAX"}; - return AuthModeNames[auth_mode]; -} - - -void wifi_scan(void) -{ - wifi_scan_config_t wifi_scan_config; - memset(&wifi_scan_config, 0, sizeof(wifi_scan_config)); - - wifi_scan_config.ssid = (uint8_t*)wlan_config.ssid.c_str(); // only scan for configured SSID - wifi_scan_config.show_hidden = true; // scan also hidden SSIDs - wifi_scan_config.channel = 0; // scan all channels - - esp_wifi_scan_start(&wifi_scan_config, true); // not using event handler SCAN_DONE by purpose to keep SYS_EVENT heap smaller - // and the calling task task_autodoFlow is after scan is finish in wait state anyway - // Scan duration: ca. (120ms + 30ms) * Number of channels -> ca. 1,5 - 2s - - uint16_t max_number_of_ap_found = 10; // max. number of APs, value will be updated by function "esp_wifi_scan_get_ap_num" - esp_wifi_scan_get_ap_num(&max_number_of_ap_found); // get actual found APs - wifi_ap_record_t* wifi_ap_records = new wifi_ap_record_t[max_number_of_ap_found]; // Allocate necessary record datasets - if (wifi_ap_records == NULL) { - esp_wifi_scan_get_ap_records(0, NULL); // free internal heap - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "wifi_scan: Failed to allocate heap for wifi_ap_records"); - return; - } - else { - if (esp_wifi_scan_get_ap_records(&max_number_of_ap_found, wifi_ap_records) != ESP_OK) { // Retrieve results (and free internal heap) - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "wifi_scan: esp_wifi_scan_get_ap_records: Error retrieving datasets"); - delete[] wifi_ap_records; - return; - } - } - - wifi_ap_record_t currentAP; - esp_wifi_sta_get_ap_info(¤tAP); - - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: Current AP BSSID=" + BssidToString((char*)currentAP.bssid)); - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: Scan completed, APs found with configured SSID: " + std::to_string(max_number_of_ap_found)); - for (int i = 0; i < max_number_of_ap_found; i++) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: " + std::to_string(i+1) + - ": SSID=" + std::string((char*)wifi_ap_records[i].ssid) + - ", BSSID=" + BssidToString((char*)wifi_ap_records[i].bssid) + - ", RSSI=" + std::to_string(wifi_ap_records[i].rssi) + - ", CH=" + std::to_string(wifi_ap_records[i].primary) + - ", AUTH=" + getAuthModeName(wifi_ap_records[i].authmode)); - if (wifi_ap_records[i].rssi > (currentAP.rssi + 5) && // RSSI is better than actual RSSI + 5 --> Avoid switching to AP with roughly same RSSI - (strcmp(BssidToString((char*)wifi_ap_records[i].bssid).c_str(), BssidToString((char*)currentAP.bssid).c_str()) != 0)) - { - APWithBetterRSSI = true; - } - } - delete[] wifi_ap_records; -} - - -void wifiRoamByScanning(void) -{ - if (wlan_config.rssi_threshold != 0 && get_WIFI_RSSI() != -127 && (get_WIFI_RSSI() < wlan_config.rssi_threshold)) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: Start scan of all channels for SSID " + wlan_config.ssid); - wifi_scan(); - - if (APWithBetterRSSI) { - APWithBetterRSSI = false; - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Roaming: AP with better RSSI in range, disconnecting to switch AP..."); - esp_wifi_disconnect(); - } - else { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Roaming: Scan completed, stay on current AP"); - } - } -} -#endif // WLAN_USE_ROAMING_BY_SCANNING - - -std::string* getIPAddress() -{ - return &wlan_config.ipaddress; -} - - -std::string* getSSID() -{ - return &wlan_config.ssid; -} - - -static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) - { - WIFIConnected = false; - esp_wifi_connect(); - } - else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) - { - /* Disconnect reason: https://github.com/espressif/esp-idf/blob/d825753387c1a64463779bbd2369e177e5d59a79/components/esp_wifi/include/esp_wifi_types.h */ - wifi_event_sta_disconnected_t *disconn = (wifi_event_sta_disconnected_t *)event_data; - if (disconn->reason == WIFI_REASON_ROAMING) { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ", Roaming 802.11kv)"); - // --> no reconnect neccessary, it should automatically reconnect to new AP - } - else { - WIFIConnected = false; - if (disconn->reason == WIFI_REASON_NO_AP_FOUND) { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ", No AP)"); - StatusLED(WLAN_CONN, 1, false); - } - else if (disconn->reason == WIFI_REASON_AUTH_EXPIRE || - disconn->reason == WIFI_REASON_AUTH_FAIL || - disconn->reason == WIFI_REASON_NOT_AUTHED || - disconn->reason == WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT || - disconn->reason == WIFI_REASON_HANDSHAKE_TIMEOUT) { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ", Auth fail)"); - StatusLED(WLAN_CONN, 2, false); - } - else if (disconn->reason == WIFI_REASON_BEACON_TIMEOUT) { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ", Timeout)"); - StatusLED(WLAN_CONN, 3, false); - } - else { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected (" + std::to_string(disconn->reason) + ")"); - StatusLED(WLAN_CONN, 4, false); - } - WIFIReconnectCnt++; - esp_wifi_connect(); // Try to connect again - } - - if (WIFIReconnectCnt >= 10) { - WIFIReconnectCnt = 0; - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Disconnected, multiple reconnect attempts failed (" + - std::to_string(disconn->reason) + "), retrying after 5s"); - vTaskDelay(5000 / portTICK_PERIOD_MS); // Delay between the reconnections - } - } - else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) - { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Connected to: " + wlan_config.ssid + ", RSSI: " + - std::to_string(get_WIFI_RSSI())); - - #ifdef WLAN_USE_MESH_ROAMING - printRoamingFeatureSupport(); - - #ifdef WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES - // wifiRoamingQuery(); // Avoid client triggered query during processing flow (reduce risk of heap shortage). Request will be triggered at the end of every round anyway - #endif //WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES - - #endif //WLAN_USE_MESH_ROAMING - } - else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) - { - WIFIConnected = true; - WIFIReconnectCnt = 0; - - ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; - wlan_config.ipaddress = std::string(ip4addr_ntoa((const ip4_addr*) &event->ip_info.ip)); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Assigned IP: " + wlan_config.ipaddress); - - #ifdef ENABLE_MQTT - if (getMQTTisEnabled()) { - vTaskDelay(5000 / portTICK_PERIOD_MS); - MQTT_Init(); // Init when WIFI is getting connected - } - #endif //ENABLE_MQTT - } -} - - -esp_err_t wifi_init_sta(void) -{ - esp_err_t retval = esp_netif_init(); - if (retval != ESP_OK) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_netif_init: Error: " + std::to_string(retval)); - return retval; - } - - retval = esp_event_loop_create_default(); - if (retval != ESP_OK) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_loop_create_default: Error: " + std::to_string(retval)); - return retval; - } - - my_sta = esp_netif_create_default_wifi_sta(); - - if (!wlan_config.ipaddress.empty() && !wlan_config.gateway.empty() && !wlan_config.netmask.empty()) - { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Manual interface config -> IP: " + wlan_config.ipaddress + ", Gateway: " + - std::string(wlan_config.gateway) + ", Netmask: " + std::string(wlan_config.netmask)); - esp_netif_dhcpc_stop(my_sta); // Stop DHCP service - - esp_netif_ip_info_t ip_info; - int a, b, c, d; - strinttoip4(wlan_config.ipaddress.c_str(), a, b, c, d); - IP4_ADDR(&ip_info.ip, a, b, c, d); // Set static IP address - - strinttoip4(wlan_config.gateway.c_str(), a, b, c, d); - IP4_ADDR(&ip_info.gw, a, b, c, d); // Set gateway - - strinttoip4(wlan_config.netmask.c_str(), a, b, c, d); - IP4_ADDR(&ip_info.netmask, a, b, c, d); // Set netmask - - esp_netif_set_ip_info(my_sta, &ip_info); // Set static IP configuration - } - else { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Automatic interface config --> Use DHCP service"); - } - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - retval = esp_wifi_init(&cfg); - if (retval != ESP_OK) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_init: Error: " + std::to_string(retval)); - return retval; - } - - if (!wlan_config.ipaddress.empty() && !wlan_config.gateway.empty() && !wlan_config.netmask.empty()) - { - if (wlan_config.dns.empty()) { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "No DNS server, use gateway"); - wlan_config.dns = wlan_config.gateway; - } - else { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Manual interface config -> DNS: " + wlan_config.dns); - } - - esp_netif_dns_info_t dns_info; - ip4_addr_t ip; - ip.addr = esp_ip4addr_aton(wlan_config.dns.c_str()); - ip_addr_set_ip4_u32(&dns_info.ip, ip.addr); - - retval = esp_netif_set_dns_info(my_sta, ESP_NETIF_DNS_MAIN, &dns_info); - if (retval != ESP_OK) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_netif_set_dns_info: Error: " + std::to_string(retval)); - return retval; - } - } - - retval = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, - &event_handler, NULL, NULL); - if (retval != ESP_OK) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_handler_instance_register - WIFI_ANY: Error: " + std::to_string(retval)); - return retval; - } - - retval = esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, - &event_handler, NULL, NULL); - if (retval != ESP_OK) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_handler_instance_register - GOT_IP: Error: " + std::to_string(retval)); - return retval; - } - - #ifdef WLAN_USE_MESH_ROAMING - retval = esp_event_handler_instance_register(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, - &esp_bss_rssi_low_handler, NULL, NULL); - if (retval != ESP_OK) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_handler_instance_register - BSS_RSSI_LOW: Error: " + std::to_string(retval)); - return retval; - } - #endif - - wifi_config_t wifi_config = { }; - - wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; // Scan all channels instead of stopping after first match - wifi_config.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL; // Sort by signal strength and keep up to 4 best APs - //wifi_config.sta.failure_retry_cnt = 3; // IDF version 5.0 will support this - - #ifdef WLAN_USE_MESH_ROAMING - wifi_config.sta.rm_enabled = 1; // 802.11k (Radio Resource Management) - wifi_config.sta.btm_enabled = 1; // 802.11v (BSS Transition Management) - //wifi_config.sta.mbo_enabled = 1; // Multiband Operation (better use of Wi-Fi network resources in roaming decisions) -> not activated to save heap - wifi_config.sta.pmf_cfg.capable = 1; // 802.11w (Protected Management Frame, activated by default if other device also advertizes PMF capability) - //wifi_config.sta.ft_enabled = 1; // 802.11r (BSS Fast Transition) -> Upcoming IDF version 5.0 will support 11r - #endif - - strcpy((char*)wifi_config.sta.ssid, (const char*)wlan_config.ssid.c_str()); - strcpy((char*)wifi_config.sta.password, (const char*)wlan_config.password.c_str()); - - retval = esp_wifi_set_mode(WIFI_MODE_STA); - if (retval != ESP_OK) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_set_mode: Error: " + std::to_string(retval)); - return retval; - } - - retval = esp_wifi_set_config(WIFI_IF_STA, &wifi_config); - if (retval != ESP_OK) { - if (retval == ESP_ERR_WIFI_PASSWORD) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_set_config: SSID password invalid! Error: " + std::to_string(retval)); - } - else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_set_config: Error: " + std::to_string(retval)); - } - return retval; - } - - retval = esp_wifi_start(); - if (retval != ESP_OK) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_wifi_start: Error: " + std::to_string(retval)); - return retval; - } - - if (!wlan_config.hostname.empty()) - { - retval = esp_netif_set_hostname(my_sta, wlan_config.hostname.c_str()); - if(retval != ESP_OK ) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to set hostname! Error: " + std::to_string(retval)); - } - else { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Set hostname to: " + wlan_config.hostname); - } - //initialize mDNS service - retval = mdns_init(); - if (retval != ESP_OK) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "mdns_init failed! Error: " + std::to_string(retval)); - } else { - //set mdns hostname - mdns_hostname_set(wlan_config.hostname.c_str()); - } - } - - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init successful"); - return ESP_OK; -} - - -int get_WIFI_RSSI() -{ - wifi_ap_record_t ap; - if (esp_wifi_sta_get_ap_info(&ap) == ESP_OK) - return ap.rssi; - else - return -127; // Return -127 if no info available e.g. not connected -} - - -/*std::string getIp() { - esp_netif_ip_info_t ip_info; - ESP_ERROR_CHECK(esp_netif_get_ip_info(my_sta, ip_info)); - char ipFormated[4*3+3+1]; - sprintf(ipFormated, IPSTR, IP2STR(&ip_info.ip)); - return std::string(ipFormated); -}*/ - - -std::string* getHostname() { - return &wlan_config.hostname; -} - - -bool getWIFIisConnected() -{ - return WIFIConnected; -} - - -void WIFIDestroy() -{ - esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler); - esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler); - #ifdef WLAN_USE_MESH_ROAMING - esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_BSS_RSSI_LOW, esp_bss_rssi_low_handler); - #endif - - esp_wifi_disconnect(); - esp_wifi_stop(); - esp_wifi_deinit(); -} - diff --git a/code/components/jomjol_wlan/connect_wlan.h b/code/components/jomjol_wlan/connect_wlan.h deleted file mode 100644 index a97168c3..00000000 --- a/code/components/jomjol_wlan/connect_wlan.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#ifndef CONNECT_WLAN_H -#define CONNECT_WLAN_H - -#include - -int wifi_init_sta(void); -std::string* getIPAddress(); -std::string* getSSID(); -int get_WIFI_RSSI(); -std::string* getHostname(); - -bool getWIFIisConnected(); -void WIFIDestroy(); - -#if (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES) -void wifiRoamingQuery(void); -#endif - -#ifdef WLAN_USE_ROAMING_BY_SCANNING -void wifiRoamByScanning(void); -#endif - -#endif //CONNECT_WLAN_H \ No newline at end of file diff --git a/code/components/jomjol_wlan/read_wlanini.cpp b/code/components/jomjol_wlan/read_wlanini.cpp deleted file mode 100644 index 8473a888..00000000 --- a/code/components/jomjol_wlan/read_wlanini.cpp +++ /dev/null @@ -1,396 +0,0 @@ -#include "read_wlanini.h" - -#include "Helper.h" - -#include "connect_wlan.h" - -#include -#include -#include -#include -#include -#include -#include "esp_log.h" -#include "ClassLogFile.h" -#include "../../include/defines.h" - -static const char *TAG = "WLANINI"; - - -struct wlan_config wlan_config = {}; - - -std::vector ZerlegeZeileWLAN(std::string input, std::string _delimiter = "") -{ - std::vector Output; - std::string delimiter = " =,"; - if (_delimiter.length() > 0){ - delimiter = _delimiter; - } - - input = trim(input, delimiter); - size_t pos = findDelimiterPos(input, delimiter); - std::string token; - if (pos != std::string::npos) // splitted only up to first equal sign !!! Special case for WLAN.ini - { - token = input.substr(0, pos); - token = trim(token, delimiter); - Output.push_back(token); - input.erase(0, pos + 1); - input = trim(input, delimiter); - } - Output.push_back(input); - - return Output; -} - - -int LoadWlanFromFile(std::string fn) -{ - std::string line = ""; - std::string tmp = ""; - std::vector splitted; - - fn = FormatFileName(fn); - FILE* pFile = fopen(fn.c_str(), "r"); - if (pFile == NULL) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Unable to open file (read). Device init aborted!"); - return -1; - } - - ESP_LOGD(TAG, "LoadWlanFromFile: wlan.ini opened"); - - char zw[256]; - if (fgets(zw, sizeof(zw), pFile) == NULL) { - line = ""; - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "file opened, but empty or content not readable. Device init aborted!"); - fclose(pFile); - return -1; - } - else { - line = std::string(zw); - } - - while ((line.size() > 0) || !(feof(pFile))) - { - //ESP_LOGD(TAG, "line: %s", line.c_str()); - if (line[0] != ';') { // Skip lines which starts with ';' - - splitted = ZerlegeZeileWLAN(line, "="); - splitted[0] = trim(splitted[0], " "); - - if ((splitted.size() > 1) && (toUpper(splitted[0]) == "SSID")){ - tmp = trim(splitted[1]); - if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){ - tmp = tmp.substr(1, tmp.length()-2); - } - wlan_config.ssid = tmp; - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "SSID: " + wlan_config.ssid); - } - - else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "PASSWORD")){ - tmp = splitted[1]; - if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){ - tmp = tmp.substr(1, tmp.length()-2); - } - wlan_config.password = tmp; - #ifndef __HIDE_PASSWORD - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Password: " + wlan_config.password); - #else - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Password: XXXXXXXX"); - #endif - } - - else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "HOSTNAME")){ - tmp = trim(splitted[1]); - if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){ - tmp = tmp.substr(1, tmp.length()-2); - } - wlan_config.hostname = tmp; - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Hostname: " + wlan_config.hostname); - } - - else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "IP")){ - tmp = splitted[1]; - if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){ - tmp = tmp.substr(1, tmp.length()-2); - } - wlan_config.ipaddress = tmp; - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "IP-Address: " + wlan_config.ipaddress); - } - - else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "GATEWAY")){ - tmp = splitted[1]; - if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){ - tmp = tmp.substr(1, tmp.length()-2); - } - wlan_config.gateway = tmp; - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Gateway: " + wlan_config.gateway); - } - - else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "NETMASK")){ - tmp = splitted[1]; - if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){ - tmp = tmp.substr(1, tmp.length()-2); - } - wlan_config.netmask = tmp; - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Netmask: " + wlan_config.netmask); - } - - else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "DNS")){ - tmp = splitted[1]; - if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){ - tmp = tmp.substr(1, tmp.length()-2); - } - wlan_config.dns = tmp; - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "DNS: " + wlan_config.dns); - } - - else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "HTTP_USERNAME")){ - tmp = splitted[1]; - if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){ - tmp = tmp.substr(1, tmp.length()-2); - } - wlan_config.http_username = tmp; - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_USERNAME: " + wlan_config.http_username); - } - - else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "HTTP_PASSWORD")){ - tmp = splitted[1]; - if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){ - tmp = tmp.substr(1, tmp.length()-2); - } - wlan_config.http_password = tmp; - #ifndef __HIDE_PASSWORD - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_PASSWORD: " + wlan_config.http_password); - #else - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP_PASSWORD: XXXXXXXX"); - #endif - } - - #if (defined WLAN_USE_ROAMING_BY_SCANNING || (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES)) - else if ((splitted.size() > 1) && (toUpper(splitted[0]) == "RSSITHRESHOLD")){ - tmp = trim(splitted[1]); - if ((tmp[0] == '"') && (tmp[tmp.length()-1] == '"')){ - tmp = tmp.substr(1, tmp.length()-2); - } - wlan_config.rssi_threshold = atoi(tmp.c_str()); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "RSSIThreshold: " + std::to_string(wlan_config.rssi_threshold)); - } - #endif - } - - /* read next line */ - if (fgets(zw, sizeof(zw), pFile) == NULL) { - line = ""; - } - else { - line = std::string(zw); - } - } - fclose(pFile); - - /* Check if SSID is empty (mandatory parameter) */ - if (wlan_config.ssid.empty()) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "SSID empty. Device init aborted!"); - return -2; - } - - /* Check if password is empty (mandatory parameter) */ - /* Disabled see issue #2393 - if (wlan_config.password.empty()) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Password empty. Device init aborted!"); - return -2; - } - */ - - return 0; -} - - -bool ChangeHostName(std::string fn, std::string _newhostname) -{ - if (_newhostname == wlan_config.hostname) - return false; - - std::string line = ""; - std::vector splitted; - std::vector neuesfile; - bool found = false; - - FILE* pFile = NULL; - - fn = FormatFileName(fn); - pFile = fopen(fn.c_str(), "r"); - if (pFile == NULL) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeHostName: Unable to open file wlan.ini (read)"); - return false; - } - - ESP_LOGD(TAG, "ChangeHostName: wlan.ini opened"); - - char zw[256]; - if (fgets(zw, sizeof(zw), pFile) == NULL) { - line = ""; - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeHostName: File opened, but empty or content not readable"); - return false; - } - else { - line = std::string(zw); - } - - while ((line.size() > 0) || !(feof(pFile))) - { - //ESP_LOGD(TAG, "ChangeHostName: line: %s", line.c_str()); - splitted = ZerlegeZeileWLAN(line, "="); - splitted[0] = trim(splitted[0], " "); - - if ((splitted.size() > 1) && ((toUpper(splitted[0]) == "HOSTNAME") || (toUpper(splitted[0]) == ";HOSTNAME"))){ - line = "hostname = \"" + _newhostname + "\"\n"; - found = true; - } - - neuesfile.push_back(line); - - if (fgets(zw, sizeof(zw), pFile) == NULL) - { - line = ""; - } - else - { - line = std::string(zw); - } - } - - if (!found) - { - line = "\n;++++++++++++++++++++++++++++++++++\n"; - line += "; Hostname: Name of device in network\n"; - line += "; This parameter can be configured via WebUI configuration\n"; - line += "; Default: \"watermeter\", if nothing is configured\n\n"; - line = "hostname = \"" + _newhostname + "\"\n"; - neuesfile.push_back(line); - } - fclose(pFile); - - pFile = fopen(fn.c_str(), "w+"); - if (pFile == NULL) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeHostName: Unable to open file wlan.ini (write)"); - return false; - } - - for (int i = 0; i < neuesfile.size(); ++i) - { - //ESP_LOGD(TAG, "%s", neuesfile[i].c_str()); - fputs(neuesfile[i].c_str(), pFile); - } - fclose(pFile); - - ESP_LOGD(TAG, "ChangeHostName done"); - - return true; -} - -#if (defined WLAN_USE_ROAMING_BY_SCANNING || (defined WLAN_USE_MESH_ROAMING && defined WLAN_USE_MESH_ROAMING_ACTIVATE_CLIENT_TRIGGERED_QUERIES)) -bool ChangeRSSIThreshold(std::string fn, int _newrssithreshold) -{ - if (wlan_config.rssi_threshold == _newrssithreshold) - return false; - - std::string line = ""; - std::vector splitted; - std::vector neuesfile; - bool found = false; - - FILE* pFile = NULL; - - fn = FormatFileName(fn); - pFile = fopen(fn.c_str(), "r"); - if (pFile == NULL) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeRSSIThreshold: Unable to open file wlan.ini (read)"); - return false; - } - - ESP_LOGD(TAG, "ChangeRSSIThreshold: wlan.ini opened"); - - char zw[256]; - if (fgets(zw, sizeof(zw), pFile) == NULL) { - line = ""; - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeRSSIThreshold: File opened, but empty or content not readable"); - return false; - } - else { - line = std::string(zw); - } - - while ((line.size() > 0) || !(feof(pFile))) - { - ESP_LOGD(TAG, "%s", line.c_str()); - splitted = ZerlegeZeileWLAN(line, "="); - splitted[0] = trim(splitted[0], " "); - - /* Workaround to eliminate line with typo "RSSIThreashold" or "rssi" if existing */ - if (((splitted.size() > 1) && (toUpper(splitted[0]) == "RSSITHREASHOLD")) || - ((splitted.size() > 1) && (toUpper(splitted[0]) == ";RSSITHREASHOLD")) || - ((splitted.size() > 1) && (toUpper(splitted[0]) == "RSSI")) || - ((splitted.size() > 1) && (toUpper(splitted[0]) == ";RSSI"))) { - if (fgets(zw, sizeof(zw), pFile) == NULL) { - line = ""; - } - else { - line = std::string(zw); - } - continue; - } - - if ((splitted.size() > 1) && ((toUpper(splitted[0]) == "RSSITHRESHOLD") || (toUpper(splitted[0]) == ";RSSITHRESHOLD"))) { - line = "RSSIThreshold = " + to_string(_newrssithreshold) + "\n"; - found = true; - } - - neuesfile.push_back(line); - - if (fgets(zw, sizeof(zw), pFile) == NULL) { - line = ""; - } - else { - line = std::string(zw); - } - } - - if (!found) - { - line = "\n;++++++++++++++++++++++++++++++++++\n"; - line += "; WIFI Roaming:\n"; - line += "; Network assisted roaming protocol is activated by default\n"; - line += "; AP / mesh system needs to support roaming protocol 802.11k/v\n"; - line += ";\n"; - line += "; Optional feature (usually not neccessary):\n"; - line += "; RSSI Threshold for client requested roaming query (RSSI < RSSIThreshold)\n"; - line += "; Note: This parameter can be configured via WebUI configuration\n"; - line += "; Default: 0 = Disable client requested roaming query\n\n"; - line += "RSSIThreshold = " + to_string(_newrssithreshold) + "\n"; - neuesfile.push_back(line); - } - - fclose(pFile); - - pFile = fopen(fn.c_str(), "w+"); - if (pFile == NULL) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ChangeRSSIThreshold: Unable to open file wlan.ini (write)"); - return false; - } - - for (int i = 0; i < neuesfile.size(); ++i) - { - //ESP_LOGD(TAG, "%s", neuesfile[i].c_str()); - fputs(neuesfile[i].c_str(), pFile); - } - - fclose(pFile); - - ESP_LOGD(TAG, "ChangeRSSIThreshold done"); - - return true; -} -#endif diff --git a/code/components/jomjol_wlan/read_wlanini.h b/code/components/jomjol_wlan/read_wlanini.h deleted file mode 100644 index 759f6833..00000000 --- a/code/components/jomjol_wlan/read_wlanini.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#ifndef READ_WLANINI_H -#define READ_WLANINI_H - -#include - -struct wlan_config { - std::string ssid = ""; - std::string password = ""; - std::string hostname = "watermeter"; // Default: watermeter - std::string ipaddress = ""; - std::string gateway = ""; - std::string netmask = ""; - std::string dns = ""; - std::string http_username = ""; - std::string http_password = ""; - int rssi_threshold = 0; // Default: 0 -> ROAMING disabled -}; -extern struct wlan_config wlan_config; - - -int LoadWlanFromFile(std::string fn); -bool ChangeHostName(std::string fn, std::string _newhostname); -bool ChangeRSSIThreshold(std::string fn, int _newrssithreshold); - - -#endif //READ_WLANINI_H \ No newline at end of file diff --git a/code/components/mdns/.cz.yaml b/code/components/mdns/.cz.yaml new file mode 100644 index 00000000..a8390acf --- /dev/null +++ b/code/components/mdns/.cz.yaml @@ -0,0 +1,8 @@ +--- +commitizen: + bump_message: 'bump(mdns): $current_version -> $new_version' + pre_bump_hooks: python ../../ci/changelog.py mdns + tag_format: mdns-v$version + version: 1.4.3 + version_files: + - idf_component.yml diff --git a/code/components/mdns/CHANGELOG.md b/code/components/mdns/CHANGELOG.md new file mode 100644 index 00000000..2fae3699 --- /dev/null +++ b/code/components/mdns/CHANGELOG.md @@ -0,0 +1,493 @@ +# Changelog + +## [1.4.3](https://github.com/espressif/esp-protocols/commits/mdns-v1.4.3) + +### Features + +- support zero item when update subtype ([5bd82c01](https://github.com/espressif/esp-protocols/commit/5bd82c01)) + +## [1.4.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.4.2) + +### Features + +- support update subtype ([062b8dca](https://github.com/espressif/esp-protocols/commit/062b8dca)) + +### Updated + +- chore(mdns): Add more info to idf_component.yml ([4a1cb65c](https://github.com/espressif/esp-protocols/commit/4a1cb65c)) + +## [1.4.1](https://github.com/espressif/esp-protocols/commits/mdns-v1.4.1) + +### Features + +- Send PTR query for mdns browse when interface is ready ([010a404a](https://github.com/espressif/esp-protocols/commit/010a404a)) + +### Bug Fixes + +- Prevent deadlock when deleting a browse request ([3f48f9ea](https://github.com/espressif/esp-protocols/commit/3f48f9ea)) +- Fix use after free reported by coverity ([25b3d5fd](https://github.com/espressif/esp-protocols/commit/25b3d5fd)) +- Fixed dead-code reported by coverity ([11846c7d](https://github.com/espressif/esp-protocols/commit/11846c7d)) + +## [1.4.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.4.0) + +### Major changes + +- Fixed mdns API issues when add/remove/update records from multiple threads ([Fix services API races to directly add/remove services](https://github.com/espressif/esp-protocols/commit/8a690503)) + +### Features + +- Unit tests for add/remove/update deleg/selfhosted services ([0660ece1](https://github.com/espressif/esp-protocols/commit/0660ece1)) +- Add console command for mdns browsing ([1e8ede33](https://github.com/espressif/esp-protocols/commit/1e8ede33)) +- Console test: set instance for service ([f107dcd1](https://github.com/espressif/esp-protocols/commit/f107dcd1)) +- Console test: add subtype for service ([ee00e97b](https://github.com/espressif/esp-protocols/commit/ee00e97b)) +- Console test: set port for (delegated) srvs ([07b79abf](https://github.com/espressif/esp-protocols/commit/07b79abf)) +- Console test: add/remove TXT recs for delegated srvs ([c9a58d73](https://github.com/espressif/esp-protocols/commit/c9a58d73)) +- Console test for changing TXT records ([6b9a6ce6](https://github.com/espressif/esp-protocols/commit/6b9a6ce6)) +- Console test for add/remove delegated service APIs ([43de7e5c](https://github.com/espressif/esp-protocols/commit/43de7e5c)) +- Console test for add/remove delegated host APIs ([ce7f326a](https://github.com/espressif/esp-protocols/commit/ce7f326a)) +- Console test for lookup service APIs ([a91ead8e](https://github.com/espressif/esp-protocols/commit/a91ead8e)) +- Add linux console functional tests ([50d059af](https://github.com/espressif/esp-protocols/commit/50d059af)) +- check if the txt items is changed when browsing ([e2f0477a](https://github.com/espressif/esp-protocols/commit/e2f0477a)) + +### Bug Fixes + +- Fix mdns_delegate_hostname_add() to block until done ([2c1b1661](https://github.com/espressif/esp-protocols/commit/2c1b1661)) +- Fix API races when removing all services ([169405b5](https://github.com/espressif/esp-protocols/commit/169405b5)) +- Fix API races setting instance name for services ([643dc6d4](https://github.com/espressif/esp-protocols/commit/643dc6d4)) +- Fix API races while adding subtypes for services ([f9f234c4](https://github.com/espressif/esp-protocols/commit/f9f234c4)) +- Fix API races removing txt item for services ([3f97a822](https://github.com/espressif/esp-protocols/commit/3f97a822)) +- Fix API races adding txt item for services ([c62b920b](https://github.com/espressif/esp-protocols/commit/c62b920b)) +- Fix API races while setting txt for services ([a927bf3a](https://github.com/espressif/esp-protocols/commit/a927bf3a)) +- Fix API races while setting port for services ([99d5fb27](https://github.com/espressif/esp-protocols/commit/99d5fb27)) +- Fix services API races to directly add/remove services ([8a690503](https://github.com/espressif/esp-protocols/commit/8a690503)) +- Fix mdns mdns_lookup_service() to handle empty TXT ([d4da9cb0](https://github.com/espressif/esp-protocols/commit/d4da9cb0)) + +## [1.3.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.3.2) + +### Features + +- add check of instance when handling PTR query ([6af6ca5](https://github.com/espressif/esp-protocols/commit/6af6ca5)) + +### Bug Fixes + +- Fix of mdns afl tests ([139166c](https://github.com/espressif/esp-protocols/commit/139166c)) +- remove same protocol services with different instances ([042533a](https://github.com/espressif/esp-protocols/commit/042533a)) + +## [1.3.1](https://github.com/espressif/esp-protocols/commits/mdns-v1.3.1) + +### Bug Fixes + +- free txt value len ([afd98bb](https://github.com/espressif/esp-protocols/commit/afd98bb)) + +## [1.3.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.3.0) + +### Features + +- add a new mdns query mode `browse` ([af330b6](https://github.com/espressif/esp-protocols/commit/af330b6)) +- Make including mdns_console KConfigurable ([27adbfe](https://github.com/espressif/esp-protocols/commit/27adbfe)) + +### Bug Fixes + +- Schedule all queued Tx packets from timer task ([d4e693e](https://github.com/espressif/esp-protocols/commit/d4e693e)) +- add lock for some common apis ([21c84bf](https://github.com/espressif/esp-protocols/commit/21c84bf)) +- fix mdns answer append while host is invalid ([7be16bc](https://github.com/espressif/esp-protocols/commit/7be16bc)) + +## [1.2.5](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.5) + +### Bug Fixes + +- Fixed build issues for targets without WiFi caps ([302b46f](https://github.com/espressif/esp-protocols/commit/302b46f)) + +## [1.2.4](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.4) + +### Bug Fixes + +- Correction on 6d2c475 MDNS_PREDEF_NETIF_ETH fix ([fc59f87c4e](https://github.com/espressif/esp-protocols/commit/fc59f87c4e)) +- fix the logic of creating pcb for networking socket ([5000a9a20a](https://github.com/espressif/esp-protocols/commit/5000a9a20a)) +- fix compiling issue when disabling IPv4 ([2646dcd23a](https://github.com/espressif/esp-protocols/commit/2646dcd23a)) +- Fix compile error when MDNS_PREDEF_NETIF_ETH is defined, but ETH_ENABLED is not (#459) ([6d2c475c20](https://github.com/espressif/esp-protocols/commit/6d2c475c20)) + +## [1.2.3](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.3) + +### Bug Fixes + +- fixed CI issues for host and afl tests ([4be5efc84e](https://github.com/espressif/esp-protocols/commit/4be5efc84e)) +- fix copy delegated host addr ([19fb36000c](https://github.com/espressif/esp-protocols/commit/19fb36000c)) +- enable CONFIG_ESP_WIFI_ENABLED if CONFIG_SOC_WIFI_SUPPORTED is also enabled (for ESP-IDF <= 5.1) ([d20a718320](https://github.com/espressif/esp-protocols/commit/d20a718320)) +- remove protocol_examples_common ([1ee9dae6bf](https://github.com/espressif/esp-protocols/commit/1ee9dae6bf)) +- move the example into a subdirectory ([d28232b9f8](https://github.com/espressif/esp-protocols/commit/d28232b9f8)) +- reference protocol_examples_common from IDF ([c83b76ea8f](https://github.com/espressif/esp-protocols/commit/c83b76ea8f)) + +## [1.2.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.2) + +### Bug Fixes + +- add terminator for the getting host name ([b6a4d94](https://github.com/espressif/esp-protocols/commit/b6a4d94)) +- Enable ESP_WIFI_CONFIG when ESP-IDF <= 5.1 ([0b783c0](https://github.com/espressif/esp-protocols/commit/0b783c0)) +- set host list NULL on destroy ([ea54eef](https://github.com/espressif/esp-protocols/commit/ea54eef)) +- removed Wno-format flag and fixed formatting warnings ([c48e442](https://github.com/espressif/esp-protocols/commit/c48e442)) +- remove the the range of MDNS_MAX_SERVICES and fix issues of string functions ([3dadce2](https://github.com/espressif/esp-protocols/commit/3dadce2)) + +## [1.2.1](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.1) + +### Features + +- Allow setting length of mDNS action queue in menuconfig ([28cd898](https://github.com/espressif/esp-protocols/commit/28cd898)) + +### Bug Fixes + +- fix build issue if CONFIG_ESP_WIFI_ENABLED disabled ([24f7031](https://github.com/espressif/esp-protocols/commit/24f7031)) +- added idf_component.yml for examples ([d273e10](https://github.com/espressif/esp-protocols/commit/d273e10)) +- added guard check for null pointer ([71bb461](https://github.com/espressif/esp-protocols/commit/71bb461)) + +## [1.2.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.2.0) + +### Features + +- add an API for setting address to a delegated host ([ddc3eb6](https://github.com/espressif/esp-protocols/commit/ddc3eb6)) +- Add support for lwip build under linux ([588465d](https://github.com/espressif/esp-protocols/commit/588465d)) +- Allow for adding a delegated host with no address ([c562461](https://github.com/espressif/esp-protocols/commit/c562461)) +- Add APIs for looking up self hosted services and getting the self hostname ([f0df12d](https://github.com/espressif/esp-protocols/commit/f0df12d)) + +### Bug Fixes + +- Refactor freertos linux compat layers ([79a0e57](https://github.com/espressif/esp-protocols/commit/79a0e57)) +- Fix delegated service PTR response ([cab0e1d](https://github.com/espressif/esp-protocols/commit/cab0e1d)) +- Added unit tests to CI + minor fix to pass it ([c974c14](https://github.com/espressif/esp-protocols/commit/c974c14)) + +### Updated + +- docs: update documentation links ([4de5298](https://github.com/espressif/esp-protocols/commit/4de5298)) + +## [1.1.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.1.0) + +### Features + +- Decouple main module from mdns-networking ([d238e93](https://github.com/espressif/esp-protocols/commit/d238e93)) + +### Bug Fixes + +- Use idf-build-apps package for building mdns ([1a0a41f](https://github.com/espressif/esp-protocols/commit/1a0a41f)) +- socket networking to init interfaces properly ([ee9b04f](https://github.com/espressif/esp-protocols/commit/ee9b04f)) +- Removed unused internal lock from mdns_server struct ([a06fb77](https://github.com/espressif/esp-protocols/commit/a06fb77)) +- Resolve conflicts only on self hosted items ([e69a9eb](https://github.com/espressif/esp-protocols/commit/e69a9eb), [#185](https://github.com/espressif/esp-protocols/issues/185)) +- Fix memory issues reported by valgrind ([0a682e7](https://github.com/espressif/esp-protocols/commit/0a682e7)) + +### Updated + +- docs(common): updated component and example links ([f48d9b2](https://github.com/espressif/esp-protocols/commit/f48d9b2)) +- Add APIs to look up delegated services ([87dcd7d](https://github.com/espressif/esp-protocols/commit/87dcd7d)) +- Fix deadly mdns crash ([4fa3023](https://github.com/espressif/esp-protocols/commit/4fa3023)) +- docs(common): improving documentation ([ca3fce0](https://github.com/espressif/esp-protocols/commit/ca3fce0)) +- append all ipv6 address in mdns answer ([5ed3e9a](https://github.com/espressif/esp-protocols/commit/5ed3e9a)) +- test(mdns): Host tests to use IDF's esp_event_stub ([537d170](https://github.com/espressif/esp-protocols/commit/537d170)) + +## [1.0.9](https://github.com/espressif/esp-protocols/commits/mdns-v1.0.9) + +### Features + +- Add reverse lookup to the example and test ([d464ed7](https://github.com/espressif/esp-protocols/commit/d464ed7)) +- Add support for IPv6 reverse query ([d4825f5](https://github.com/espressif/esp-protocols/commit/d4825f5)) + +### Bug Fixes + +- Reintroduce missing CHANGELOGs ([200cbb3](https://github.com/espressif/esp-protocols/commit/200cbb3)) +- use semaphore instead of task notification bits (IDFGH-9380) ([73f2800](https://github.com/espressif/esp-protocols/commit/73f2800), [IDF#10754](https://github.com/espressif/esp-idf/issues/10754)) + +### Updated + +- ci(common): force scoping commit messages with components ([c55fcc0](https://github.com/espressif/esp-protocols/commit/c55fcc0)) +- Add homepage URL and License to all components ([ef3f0ee](https://github.com/espressif/esp-protocols/commit/ef3f0ee)) +- docs: fix of mdns link translation ([1c850dd](https://github.com/espressif/esp-protocols/commit/1c850dd)) +- unite all tags under common structure py test: update tags under common structure ([c6db3ea](https://github.com/espressif/esp-protocols/commit/c6db3ea)) + +## [1.0.8](https://github.com/espressif/esp-protocols/commits/b9b4a75) + +### Features + +- Add support for IPv4 reverse query ([b87bef5](https://github.com/espressif/esp-protocols/commit/b87bef5)) + +### Bug Fixes + +- Host test with IDFv5.1 ([fb8a2f0](https://github.com/espressif/esp-protocols/commit/fb8a2f0)) +- Remove strict mode as it's invalid ([d0c9070](https://github.com/espressif/esp-protocols/commit/d0c9070)) +- Allow setting instance name only after hostname set ([a8339e4](https://github.com/espressif/esp-protocols/commit/a8339e4), [#190](https://github.com/espressif/esp-protocols/issues/190)) +- Make unit test executable with pytest ([12cfcb5](https://github.com/espressif/esp-protocols/commit/12cfcb5)) +- AFL port layer per IDF-latest changes ([0247926](https://github.com/espressif/esp-protocols/commit/0247926)) + +### Updated + +- bump the component version to 1.0.8 ([b9b4a75](https://github.com/espressif/esp-protocols/commit/b9b4a75)) +- Make reverse query conditional per Kconfig ([91134f1](https://github.com/espressif/esp-protocols/commit/91134f1)) +- Added badges with version of components to the respective README files ([e4c8a59](https://github.com/espressif/esp-protocols/commit/e4c8a59)) +- fix some coverity reported issues ([c73c797](https://github.com/espressif/esp-protocols/commit/c73c797)) +- Examples: using pytest.ini from top level directory ([aee016d](https://github.com/espressif/esp-protocols/commit/aee016d)) +- mDNS: test_app pytest migration ([f71f61f](https://github.com/espressif/esp-protocols/commit/f71f61f)) +- CI: fixing the files to be complient with pre-commit hooks ([945bd17](https://github.com/espressif/esp-protocols/commit/945bd17)) +- prevent crash when hostname is null ([3498e86](https://github.com/espressif/esp-protocols/commit/3498e86)) +- Example tests integration ([a045c1c](https://github.com/espressif/esp-protocols/commit/a045c1c)) +- Replace hardcoded TTL values with named defines ([bb4c002](https://github.com/espressif/esp-protocols/commit/bb4c002)) +- Fix add_service() to report error if no-hostname ([656ab21](https://github.com/espressif/esp-protocols/commit/656ab21)) + + +## [1.0.7](https://github.com/espressif/esp-protocols/commits/088f7ac) + +### Updated + +- bump up the component version ([088f7ac](https://github.com/espressif/esp-protocols/commit/088f7ac)) +- fix IPV4 only build and also update CI configuration ([e079f8b](https://github.com/espressif/esp-protocols/commit/e079f8b)) +- add test configuration for IPV6 disabled build ([330332a](https://github.com/espressif/esp-protocols/commit/330332a)) + + +## [1.0.6](https://github.com/espressif/esp-protocols/commits/48c157b) + +### Bug Fixes + +- Example makefile to add only mdns as extra comps ([d74c296](https://github.com/espressif/esp-protocols/commit/d74c296)) +- ignore authoritative flag on reception ([415e04a](https://github.com/espressif/esp-protocols/commit/415e04a)) + +### Updated + +- fix build issue with CONFIG_LWIP_IPV6 disabled ([48c157b](https://github.com/espressif/esp-protocols/commit/48c157b)) +- fix bit order issue in DNS header flags ([c4e85bd](https://github.com/espressif/esp-protocols/commit/c4e85bd)) +- updated package version to 0.1.19 ([469f953](https://github.com/espressif/esp-protocols/commit/469f953)) + + +## [1.0.5](https://github.com/espressif/esp-protocols/commits/36de9af) + +### Features + +- Define explicit dependencies on esp-wifi ([36de9af](https://github.com/espressif/esp-protocols/commit/36de9af)) + +### Updated + +- bugfix: mdns IPv6 address convert error ([238ee96](https://github.com/espressif/esp-protocols/commit/238ee96)) + + +## [1.0.4](https://github.com/espressif/esp-protocols/commits/57afa38) + +### Updated + +- Bump asio/mdns/esp_websocket_client versions ([57afa38](https://github.com/espressif/esp-protocols/commit/57afa38)) +- ignore format warnings ([d66f9dc](https://github.com/espressif/esp-protocols/commit/d66f9dc)) +- Fix test_app build ([0b102f6](https://github.com/espressif/esp-protocols/commit/0b102f6)) + + +## [1.0.3](https://github.com/espressif/esp-protocols/commits/4868689) + +### Updated + +- Updated mDNS to explicitely use esp-eth dependency if needed ([4868689](https://github.com/espressif/esp-protocols/commit/4868689), [IDF@5e19b9c](https://github.com/espressif/esp-idf/commit/5e19b9c9518ae253d82400ab24b86af8af832425)) + + +## [1.0.2](https://github.com/espressif/esp-protocols/commits/8fe2a3a) + +### Features + +- fix bug when clean action memory ([81c219d](https://github.com/espressif/esp-protocols/commit/81c219d), [IDF@3d4deb9](https://github.com/espressif/esp-idf/commit/3d4deb972620cae8e8ce4d0050153cc6f39db372)) + +### Bug Fixes + +- add the maximum number of services ([0191d6f](https://github.com/espressif/esp-protocols/commit/0191d6f), [IDF@ba458c6](https://github.com/espressif/esp-idf/commit/ba458c69cfb2f18478d73690c289b09641c62004)) +- fix the exception when remove one of multiple service ([b26c866](https://github.com/espressif/esp-protocols/commit/b26c866), [IDF@696d733](https://github.com/espressif/esp-idf/commit/696d733eb04ee98f764dffdc82bcef51a724c9c6)) + +### Updated + +- Minor fixes here and there ([8fe2a3a](https://github.com/espressif/esp-protocols/commit/8fe2a3a)) +- mDNS: Initial version based on IDF 5.0 ([b6b20ad](https://github.com/espressif/esp-protocols/commit/b6b20ad)) +- soc: moved kconfig options out of the target component. ([4a52cf2](https://github.com/espressif/esp-protocols/commit/4a52cf2), [IDF@d287209](https://github.com/espressif/esp-idf/commit/d2872095f93ed82fb91c776081bc1d032493d93e)) +- cmake: fix issue with passing cxx_std option for GCC 11, a common workaround ([87c2699](https://github.com/espressif/esp-protocols/commit/87c2699), [IDF@ea0d212](https://github.com/espressif/esp-idf/commit/ea0d2123c806bd0ad77bc49843ee905cf9be65ff)) +- kconfig: Changed default values of bool configs - Some bool configs were using default values true and false, instead of y and n. ([eb536a7](https://github.com/espressif/esp-protocols/commit/eb536a7), [IDF@25c5c21](https://github.com/espressif/esp-idf/commit/25c5c214f38ca690b03533e12fb5a4d774c7eae0)) +- esp_netif: Remove tcpip_adapter compatibility layer ([3e93ea9](https://github.com/espressif/esp-protocols/commit/3e93ea9), [IDF@795b7ed](https://github.com/espressif/esp-idf/commit/795b7ed993784e3134195e12b0978504d83dfd56)) +- Fix copyright messages, update API descrition ([2c764b1](https://github.com/espressif/esp-protocols/commit/2c764b1), [IDF@42ba8a8](https://github.com/espressif/esp-idf/commit/42ba8a8338fd5efd82498a5989fc5c105938d447)) +- Add API to control custom network interfaces ([f836ae7](https://github.com/espressif/esp-protocols/commit/f836ae7), [IDF@b02468d](https://github.com/espressif/esp-idf/commit/b02468dc98d614f931d14cd8b5e2373ca51fb18d)) +- CI/mdns: Fix fuzzer build ([4b5f24f](https://github.com/espressif/esp-protocols/commit/4b5f24f), [IDF@98e9426](https://github.com/espressif/esp-idf/commit/98e9426b660a6e825f811cccd45a0722cc801ccd)) +- Add support for registering custom netif ([30f37c0](https://github.com/espressif/esp-protocols/commit/30f37c0), [IDF@bec42ff](https://github.com/espressif/esp-idf/commit/bec42ff85d5091d71e1cb1063bea20d7c6ac8c76)) +- Indicate interface using esp_netif in search results ([ddc58e8](https://github.com/espressif/esp-protocols/commit/ddc58e8), [IDF@f8495f1](https://github.com/espressif/esp-idf/commit/f8495f1e86de9a8e7d046bf13d0ca04775041b4c)) +- Use predefined interfaces to prepare for custom netifs ([fa951bf](https://github.com/espressif/esp-protocols/commit/fa951bf), [IDF@f90b3b7](https://github.com/espressif/esp-idf/commit/f90b3b798b446382d848f8c55c5e1653c81871cd)) +- Prepare for dynamic esp-netif support ([605d1fa](https://github.com/espressif/esp-protocols/commit/605d1fa), [IDF@f9892f7](https://github.com/espressif/esp-idf/commit/f9892f77b88ba77dc6608ba746175f6dc64a7607)) +- esp_hw_support/esp_system: Re-evaluate header inclusions and include directories ([58bf218](https://github.com/espressif/esp-protocols/commit/58bf218), [IDF@a9fda54](https://github.com/espressif/esp-idf/commit/a9fda54d39d1321005c3bc9b3cc268d0b7e9f052)) +- system: move kconfig options out of target component ([ec491ec](https://github.com/espressif/esp-protocols/commit/ec491ec), [IDF@bb88338](https://github.com/espressif/esp-idf/commit/bb88338118957c2214a4c0a33cd4a152e2e1f8ba)) +- Update to drop our own packet if bounced back ([94ae672](https://github.com/espressif/esp-protocols/commit/94ae672), [IDF@b5149e3](https://github.com/espressif/esp-idf/commit/b5149e3ee73728f790798e6757d732fe426e21c7)) +- Fix potential read behind parsed packet ([e5a3a3d](https://github.com/espressif/esp-protocols/commit/e5a3a3d), [IDF@51a5de2](https://github.com/espressif/esp-idf/commit/51a5de2525d0e82adea2e298a0edcc9b2dee5edd)) +- Fix memleak when adding delegated host ([7710ea9](https://github.com/espressif/esp-protocols/commit/7710ea9), [IDF@9cbdb87](https://github.com/espressif/esp-idf/commit/9cbdb8767bdf6e9745e895b2c5af74d0376965e7)) +- Fix null-service issue when parsing packets ([034c55e](https://github.com/espressif/esp-protocols/commit/034c55e), [IDF#8307](https://github.com/espressif/esp-idf/issues/8307), [IDF@a57be7b](https://github.com/espressif/esp-idf/commit/a57be7b7d1135ddb29f9da636e9ad315f7fa1fa7)) +- Update fuzzer test (add delegation, check memory) ([ec03fec](https://github.com/espressif/esp-protocols/commit/ec03fec), [IDF@2c10071](https://github.com/espressif/esp-idf/commit/2c1007156e01b4707b5c89d73cad05c0eef0264f)) +- Remove legacy esp_event API ([5909e9e](https://github.com/espressif/esp-protocols/commit/5909e9e), [IDF@e46aa51](https://github.com/espressif/esp-idf/commit/e46aa515bdf5606a3d868f1034774d5fc96904b8)) +- added missing includes ([82e2a5d](https://github.com/espressif/esp-protocols/commit/82e2a5d), [IDF@28d09c7](https://github.com/espressif/esp-idf/commit/28d09c7dbe145ffa6a7dd90531062d4f7669a9c8)) +- Clear notification value in mdns_hostname_set ([48e4d40](https://github.com/espressif/esp-protocols/commit/48e4d40), [IDF@83a4ddb](https://github.com/espressif/esp-idf/commit/83a4ddbd250e2b386bccabb4705d4c58c1a22bcb)) +- esp_timer: remove legacy ESP32 FRC timer implementation. ([ac6dcb6](https://github.com/espressif/esp-protocols/commit/ac6dcb6), [IDF@edb76f1](https://github.com/espressif/esp-idf/commit/edb76f14d6b3e925568ff04a87befe733ecc4517)) +- freertos: Remove legacy data types ([085dbd8](https://github.com/espressif/esp-protocols/commit/085dbd8), [IDF@57fd78f](https://github.com/espressif/esp-idf/commit/57fd78f5baf93a368a82cf4b2e00ca17ffc09115)) +- Tools: Custom baud-rate setup is not possible for IDF Monitor from menuconfig anymore ([f78e8cf](https://github.com/espressif/esp-protocols/commit/f78e8cf), [IDF@36a4011](https://github.com/espressif/esp-idf/commit/36a4011ff8985bfbae08ba0272194e6c3ef93bbf)) +- Use memcpy() for copy to support non-text TXTs ([6cdf5ee](https://github.com/espressif/esp-protocols/commit/6cdf5ee), [IDF@6aefe9c](https://github.com/espressif/esp-idf/commit/6aefe9c18563ed567d384a956cf02b6f57d6894c)) +- Support for null-value TXT records ([fcb5515](https://github.com/espressif/esp-protocols/commit/fcb5515), [IDF#8267](https://github.com/espressif/esp-idf/issues/8267), [IDF@23c2db4](https://github.com/espressif/esp-idf/commit/23c2db406dee8df09dbdba21cb7eef9fbca8bf27)) +- Fix alloc issue if TXT has empty value ([9fdbe5f](https://github.com/espressif/esp-protocols/commit/9fdbe5f), [IDF@205f6ba](https://github.com/espressif/esp-idf/commit/205f6ba8541e12d958c7c56af5a7136090f12a0e)) +- Fix random crash when defalt service instance queried ([20e6e9e](https://github.com/espressif/esp-protocols/commit/20e6e9e), [IDF@f46dffc](https://github.com/espressif/esp-idf/commit/f46dffca627e9578e49a510580f9754ec1e27e2e)) +- Fix minor memory leaks when creating services ([c588263](https://github.com/espressif/esp-protocols/commit/c588263), [IDF@fad62cc](https://github.com/espressif/esp-idf/commit/fad62cc1ed3dce63b58297172a72a489d7af2d9d)) +- Fix mDNS memory leak ([6258edf](https://github.com/espressif/esp-protocols/commit/6258edf), [IDF@119b4a9](https://github.com/espressif/esp-idf/commit/119b4a9dd12cf89cc5eaf63f8aa19730607ef30b)) +- Fix mDNS memory leak ([c8b0d5e](https://github.com/espressif/esp-protocols/commit/c8b0d5e), [IDF@f5ffd53](https://github.com/espressif/esp-idf/commit/f5ffd53aeb402afc1333a98168bb2fa35d7cdc77)) +- Use multi/uni-cast types in API ([5252b1d](https://github.com/espressif/esp-protocols/commit/5252b1d), [IDF@125c312](https://github.com/espressif/esp-idf/commit/125c3125524c71f4f48f635eda12e22fa3bca500)) +- Allow for unicast PTR queries ([4e11cc8](https://github.com/espressif/esp-protocols/commit/4e11cc8), [IDF@7eeeb01](https://github.com/espressif/esp-idf/commit/7eeeb01ea705745b027bd8bc11d2b142418e9927)) +- Fix potential null deref for ANY query type ([7af91ec](https://github.com/espressif/esp-protocols/commit/7af91ec), [IDF@99dd8ee](https://github.com/espressif/esp-idf/commit/99dd8eedb1a0e957f5f74344e3e4172e61c29ef8)) +- Make fuzzer layers compatible with llvm>=6 ([01256d3](https://github.com/espressif/esp-protocols/commit/01256d3), [IDF@1882cbe](https://github.com/espressif/esp-idf/commit/1882cbe44e6140bebb2d27dc18af06dfcb0157f5)) +- Fix copyright ([5a2d4ea](https://github.com/espressif/esp-protocols/commit/5a2d4ea), [IDF@c83678f](https://github.com/espressif/esp-idf/commit/c83678f64fe27844fc28050bde6433ccb04a0704)) +- Add mDNS miss comment ([9de3f53](https://github.com/espressif/esp-protocols/commit/9de3f53), [IDF@08e0813](https://github.com/espressif/esp-idf/commit/08e081340d9d76d1244e9f2dc527e5ae370b1fbe)) +- freertos: remove FREERTOS_ASSERT option ([bcabc8e](https://github.com/espressif/esp-protocols/commit/bcabc8e), [IDF@7255497](https://github.com/espressif/esp-idf/commit/72554971467a5edc9bd6e390cf8fe7b05e6ade81)) +- Minor err print fix in socket-networking layer ([dfb27b3](https://github.com/espressif/esp-protocols/commit/dfb27b3), [IDF@f1b8f5c](https://github.com/espressif/esp-idf/commit/f1b8f5c1023df7d649161bc76f2bcc9a8f8f4d8b)) +- unified errno format ([076c095](https://github.com/espressif/esp-protocols/commit/076c095), [IDF@87506f4](https://github.com/espressif/esp-idf/commit/87506f46e2922710f48a6b96ca75e53543ff45c4)) +- always send A/AAAA records in announcements ([7dd0bc1](https://github.com/espressif/esp-protocols/commit/7dd0bc1), [IDF@456f80b](https://github.com/espressif/esp-idf/commit/456f80b754ebd0bd74e02c7febdf461c6b573b7a)) +- filter instance name for ANY queries ([7e82a7c](https://github.com/espressif/esp-protocols/commit/7e82a7c), [IDF@5d0c473](https://github.com/espressif/esp-idf/commit/5d0c47303dd9ead0f2ad291dca1d4b7ce4e23b2b)) +- Fix potential null deref reported by fuzzer test ([ae381b7](https://github.com/espressif/esp-protocols/commit/ae381b7), [IDF@cb5653f](https://github.com/espressif/esp-idf/commit/cb5653fd940a9cd41e8554a6d753fab46e0459d7)) +- Minor fix of API description and API usage ([941dc5c](https://github.com/espressif/esp-protocols/commit/941dc5c), [IDF@c297301](https://github.com/espressif/esp-idf/commit/c297301ecc350f8315d7eaf78c72b4aba68d422a)) +- Added results count to MDNS ([525c649](https://github.com/espressif/esp-protocols/commit/525c649), [IDF@f391d61](https://github.com/espressif/esp-idf/commit/f391d610e8185631b5361dc6c844c4c04aac30b1)) +- fix mdns server instance mismatch ([f0839d9](https://github.com/espressif/esp-protocols/commit/f0839d9), [IDF@6173dd7](https://github.com/espressif/esp-idf/commit/6173dd78097216261277c20ebd92a53c68c47f89)) +- support multiple instance for mdns service txt set ([69902ea](https://github.com/espressif/esp-protocols/commit/69902ea), [IDF@50f6302](https://github.com/espressif/esp-idf/commit/50f6302c5d7c0498fa1baa6fd6129d8233971a81)) +- fix wrong PTR record count ([d0bbe88](https://github.com/espressif/esp-protocols/commit/d0bbe88), [IDF@5d3f815](https://github.com/espressif/esp-idf/commit/5d3f8157e0e481363ef93d54a29d957fc91cca86)) +- Build & config: Remove leftover files from the unsupported "make" build system ([4a9d55e](https://github.com/espressif/esp-protocols/commit/4a9d55e), [IDF@766aa57](https://github.com/espressif/esp-idf/commit/766aa5708443099f3f033b739cda0e1de101cca6)) +- Build & config: Remove the "make" build system ([be2a924](https://github.com/espressif/esp-protocols/commit/be2a924), [IDF@9c1d4f5](https://github.com/espressif/esp-idf/commit/9c1d4f5b549d6a7125e5c7c323c80d37361991cb)) +- freertos: update freertos folder structure to match upstream ([76fcd41](https://github.com/espressif/esp-protocols/commit/76fcd41), [IDF@4846222](https://github.com/espressif/esp-idf/commit/48462221029c7da4b1ea233e9e781cd57ff91c7e)) +- support service subtype ([fd8499c](https://github.com/espressif/esp-protocols/commit/fd8499c), [IDF#5508](https://github.com/espressif/esp-idf/issues/5508), [IDF@e7e8610](https://github.com/espressif/esp-idf/commit/e7e8610f563e0b8532a093ea8b803f0eb132fd0e)) +- Fix parsing non-standard queries ([38b4fe2](https://github.com/espressif/esp-protocols/commit/38b4fe2), [IDF#7694](https://github.com/espressif/esp-idf/issues/7694), [IDF@d16f9ba](https://github.com/espressif/esp-idf/commit/d16f9bade5beab3785677dd5b39ebc4e9c895008)) +- allow mutiple instances with same service type ([b266062](https://github.com/espressif/esp-protocols/commit/b266062), [IDF@b7a99f4](https://github.com/espressif/esp-idf/commit/b7a99f46587a69a2cd07e7616c3bb30b7b1a6edf)) +- Update copyright header ([5e087d8](https://github.com/espressif/esp-protocols/commit/5e087d8), [IDF@2a2b95b](https://github.com/espressif/esp-idf/commit/2a2b95b9c22bc5090d87a4f4317288b64b14fcd9)) +- Fix potential null dereference identified by fuzzer tests ([91a3d95](https://github.com/espressif/esp-protocols/commit/91a3d95), [IDF@e7dabb1](https://github.com/espressif/esp-idf/commit/e7dabb14f7c8fd9bd2bea55d8f1accc65323a1c0)) +- components/bt: move config BT_RESERVE_DRAM from bluedroid to ESP32 controller ([6d6dd2b](https://github.com/espressif/esp-protocols/commit/6d6dd2b), [IDF@b310c06](https://github.com/espressif/esp-idf/commit/b310c062cd25f249e00dd03dd27baed783921630)) +- add notification callback for async APIs ([52306e9](https://github.com/espressif/esp-protocols/commit/52306e9), [IDF@986603c](https://github.com/espressif/esp-idf/commit/986603cf07413b46c88c76c324bf500edcfb6171)) +- add more mdns result attributes ([d37ab6d](https://github.com/espressif/esp-protocols/commit/d37ab6d), [IDF@76ec76c](https://github.com/espressif/esp-idf/commit/76ec76c12c871554147343bb7141da1e5de58011)) +- Add host test using linux target ([5c55ea6](https://github.com/espressif/esp-protocols/commit/5c55ea6), [IDF@fc7e2d9](https://github.com/espressif/esp-idf/commit/fc7e2d9e908f61fb4b852cfae72aa5ff7c662ebc)) +- Implement mdns_networking using BSD sockets ([0c71c7b](https://github.com/espressif/esp-protocols/commit/0c71c7b), [IDF@73dfe84](https://github.com/espressif/esp-idf/commit/73dfe84bf295a850edfad39b6b097a71f15964dc)) +- fix crash when adding services without hostname set ([4c368c0](https://github.com/espressif/esp-protocols/commit/4c368c0), [IDF@5e98772](https://github.com/espressif/esp-idf/commit/5e98772eaf7e50d96cf2e6ecdfedcd928b61c864)) +- Fix fuzzer IDF-mock layer ([af22753](https://github.com/espressif/esp-protocols/commit/af22753), [IDF@619235c](https://github.com/espressif/esp-idf/commit/619235c2ee5a1fe8411bd2be2de8798209f95902)) +- Clean the main mdns module from lwip dependencies ([b0957e7](https://github.com/espressif/esp-protocols/commit/b0957e7), [IDF@54e3294](https://github.com/espressif/esp-idf/commit/54e329444a5dd19c51e84b5f1e16455a0f1c6225)) +- Add asynchronous query API ([47c7266](https://github.com/espressif/esp-protocols/commit/47c7266), [IDF#7090](https://github.com/espressif/esp-idf/issues/7090), [IDF@d81482d](https://github.com/espressif/esp-idf/commit/d81482d699232b22f4a5cbee2a76199a5285dadb)) +- Fix crashes reported by the fuzzer tests ([40da0d2](https://github.com/espressif/esp-protocols/commit/40da0d2), [IDF@4a2e726](https://github.com/espressif/esp-idf/commit/4a2e72677c6fb7681a7e2acd1a878d3deb114079)) +- mdns/fuzzer: Fix non-instrumentation test to reproduce fuzzer issues ([5f6b6f9](https://github.com/espressif/esp-protocols/commit/5f6b6f9), [IDF@dae8033](https://github.com/espressif/esp-idf/commit/dae803335e6bc6d9751a360cd3f675ce4027853b)) +- return ESP_OK rather than ERR_OK in API functions ([8a12082](https://github.com/espressif/esp-protocols/commit/8a12082), [IDF@2386113](https://github.com/espressif/esp-idf/commit/2386113972ee51ea93e9740d8c34bfe9289ce909)) +- fix memory leak in mdns_free when adding delegated hostnames ([46f28a8](https://github.com/espressif/esp-protocols/commit/46f28a8), [IDF@0baee93](https://github.com/espressif/esp-idf/commit/0baee932111268c4a2103e1c1adeb7d99914a937)) +- Support for One-Shot mDNS queries ([5a81eae](https://github.com/espressif/esp-protocols/commit/5a81eae), [IDF@f167238](https://github.com/espressif/esp-idf/commit/f167238fac37818aed75dc689eed54ad47528ab9)) +- allow explicit txt value length ([2ddaee2](https://github.com/espressif/esp-protocols/commit/2ddaee2), [IDF@b4e0088](https://github.com/espressif/esp-idf/commit/b4e0088b68321acc4698b01faec7e2ffbe1e37c1)) +- Fix crashes reported by the fuzzer ([27fc285](https://github.com/espressif/esp-protocols/commit/27fc285), [IDF@79ba738](https://github.com/espressif/esp-idf/commit/79ba738626d643d8c6f32bdcd455e0d2476f94c7)) +- Minor correction of the test code ([93e6efe](https://github.com/espressif/esp-protocols/commit/93e6efe), [IDF@7d76245](https://github.com/espressif/esp-idf/commit/7d762451731cb305c3b090509827740f0195a496)) +- Fix fuzzer from miss-interpreting adding services as timeouts ([bc4cda8](https://github.com/espressif/esp-protocols/commit/bc4cda8), [IDF@14099fe](https://github.com/espressif/esp-idf/commit/14099fe15efb1b0cde0a8370096c55bba62ff937)) +- fix test script delayed response ([8a8d58d](https://github.com/espressif/esp-protocols/commit/8a8d58d), [IDF@a4f2639](https://github.com/espressif/esp-idf/commit/a4f263948c35c13340b6f4b59a649c5073787d5e)) +- fix wrong SRV/PTR record handling ([402baeb](https://github.com/espressif/esp-protocols/commit/402baeb), [IDF@e613555](https://github.com/espressif/esp-idf/commit/e6135552d26480e39e11632437020535b1667b7a)) +- fix wrong service hostname after mangling ([9fa25ef](https://github.com/espressif/esp-protocols/commit/9fa25ef), [IDF@439b31d](https://github.com/espressif/esp-idf/commit/439b31d065eddfdfb6eb4cf9c00454edfebc3d9b)) +- fix empty address change announce packets ([121b525](https://github.com/espressif/esp-protocols/commit/121b525), [IDF@7bbb72d](https://github.com/espressif/esp-idf/commit/7bbb72d86540f04d37b0e2c4efb6dc66ee9c9ea0)) +- fix mdns probe/reply behavior ([418fb60](https://github.com/espressif/esp-protocols/commit/418fb60), [IDF@d2a5d25](https://github.com/espressif/esp-idf/commit/d2a5d25984432d149ca31aea4a0d177f3509dd7b)) +- make delegate host address a list ([4049b3b](https://github.com/espressif/esp-protocols/commit/4049b3b), [IDF@2d34352](https://github.com/espressif/esp-idf/commit/2d34352f3db0fa71366a838933a29138a90eb2af)) +- add remove delegate host api ([c882119](https://github.com/espressif/esp-protocols/commit/c882119), [IDF@2174693](https://github.com/espressif/esp-idf/commit/2174693096b73ce93261611c44ecba647cd01859)) +- add mdns delegation ([1eb5df9](https://github.com/espressif/esp-protocols/commit/1eb5df9), [IDF@401ff56](https://github.com/espressif/esp-idf/commit/401ff56cc1ad1d11284143a348cc0c0e4a363e98)) +- fix memory free issue when repeating the query in reply ([b62b4b3](https://github.com/espressif/esp-protocols/commit/b62b4b3), [IDF@5f244c8](https://github.com/espressif/esp-idf/commit/5f244c86f29da46c17610563a245d1663a46b439)) +- Fix of crash when wifi interface get deleted and mdns receives the packets ([4d8aec1](https://github.com/espressif/esp-protocols/commit/4d8aec1), [IDF#6973](https://github.com/espressif/esp-idf/issues/6973), [IDF@03de74a](https://github.com/espressif/esp-idf/commit/03de74a728d4b278f55e1fc30e0425483b806e80)) +- Docs: Added README.md for lwip fuzzer tests ([6d64910](https://github.com/espressif/esp-protocols/commit/6d64910), [IDF@53c18a8](https://github.com/espressif/esp-idf/commit/53c18a85db104bb37ebeadec2faf5d42d764d0f9)) +- Fixed the ip header TTL to be correctly set to 255 ([ab3fa69](https://github.com/espressif/esp-protocols/commit/ab3fa69), [IDF@5cce919](https://github.com/espressif/esp-idf/commit/5cce919cbef87f543bb9f5275b77b97b3b1ea67e)) +- Fix parsing answers with questions when instance name not set ([c3a5826](https://github.com/espressif/esp-protocols/commit/c3a5826), [IDF#6598](https://github.com/espressif/esp-idf/issues/6598), [IDF@3404945](https://github.com/espressif/esp-idf/commit/34049454dfaf5132d9b258ef4d04921befc8997b)) +- Fix the resolver to correctly parse it's own non-strict answers ([cbcbe4f](https://github.com/espressif/esp-protocols/commit/cbcbe4f), [IDF@b649603](https://github.com/espressif/esp-idf/commit/b649603a0d70ec804567f57752c3eddaed56198f)) +- Add MDNS_STRICT_MODE config option ([adc3430](https://github.com/espressif/esp-protocols/commit/adc3430), [IDF@0eee315](https://github.com/espressif/esp-idf/commit/0eee31546dd4e6df0d1c1cc2740da0675dffb4bf)) +- freertos: common config header ([c30617d](https://github.com/espressif/esp-protocols/commit/c30617d), [IDF@39cf818](https://github.com/espressif/esp-idf/commit/39cf818838b0259b3e00b3c198ad47b4add41939)) +- Removed freeRTOS dependancies from fuzzer tests ([1e5eeb1](https://github.com/espressif/esp-protocols/commit/1e5eeb1), [IDF@5571694](https://github.com/espressif/esp-idf/commit/55716945a9908e057743d69e1d59399df03e49bd)) +- mDNS: Updated APIs description and shows the warning when hostname contains domain name during the query ([22c7c0a](https://github.com/espressif/esp-protocols/commit/22c7c0a), [IDF#6590](https://github.com/espressif/esp-idf/issues/6590), [IDF@9f8d2b9](https://github.com/espressif/esp-idf/commit/9f8d2b944d2b3736a012e0dff1a8459b6941d295)) +- components: Use CONFIG_LWIP_IPV6 to strip IPv6 function in components ([1623c0e](https://github.com/espressif/esp-protocols/commit/1623c0e), [IDF@da58235](https://github.com/espressif/esp-idf/commit/da58235a0ee262ff552c5f1155d531b5c31e8de6)) +- add bound check when setting interface as duplicate ([b114ed6](https://github.com/espressif/esp-protocols/commit/b114ed6), [IDF@2b9d2c0](https://github.com/espressif/esp-idf/commit/2b9d2c06f54924b680c41ae641978c8d81612f65)) +- mDNS: Fix of text length calculation when detecting a collision ([2ffd223](https://github.com/espressif/esp-protocols/commit/2ffd223), [IDF@be0ae1e](https://github.com/espressif/esp-idf/commit/be0ae1ebbbe9fae6ecf7de09e8d50cba063b61f4)) +- global: fix sign-compare warnings ([1fe901f](https://github.com/espressif/esp-protocols/commit/1fe901f), [IDF@753a929](https://github.com/espressif/esp-idf/commit/753a9295259126217a9fe6ef1c5e9da21e9b4e28)) +- lwip: Moved default SNTP API to esp_sntp.h ([2cf9fd8](https://github.com/espressif/esp-protocols/commit/2cf9fd8), [IDF@76f6dd6](https://github.com/espressif/esp-idf/commit/76f6dd6214ca583b1a94c7c553ccac739a27f6d5)) +- Allow resolve its own non-strict answers ([89439e0](https://github.com/espressif/esp-protocols/commit/89439e0), [IDF#6190](https://github.com/espressif/esp-idf/issues/6190), [IDF@0693e17](https://github.com/espressif/esp-idf/commit/0693e172de392086b9bfd8cf1474d8d133af3298)) +- mDNS: Fix of collision detection during txt length calculation ([becd5d0](https://github.com/espressif/esp-protocols/commit/becd5d0), [IDF#6114](https://github.com/espressif/esp-idf/issues/6114), [IDF@f33772c](https://github.com/espressif/esp-idf/commit/f33772c96037c795366e60082bdbbefe2a69165f)) +- esp32c3: Apply one-liner/small changes for ESP32-C3 ([0d7a309](https://github.com/espressif/esp-protocols/commit/0d7a309), [IDF@5228d9f](https://github.com/espressif/esp-idf/commit/5228d9f9ced16118d87326f94d9f9dfd411e0be9)) +- test: fix several test build error ([1fdffbb](https://github.com/espressif/esp-protocols/commit/1fdffbb), [IDF@b7ecccd](https://github.com/espressif/esp-idf/commit/b7ecccd9010f1deaba83de54374231c3c7f5b472)) +- freertos: Add RISC-V port ([988d120](https://github.com/espressif/esp-protocols/commit/988d120), [IDF@87e13ba](https://github.com/espressif/esp-idf/commit/87e13baaf12fe6deae715d95e912a310fea4ba88)) +- Fix wrong mdns source address if lwIP IPv6 zones disabled ([fd47df3](https://github.com/espressif/esp-protocols/commit/fd47df3), [IDF@7ac9761](https://github.com/espressif/esp-idf/commit/7ac97616c119e4d2f4cdd377dfc5abbf75ec5e30)) +- Whitespace: Automated whitespace fixes (large commit) ([2cb3a6e](https://github.com/espressif/esp-protocols/commit/2cb3a6e), [IDF@66fb5a2](https://github.com/espressif/esp-idf/commit/66fb5a29bbdc2482d67c52e6f66b303378c9b789)) +- test_compile_fuzzers: Fix include paths for host build ([825652f](https://github.com/espressif/esp-protocols/commit/825652f), [IDF@98a0cc7](https://github.com/espressif/esp-idf/commit/98a0cc783f701b238bea232b53250a538d34920a)) +- CI: Add a test to pre-check fuzzer tests compilation before weekly run ([fc53888](https://github.com/espressif/esp-protocols/commit/fc53888), [IDF@637f5c0](https://github.com/espressif/esp-idf/commit/637f5c0a6842c42ee6cf7f41d3c5ae0cb28a68af)) +- soc: descriptive part occupy whole component ([7635c04](https://github.com/espressif/esp-protocols/commit/7635c04), [IDF@79887fd](https://github.com/espressif/esp-idf/commit/79887fdc6c3d9a2e509cc189bb43c998d3f0f4ee)) +- Coredump config option rename throughout IDF ([d5fe42b](https://github.com/espressif/esp-protocols/commit/d5fe42b), [IDF@20af94f](https://github.com/espressif/esp-idf/commit/20af94ff53c5147a76342800d007a6c49be50a7b)) +- mdns, dns, dhcp, dhcps: update fuzzer test to work in CI ([e0bc60a](https://github.com/espressif/esp-protocols/commit/e0bc60a), [IDF@a43c06a](https://github.com/espressif/esp-idf/commit/a43c06a592bcf9404297b22268c33bb7a246632c)) +- cmock: added cmock as component ([9772e49](https://github.com/espressif/esp-protocols/commit/9772e49), [IDF@20c068e](https://github.com/espressif/esp-idf/commit/20c068ef3b49999387896b90f8011b02f718485f)) +- Support queries in responses in mDNS non-strict mode ([6021a88](https://github.com/espressif/esp-protocols/commit/6021a88), [IDF#5521](https://github.com/espressif/esp-idf/issues/5521), [IDF@bcfa36d](https://github.com/espressif/esp-idf/commit/bcfa36db8ffff997f1f95eaf6b011ffc4d46a10f)) +- Fix include query ID in reponses ([78f71ec](https://github.com/espressif/esp-protocols/commit/78f71ec), [IDF#5574](https://github.com/espressif/esp-idf/issues/5574), [IDF@f62e321](https://github.com/espressif/esp-idf/commit/f62e321d87c1d520cccca951715c27730e06607a)) +- Allow config mDNS task stack size ([3319844](https://github.com/espressif/esp-protocols/commit/3319844), [IDF@cf7e48c](https://github.com/espressif/esp-idf/commit/cf7e48c779edd84c3f99d5e8ed81027932302382)) +- Remove mbedtls dependency ([ac70c9a](https://github.com/espressif/esp-protocols/commit/ac70c9a), [IDF@f4a4549](https://github.com/espressif/esp-idf/commit/f4a4549a344e7ff2444a188adbebbc136b47a7bb)) +- limit the GOT_IP6_EVENT to only known network interfaces ([2b7d43e](https://github.com/espressif/esp-protocols/commit/2b7d43e), [IDF@ab8cab1](https://github.com/espressif/esp-idf/commit/ab8cab1c553ee5312ef47a7dea002f2585605006)) +- esp32: add implementation of esp_timer based on TG0 LAC timer ([4eb3e89](https://github.com/espressif/esp-protocols/commit/4eb3e89), [IDF@739eb05](https://github.com/espressif/esp-idf/commit/739eb05bb97736b70507e7ebcfee58e670672d23)) +- fixed typos in the variable names and the comments ([b5e5a64](https://github.com/espressif/esp-protocols/commit/b5e5a64), [IDF@ecca39e](https://github.com/espressif/esp-idf/commit/ecca39e19f663e32e16aef2a09df15443de347e9)) +- fix preset of esp_netif ptr for local interfaces ([6713ffe](https://github.com/espressif/esp-protocols/commit/6713ffe), [IDF@09e36f9](https://github.com/espressif/esp-idf/commit/09e36f9f3354092b2a528baaaaccab28ff4774d6)) +- fixed crash on event during deinit ([817c4fd](https://github.com/espressif/esp-protocols/commit/817c4fd), [IDF@eaa2f12](https://github.com/espressif/esp-idf/commit/eaa2f12d6761710d2633b4934fe09f6f45e20f4f)) +- respond to discovery with the proper pseudo name _services._dns-sd._udp ([8f0dc6d](https://github.com/espressif/esp-protocols/commit/8f0dc6d), [IDF#4369](https://github.com/espressif/esp-idf/issues/4369), [IDF@de17a14](https://github.com/espressif/esp-idf/commit/de17a1487f8ba6f432b06199f2261132ec6e735f)) +- fixed forgotten merge conflicts in debug code ([d20666f](https://github.com/espressif/esp-protocols/commit/d20666f), [IDF@d9433ef](https://github.com/espressif/esp-idf/commit/d9433ef69223a32d05abdca543fb530f2e6679e4)) +- add missing include of esp_task.h ([662a4ce](https://github.com/espressif/esp-protocols/commit/662a4ce), [IDF@5884b80](https://github.com/espressif/esp-idf/commit/5884b80908d680874e27fa0c8b2df85b69d03dd3)) +- add configuration values for task priority, affinity and internal service timeouts ([fb1de80](https://github.com/espressif/esp-protocols/commit/fb1de80), [IDF@c6f38f0](https://github.com/espressif/esp-idf/commit/c6f38f04f8eec1aae937cc87c111609772681cb3)) +- tcpip_adapter: updated tcpip_adapter compatablity layer to include all public API and keep 100% backward compatibility update build of tcpip adapter when ethernet disabled ([1f35e9a](https://github.com/espressif/esp-protocols/commit/1f35e9a), [IDF@7f5cda1](https://github.com/espressif/esp-idf/commit/7f5cda1b825586903f85dc4ad7736b35712e46d7)) +- update mdns to use esp-netif for mdns supported services such as STA, AP, ETH ([48b819b](https://github.com/espressif/esp-protocols/commit/48b819b), [IDF@19e24fe](https://github.com/espressif/esp-idf/commit/19e24fe61ed5ea6698dfd5e1f427e783360aa846)) +- esp_netif: Introduction of esp-netif component as a replacement of tcpip_adpter ([53e2aa3](https://github.com/espressif/esp-protocols/commit/53e2aa3), [IDF@ffe043b](https://github.com/espressif/esp-idf/commit/ffe043b1a81a0f9e1cc2cfa8873e21318ec89143)) +- examples: removed ip4addr_ntoa and used prefered IP2STR for displaying IP addresses ([3cc6446](https://github.com/espressif/esp-protocols/commit/3cc6446), [IDF@ec9f245](https://github.com/espressif/esp-idf/commit/ec9f245dd35d3e8e7b19a8dec5e05e003dc21f39)) +- esp_event, mdns: fixes for CONFIG_ETH_ENABLED=n ([248b11b](https://github.com/espressif/esp-protocols/commit/248b11b), [IDF@569ad75](https://github.com/espressif/esp-idf/commit/569ad7545c32a2f1d0eff3f1e81df70fb76ad125)) +- build and link hello-world for esp32s2beta ([901124b](https://github.com/espressif/esp-protocols/commit/901124b), [IDF@84b2f9f](https://github.com/espressif/esp-idf/commit/84b2f9f14d16533c84db2210f13a24cd817e0b0a)) +- fix crash for hostname queries ([f6ff165](https://github.com/espressif/esp-protocols/commit/f6ff165), [IDF#4224](https://github.com/espressif/esp-idf/issues/4224), [IDF@3d11700](https://github.com/espressif/esp-idf/commit/3d1170031b340a231949fdc0d9c46d87af0d1b5d)) +- fix possible race condition when checking DHCP status on WIFI_EVENT_STA_CONNECTED event. ([f44c569](https://github.com/espressif/esp-protocols/commit/f44c569), [IDF@7f410a0](https://github.com/espressif/esp-idf/commit/7f410a0bcbafa85dba05807c53c3c38999506509)) +- use constant size of AAAA answer in mdns packets instead of deriving from lwip struct size, since the struct could contain also zones ([286c646](https://github.com/espressif/esp-protocols/commit/286c646), [IDF@e5e31c5](https://github.com/espressif/esp-idf/commit/e5e31c5d0172d68fd207fa31cc5d3bba82dad020)) +- esp_wifi: wifi support new event mechanism ([c70d527](https://github.com/espressif/esp-protocols/commit/c70d527), [IDF@003a987](https://github.com/espressif/esp-idf/commit/003a9872b7de69d799e9d37521cfbcaff9b37e85)) +- fix missing bye packet if services removed with mdns_service_remove_all() or mdns_free() ([7cdf96c](https://github.com/espressif/esp-protocols/commit/7cdf96c), [IDF#3660](https://github.com/espressif/esp-idf/issues/3660), [IDF@a001998](https://github.com/espressif/esp-idf/commit/a001998d5283b29ca9a374adf7cef3357b39a03a)) +- mdns_service_remove_all doesn't take an argument ([407875d](https://github.com/espressif/esp-protocols/commit/407875d), [IDF@c2764f6](https://github.com/espressif/esp-idf/commit/c2764f6fe85681cfaf5dbbe168295284f09c09cd)) +- tools: Mass fixing of empty prototypes (for -Wstrict-prototypes) ([3e753f5](https://github.com/espressif/esp-protocols/commit/3e753f5), [IDF@afbaf74](https://github.com/espressif/esp-idf/commit/afbaf74007e89d016dbade4072bf2e7a3874139a)) +- fix ignoring mdns packet with some invalid name entries in question field ([144d4ad](https://github.com/espressif/esp-protocols/commit/144d4ad), [IDF@4bd4c7c](https://github.com/espressif/esp-idf/commit/4bd4c7caf3f9ef8402c5a27ab44561537407eb60)) +- add esp_eth component ([680bad6](https://github.com/espressif/esp-protocols/commit/680bad6), [IDF@90c4827](https://github.com/espressif/esp-idf/commit/90c4827bd22aa61894a5b22b3b39247a7e44d6cf)) +- components: use new component registration api ([7fb6686](https://github.com/espressif/esp-protocols/commit/7fb6686), [IDF@9eccd7c](https://github.com/espressif/esp-idf/commit/9eccd7c0826d6cc2e9de59304d1e5f76c0063ccf)) +- fix static analysis warnings ([4912bef](https://github.com/espressif/esp-protocols/commit/4912bef), [IDF@c34de4c](https://github.com/espressif/esp-idf/commit/c34de4cba658e8331f8a3ab2f466190c7640595b)) +- added initial suite of api unit tests ([181a22e](https://github.com/espressif/esp-protocols/commit/181a22e), [IDF@e680191](https://github.com/espressif/esp-idf/commit/e6801912c5c4861f828ab1f447280628bba9a5d7)) +- mdns tests: adapt mdns fuzzer test to compile with event loop library ([4172219](https://github.com/espressif/esp-protocols/commit/4172219), [IDF@38d15cb](https://github.com/espressif/esp-idf/commit/38d15cbd637e8b8baacda9fc43e8e99d224530f5)) +- fixed mdns crashing on reception of txt packet without a corresponding service closes #2866 ([98d2c1a](https://github.com/espressif/esp-protocols/commit/98d2c1a), [IDF@af48977](https://github.com/espressif/esp-idf/commit/af48977f21cea6b18dae10b2c8b64a78acfc647f)) +- use const char* for mdns txt items types to remove warning when assigning ([84cbb1f](https://github.com/espressif/esp-protocols/commit/84cbb1f), [IDF@c050a75](https://github.com/espressif/esp-idf/commit/c050a75616803c7871ef11c060e440fae09000d9)) +- updated doxygen comments documenting mdns api ([4c6818e](https://github.com/espressif/esp-protocols/commit/4c6818e), [IDF#1718](https://github.com/espressif/esp-idf/issues/1718), [IDF@a851aac](https://github.com/espressif/esp-idf/commit/a851aac255311124529f504486ca55bad15c1951)) +- update mdns_out_question_s to be in line with mdns_parsed_question_s struct ([c440114](https://github.com/espressif/esp-protocols/commit/c440114), [IDF#1568]( https://github.com/espressif/esp-idf/issues/1568), [IDF@eddd5c4](https://github.com/espressif/esp-idf/commit/eddd5c4f2c686d9a1d6d3258569cc33752e78880)) +- use esp_event library to handle events ([6ea0ea9](https://github.com/espressif/esp-protocols/commit/6ea0ea9), [IDF@a2d5952](https://github.com/espressif/esp-idf/commit/a2d59525e53099ee1ad63c3d60ff853f573ab535)) +- fuzzer tests: update of mdns and lwip host compilation for fuzzer testing ([d9aec9f](https://github.com/espressif/esp-protocols/commit/d9aec9f), [IDF@bc60bbb](https://github.com/espressif/esp-idf/commit/bc60bbbeaf89f2bbfc5db4bd4f1e7ace81a2ab37)) +- fix possible crash when probing on particular interface with duplicated service instances due to naming conflicts on network ([985e691](https://github.com/espressif/esp-protocols/commit/985e691), [IDF@265e983](https://github.com/espressif/esp-idf/commit/265e983a452a7eaefc1662cdc0e6ed839a37fe1a)) +- enable pcbs before starting service thread to avoid updating pcb's internal variables from concurent tasks ([75deebb](https://github.com/espressif/esp-protocols/commit/75deebb), [IDF@c87f0cb](https://github.com/espressif/esp-idf/commit/c87f0cb6cad3c36b077f4aaeb1ca52fe6ed0cdaf)) +- fix possible deadlock on mdns deinit calling mdns_free() ([fdd27dc](https://github.com/espressif/esp-protocols/commit/fdd27dc), [IDF#1696](https://github.com/espressif/esp-idf/issues/1696), [IDF@48b5501](https://github.com/espressif/esp-idf/commit/48b5501c250ed90da51a55ad4fc18e09f466a517)) +- mdsn: fix race condition in updating packet data from user task when failed to allocate or queue a new service ([2ec3b55](https://github.com/espressif/esp-protocols/commit/2ec3b55), [IDF@021dc5d](https://github.com/espressif/esp-idf/commit/021dc5d453e21e2d1707f194668e69cf63ef4e84)) +- fix possible crash when packet scheduled to transmit contained service which might have been already removed ([450cbf0](https://github.com/espressif/esp-protocols/commit/450cbf0), [IDF@67051a2](https://github.com/espressif/esp-idf/commit/67051a286ba60a01d4755c3682129153c2f95953)) +- use binary semaphore instead of mutex when searching ([34f6d8d](https://github.com/espressif/esp-protocols/commit/34f6d8d), [IDF@eef0b50](https://github.com/espressif/esp-idf/commit/eef0b5090aee87efef1a6a37772b3b88c9ce8df8)) +- fix memory leak in pbuf if tcpipadapter failed to get netif ([b6efc68](https://github.com/espressif/esp-protocols/commit/b6efc68), [IDF@8462751](https://github.com/espressif/esp-idf/commit/8462751f95a3ff18bdc1b01d02fabd1829fd9135)) +- fix malfuctional query_txt ([90e4bab](https://github.com/espressif/esp-protocols/commit/90e4bab), [IDF@1a02773](https://github.com/espressif/esp-idf/commit/1a027734af06abf08fcb1c34ee65bdf50d12be4d)) +- fix possible crash when mdns_free called while action queue not empty ([c546ab8](https://github.com/espressif/esp-protocols/commit/c546ab8), [IDF@206b47c](https://github.com/espressif/esp-idf/commit/206b47c03aca0acdf40d1d9c250e18aeddfe1bd7)) +- fix memory leak when query for service receives multiple ptr entries for one instance ([6582b41](https://github.com/espressif/esp-protocols/commit/6582b41), [IDF@9a4da97](https://github.com/espressif/esp-idf/commit/9a4da97fb4b3c241998cb969a08c3a917ffb4cd1)) +- fix crash after init if no memory for task ([358d26c](https://github.com/espressif/esp-protocols/commit/358d26c), [IDF@a47768d](https://github.com/espressif/esp-idf/commit/a47768dc4e4750fd7e1c29b15d6e2dd3c76e6591)) +- fixed crash on free undefined ptr after skipped strdup ([2ac83d0](https://github.com/espressif/esp-protocols/commit/2ac83d0), [IDF@e0a8044](https://github.com/espressif/esp-idf/commit/e0a8044a16907e642001b883469618a999dbe6db)) +- Correct Kconfigs according to the coding style ([98e3171](https://github.com/espressif/esp-protocols/commit/98e3171), [IDF@37126d3](https://github.com/espressif/esp-idf/commit/37126d3451eabb44eeeb48b8e2ee554dc233e2a8)) +- fix networking running udp_sendif from lwip thread ([2f85c07](https://github.com/espressif/esp-protocols/commit/2f85c07), [IDF@f7d4a4b](https://github.com/espressif/esp-idf/commit/f7d4a4be6a9e0b0ac5edb9400d3b123dbbed2ffc)) +- fixed static memory leak ([b30a7fe](https://github.com/espressif/esp-protocols/commit/b30a7fe), [IDF@6bb68a5](https://github.com/espressif/esp-idf/commit/6bb68a5a7567a94c3605136d44960ff060c74663)) +- check all mallocs for failure and add default hook to log error with free heap ([7a4fdad](https://github.com/espressif/esp-protocols/commit/7a4fdad), [IDF@c8cb4cd](https://github.com/espressif/esp-idf/commit/c8cb4cd3c8eb56d5901ade03302ad1231d7f3de5)) +- resolve memory leak when txt record received multiple times ([b4e5742](https://github.com/espressif/esp-protocols/commit/b4e5742), [IDF@a6b2b73](https://github.com/espressif/esp-idf/commit/a6b2b73f03bbb75a39685ddba6cf877fd1e5e6d7)) +- skip sending search when finished, not properly locked timer task ([2763bcd](https://github.com/espressif/esp-protocols/commit/2763bcd), [IDF@31163f0](https://github.com/espressif/esp-idf/commit/31163f02d5c414d8b492dce6f729b43a0061581b)) +- sending search packets also in probing and announcing state ([8cd0e8a](https://github.com/espressif/esp-protocols/commit/8cd0e8a), [IDF@d16762a](https://github.com/espressif/esp-idf/commit/d16762a036e35ce86ece86bb44e6e99f9cc7c431)) +- fixed crashes on network changes ([9b3b41c](https://github.com/espressif/esp-protocols/commit/9b3b41c), [IDF@097282a](https://github.com/espressif/esp-idf/commit/097282a8e3f85958747430d9931ce0a545d37700)) +- Update network code for mDNS to work with newest LwIP ([ea23007](https://github.com/espressif/esp-protocols/commit/ea23007), [IDF@3ec0e7e](https://github.com/espressif/esp-idf/commit/3ec0e7e2d2ddea70e9f8fb5025664d0fe24c301a)) +- bugfix: mdns_service_txt_set() wasn't allocating memory for TXT records ([0c17121](https://github.com/espressif/esp-protocols/commit/0c17121), [IDF@e5e2702](https://github.com/espressif/esp-idf/commit/e5e2702ca3f63a29da57eb138f75a20c74fb2a94)) +- cmake: make main a component again ([67173f6](https://github.com/espressif/esp-protocols/commit/67173f6), [IDF@d9939ce](https://github.com/espressif/esp-idf/commit/d9939cedd9b44d63dc148354c3a0a139b9c7113d)) +- Feature/sync lwip as submodule ([fed787f](https://github.com/espressif/esp-protocols/commit/fed787f), [IDF@3578fe3](https://github.com/espressif/esp-idf/commit/3578fe39e01ba0c2d54824ac70c3276502661c6b)) +- Fix a portion of the queries are issued with the wildcard query type ([b4ab30b](https://github.com/espressif/esp-protocols/commit/b4ab30b), [IDF@f3f0445](https://github.com/espressif/esp-idf/commit/f3f0445f4db7c9ad97ae10a9728767337aa7bb62)) +- added CI job for AFL fuzzer tests ([dd71494](https://github.com/espressif/esp-protocols/commit/dd71494), [IDF@0c14764](https://github.com/espressif/esp-idf/commit/0c147648f7642d058b63fbe2ddd5de31c2326304)) +- Minor fix for mdns_service_remove() ([39de491](https://github.com/espressif/esp-protocols/commit/39de491), [IDF@5c7eb7e](https://github.com/espressif/esp-idf/commit/5c7eb7e27be7508130459d896cf7d13ffefda87f)) +- Replace all DOS line endings with Unix ([19acac7](https://github.com/espressif/esp-protocols/commit/19acac7), [IDF@a67d5d8](https://github.com/espressif/esp-idf/commit/a67d5d89e0e90390fa7ff02816a6a79008d75d40)) +- remove executable permission from source files ([98069f9](https://github.com/espressif/esp-protocols/commit/98069f9), [IDF@cb649e4](https://github.com/espressif/esp-idf/commit/cb649e452f3c64a7db1f4a61e162a16b70248204)) +- Fixed nullptr dereference in MDNS.c ([ad29d34](https://github.com/espressif/esp-protocols/commit/ad29d34), [IDF@fffbf7b](https://github.com/espressif/esp-idf/commit/fffbf7b75065b5852e064e04b0c5102dd0fc2244)) +- MDNS-Fuzzer: AFL fuzzer tests for mdsn packet parser ([9f1be36](https://github.com/espressif/esp-protocols/commit/9f1be36), [IDF@e983230](https://github.com/espressif/esp-idf/commit/e983230be933fb83cebdd1945ba6539a7dc99b28)) +- cmake: Add component dependency support ([c7701d4](https://github.com/espressif/esp-protocols/commit/c7701d4), [IDF@1cb5712](https://github.com/espressif/esp-idf/commit/1cb5712463a8963cd3e8331da90fb5e03f13575f)) +- cmake: Remove defaults for COMPONENT_SRCDIRS, COMPONENT_SRCS, COMPONENT_ADD_INCLUDEDIRS ([f1ccc40](https://github.com/espressif/esp-protocols/commit/f1ccc40), [IDF@4f1a856](https://github.com/espressif/esp-idf/commit/4f1a856dbfd752336cd71730105e02ad8c045541)) +- build system: Initial cmake support, work in progress ([84bd1d7](https://github.com/espressif/esp-protocols/commit/84bd1d7), [IDF@c671a0c](https://github.com/espressif/esp-idf/commit/c671a0c3ebf90f18576d6db55b51b62730c58301)) +- fix the bug that in mdns test code redefine esp_err_t to uint32_t, which should be int32_t ([259d3fc](https://github.com/espressif/esp-protocols/commit/259d3fc), [IDF@81e4cad](https://github.com/espressif/esp-idf/commit/81e4cad61593cde879a5c44a08060d9d336e5a3f)) +- Fix exception when service is removed while there are pending packets that depend on it ([7784d00](https://github.com/espressif/esp-protocols/commit/7784d00), [IDF@421c6f1](https://github.com/espressif/esp-idf/commit/421c6f154b10d9253b78875ab28ee6bdcaaaf3c0)) +- Fix case where service is NULL and that will cause exception ([bce7d52](https://github.com/espressif/esp-protocols/commit/bce7d52), [IDF@4fa130a](https://github.com/espressif/esp-idf/commit/4fa130ae4fb5de99ddddc5a7bed7e26ae645591c)) +- Fix issue with some mDNS parsers ([ef924f1](https://github.com/espressif/esp-protocols/commit/ef924f1), [IDF@51dde19](https://github.com/espressif/esp-idf/commit/51dde19a765533af67fc7be21f116641773a9be4)) +- Import mDNS changes ([ad8c92d](https://github.com/espressif/esp-protocols/commit/ad8c92d), [IDF@4bddbc0](https://github.com/espressif/esp-idf/commit/4bddbc031cee83935c0e4df1dc72cc7000c97ba5)) +- Fix compilation errors when using gcc-7.2.0 for the crosstool-ng toolchain ([3aa605f](https://github.com/espressif/esp-protocols/commit/3aa605f), [IDF@519edc3](https://github.com/espressif/esp-idf/commit/519edc332dae0160069fd790467cde8de78f1a0e)) +- components/mdns: wrong Message compression detect ([00a72b8](https://github.com/espressif/esp-protocols/commit/00a72b8), [IDF@6e24566](https://github.com/espressif/esp-idf/commit/6e24566186c52dc5432b6b25c81abda577c21e85)) +- fix leak after _mdns_create_service if we have a malloc error. ([907e7ee](https://github.com/espressif/esp-protocols/commit/907e7ee), [IDF@b6b36bd](https://github.com/espressif/esp-idf/commit/b6b36bd9ddf169039a5528f8b766048d97b975f7)) +- Use LwIP IPC for low-level API calls ([b367484](https://github.com/espressif/esp-protocols/commit/b367484), [IDF@713964f](https://github.com/espressif/esp-idf/commit/713964fe9e98b4fa34145c497b7ab638dc57614c)) +- Add AFL fuzz test ([4a8582f](https://github.com/espressif/esp-protocols/commit/4a8582f), [IDF@4c26227](https://github.com/espressif/esp-idf/commit/4c2622755d92efa1818d062d433725553437993c)) +- implement fixes for issues found while fuzz testing ([75de31c](https://github.com/espressif/esp-protocols/commit/75de31c), [IDF@99d3990](https://github.com/espressif/esp-idf/commit/99d39909c4f19c63909d663e927ac0a8933a3ed5)) +- add simple dns-sd meta query support ([4acf639](https://github.com/espressif/esp-protocols/commit/4acf639), [IDF@96e8a3c](https://github.com/espressif/esp-idf/commit/96e8a3c725095562d2725aaefa15adcfc5d78dd5)) +- address security issues with mDNS ([91bb509](https://github.com/espressif/esp-protocols/commit/91bb509), [IDF@c89e11c](https://github.com/espressif/esp-idf/commit/c89e11c8fa64641edddf9a055745d825ae3fab9d)) +- Initial mDNS component and example ([7fbf8e5](https://github.com/espressif/esp-protocols/commit/7fbf8e5), [IDF@dd3f18d](https://github.com/espressif/esp-idf/commit/dd3f18d2d88ee78909d4af2840dfdf0b9f715f28)) diff --git a/code/components/mdns/CMakeLists.txt b/code/components/mdns/CMakeLists.txt new file mode 100644 index 00000000..e0b2a4d9 --- /dev/null +++ b/code/components/mdns/CMakeLists.txt @@ -0,0 +1,38 @@ +if(CONFIG_MDNS_NETWORKING_SOCKET) + set(MDNS_NETWORKING "mdns_networking_socket.c") +else() + set(MDNS_NETWORKING "mdns_networking_lwip.c") +endif() + +if(CONFIG_MDNS_ENABLE_CONSOLE_CLI) + set(MDNS_CONSOLE "mdns_console.c") +else() + set(MDNS_CONSOLE "") +endif() + +idf_build_get_property(target IDF_TARGET) +if(${target} STREQUAL "linux") + set(dependencies esp_netif_linux esp_event) + set(private_dependencies esp_timer console esp_system) + set(srcs "mdns.c" ${MDNS_NETWORKING} ${MDNS_CONSOLE}) +else() + set(dependencies lwip console esp_netif) + set(private_dependencies esp_timer esp_wifi) + set(srcs "mdns.c" ${MDNS_NETWORKING} ${MDNS_CONSOLE}) +endif() + +idf_component_register( + SRCS ${srcs} + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "private_include" + REQUIRES ${dependencies} + PRIV_REQUIRES ${private_dependencies}) + +if(${target} STREQUAL "linux") + target_link_libraries(${COMPONENT_LIB} PRIVATE "-lbsd") +endif() + + +if(CONFIG_ETH_ENABLED) + idf_component_optional_requires(PRIVATE esp_eth) +endif() diff --git a/code/components/mdns/Kconfig b/code/components/mdns/Kconfig new file mode 100644 index 00000000..3ef88c8d --- /dev/null +++ b/code/components/mdns/Kconfig @@ -0,0 +1,148 @@ +menu "mDNS" + + config MDNS_MAX_INTERFACES + int "Max number of interfaces" + range 1 9 + default 3 + help + Number of network interfaces to be served by the mDNS library. + Lowering this number helps to reduce some static RAM usage. + + config MDNS_MAX_SERVICES + int "Max number of services" + default 10 + help + Services take up a certain amount of memory, and allowing fewer + services to be open at the same time conserves memory. Specify + the maximum amount of services here. + + config MDNS_TASK_PRIORITY + int "mDNS task priority" + range 1 255 + default 1 + help + Allows setting mDNS task priority. Please do not set the task priority + higher than priorities of system tasks. Compile time warning/error + would be emitted if the chosen task priority were too high. + + config MDNS_ACTION_QUEUE_LEN + int "Maximum actions pending to the server" + range 8 64 + default 16 + help + Allows setting the length of mDNS action queue. + + config MDNS_TASK_STACK_SIZE + int "mDNS task stack size" + default 4096 + help + Allows setting mDNS task stacksize. + + choice MDNS_TASK_AFFINITY + prompt "mDNS task affinity" + default MDNS_TASK_AFFINITY_CPU0 + help + Allows setting mDNS tasks affinity, i.e. whether the task is pinned to + CPU0, pinned to CPU1, or allowed to run on any CPU. + + config MDNS_TASK_AFFINITY_NO_AFFINITY + bool "No affinity" + config MDNS_TASK_AFFINITY_CPU0 + bool "CPU0" + config MDNS_TASK_AFFINITY_CPU1 + bool "CPU1" + depends on !FREERTOS_UNICORE + + endchoice + + config MDNS_TASK_AFFINITY + hex + default FREERTOS_NO_AFFINITY if MDNS_TASK_AFFINITY_NO_AFFINITY + default 0x0 if MDNS_TASK_AFFINITY_CPU0 + default 0x1 if MDNS_TASK_AFFINITY_CPU1 + + config MDNS_SERVICE_ADD_TIMEOUT_MS + int "mDNS adding service timeout (ms)" + range 10 30000 + default 2000 + help + Configures timeout for adding a new mDNS service. Adding a service + fails if could not be completed within this time. + + config MDNS_TIMER_PERIOD_MS + int "mDNS timer period (ms)" + range 10 10000 + default 100 + help + Configures period of mDNS timer, which periodically transmits packets + and schedules mDNS searches. + + config MDNS_NETWORKING_SOCKET + bool "Use BSD sockets for mDNS networking" + default n + help + Enables optional mDNS networking implementation using BSD sockets + in UDP multicast mode. + This option creates a new thread to serve receiving packets (TODO). + This option uses additional N sockets, where N is number of interfaces. + + config MDNS_SKIP_SUPPRESSING_OWN_QUERIES + bool "Skip suppressing our own packets" + default n + help + Enable only if the querier and the responder share the same IP address. + This usually happens in test mode, where we may run multiple instances of + responders/queriers on the same interface. + + config MDNS_ENABLE_DEBUG_PRINTS + bool "Enable debug prints of mDNS packets" + default n + help + Enable for the library to log received and sent mDNS packets to stdout. + + config MDNS_ENABLE_CONSOLE_CLI + bool "Enable Command Line Interface on device console" + default y + help + Enable for the console cli to be available on the device. + + config MDNS_RESPOND_REVERSE_QUERIES + bool "Enable responding to IPv4 reverse queries" + default n + help + Enables support for IPv4 reverse lookup. If enabled, the mDNS library + response to PTR queries of "A.B.C.D.in-addr.arpa" type. + + config MDNS_MULTIPLE_INSTANCE + bool "Multiple instances under the same service type" + default y + help + Enables adding multiple service instances under the same service type. + + menu "MDNS Predefined interfaces" + + config MDNS_PREDEF_NETIF_STA + bool "Use predefined interface for WiFi Station" + default y + help + Set up mDNS for the default WiFi station. + Disable this option if you do not need mDNS on default WiFi STA. + + config MDNS_PREDEF_NETIF_AP + bool "Use predefined interface for WiFi Access Point" + default y + help + Set up mDNS for the default WiFi Access Point. + Disable this option if you do not need mDNS on default WiFi AP. + + config MDNS_PREDEF_NETIF_ETH + bool "Use predefined interface for Ethernet" + depends on ETH_ENABLED + default y + help + Set up mDNS for the default Ethernet interface. + Disable this option if you do not need mDNS on default Ethernet. + + endmenu # MDNS Predefined interfaces + +endmenu diff --git a/code/components/mdns/LICENSE b/code/components/mdns/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/code/components/mdns/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/code/components/mdns/README.md b/code/components/mdns/README.md new file mode 100644 index 00000000..a644ad4d --- /dev/null +++ b/code/components/mdns/README.md @@ -0,0 +1,14 @@ +# mDNS Service + +[![Component Registry](https://components.espressif.com/components/espressif/mdns/badge.svg)](https://components.espressif.com/components/espressif/mdns) + +mDNS is a multicast UDP service that is used to provide local network service and host discovery. + +## Examples + +Get started with example test [Example](https://github.com/espressif/esp-protocols/tree/master/components/mdns/examples): + +## Documentation + +* View the full [documentation(English)](https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/en/index.html) +* View the full [documentation(Chinese)](https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/zh_CN/index.html) diff --git a/code/components/mdns/examples/query_advertise/CMakeLists.txt b/code/components/mdns/examples/query_advertise/CMakeLists.txt new file mode 100644 index 00000000..9a16f140 --- /dev/null +++ b/code/components/mdns/examples/query_advertise/CMakeLists.txt @@ -0,0 +1,6 @@ +# 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) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(query_advertise) diff --git a/code/components/mdns/examples/query_advertise/README.md b/code/components/mdns/examples/query_advertise/README.md new file mode 100644 index 00000000..6ccdef97 --- /dev/null +++ b/code/components/mdns/examples/query_advertise/README.md @@ -0,0 +1,91 @@ +# mDNS example + +Shows how to use mDNS to advertise and query services and hosts + +## Example workflow + +- mDNS is initialized with host name and instance name defined through the project configuration and `_http._tcp` service is added to be advertised +- A delegated host `esp32-delegated._local` is added and another `_http._tcp` service is added for this host. +- WiFi STA is started and trying to connect to the access point defined through the project configuration +- The system event handler is used to pass the network events to mDNS so the service is aware when the interface comes up or down +- GPIO0 (BOOT Button) is initialized as pulled-up input that can be monitored for button press +- Example task is started to check if the button is pressed so it can execute the mDNS queries defined + +### Configure the project + +* Open the project configuration menu (`idf.py menuconfig`) + +* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu +* Set `mDNS Hostname` as host name prefix for the device and its instance name in `mDNS Instance Name` +* Disable `Resolve test services` to prevent the example from querying defined names/services on startup (cause warnings in example logs, as illustrated below) + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +- Wait for WiFi to connect to your access point +- You can now ping the device at `[board-hostname].local`, where `[board-hostname]` is preconfigured hostname, `esp32-mdns` by default. +- You can also browse for `_http._tcp` on the same network to find the advertised service +- Pressing the BOOT button will start querying the local network for the predefined in `check_button` hosts and services +- Note that for purpose of CI tests, configuration options of `MDNS_RESOLVE_TEST_SERVICES` and `MDNS_ADD_MAC_TO_HOSTNAME` are available, but disabled by default. If enabled, then the hostname suffix of last 3 bytes from device MAC address is added, e.g. `esp32-mdns-80FFFF`, and a query for test service is issued. + + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output +``` +I (0) cpu_start: Starting scheduler on APP CPU. +I (276) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +I (276) mdns-test: mdns hostname set to: [esp32-mdns] +I (286) wifi: wifi driver task: 3ffc2fa4, prio:23, stack:3584, core=0 +I (286) wifi: wifi firmware version: a3be639 +I (286) wifi: config NVS flash: enabled +I (296) wifi: config nano formating: disabled +I (296) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +I (306) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +I (336) wifi: Init dynamic tx buffer num: 32 +I (336) wifi: Init data frame dynamic rx buffer num: 32 +I (336) wifi: Init management frame dynamic rx buffer num: 32 +I (346) wifi: Init static rx buffer size: 1600 +I (346) wifi: Init static rx buffer num: 10 +I (346) wifi: Init dynamic rx buffer num: 32 +I (356) mdns-test: Setting WiFi configuration SSID myssid... +I (426) phy: phy_version: 4000, b6198fa, Sep 3 2018, 15:11:06, 0, 0 +I (426) wifi: mode : sta (30:ae:a4:80:FF:FF) +I (426) gpio: GPIO[0]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (1756) wifi: n:11 0, o:1 0, ap:255 255, sta:11 0, prof:1 +I (2736) wifi: state: init -> auth (b0) +I (2756) wifi: state: auth -> assoc (0) +I (2766) wifi: state: assoc -> run (10) +I (2786) wifi: connected with myssid, channel 11 +I (2786) wifi: pm start, type: 1 + +I (4786) event: sta ip: 192.168.0.139, mask: 255.255.255.0, gw: 192.168.0.2 +I (21126) mdns-test: Query A: esp32.local +W (23176) mdns-test: ESP_ERR_NOT_FOUND: Host was not found! +I (23176) mdns-test: Query PTR: _arduino._tcp.local +W (26276) mdns-test: No results found! +I (26276) mdns-test: Query PTR: _http._tcp.local +1: Interface: STA, Type: V6 + PTR : HP Color LaserJet MFP M277dw (7C2E10) + SRV : NPI7C2E10.local:80 + A : 254.128.0.0 +2: Interface: STA, Type: V4 + PTR : switch4e4919 + SRV : switch4e4919.local:80 + TXT : [1] path=/config/authentication_page.htm; + A : 192.168.0.118 +I (29396) mdns-test: Query PTR: _printer._tcp.local +1: Interface: STA, Type: V6 + PTR : HP Color LaserJet MFP M277dw (7C2E10) + SRV : NPI7C2E10.local:515 + A : 254.128.0.0 +2: Interface: STA, Type: V4 + PTR : HP Color LaserJet MFP M277dw (7C2E10) +``` diff --git a/code/components/mdns/examples/query_advertise/main/CMakeLists.txt b/code/components/mdns/examples/query_advertise/main/CMakeLists.txt new file mode 100644 index 00000000..eb0e6d9e --- /dev/null +++ b/code/components/mdns/examples/query_advertise/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "mdns_example_main.c" + INCLUDE_DIRS ".") diff --git a/code/components/mdns/examples/query_advertise/main/Kconfig.projbuild b/code/components/mdns/examples/query_advertise/main/Kconfig.projbuild new file mode 100644 index 00000000..18e1cc20 --- /dev/null +++ b/code/components/mdns/examples/query_advertise/main/Kconfig.projbuild @@ -0,0 +1,55 @@ +menu "Example Configuration" + + orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps" + + config MDNS_HOSTNAME + string "mDNS Hostname" + default "esp32-mdns" + help + mDNS Hostname for example to use + + config MDNS_INSTANCE + string "mDNS Instance Name" + default "ESP32 with mDNS" + help + mDNS Instance Name for example to use + + config MDNS_PUBLISH_DELEGATE_HOST + bool "Publish a delegated host" + help + Enable publishing a delegated host other than ESP32. + The example will also add a mock service for this host. + + config MDNS_RESOLVE_TEST_SERVICES + bool "Resolve test services" + default n + help + Enable resolving test services on startup. + These services are advertized and evaluated in automated tests. + When executed locally, these will not be resolved and warnings appear in the log. + Please set to false to disable initial querying to avoid warnings. + + config MDNS_ADD_MAC_TO_HOSTNAME + bool "Add mac suffix to hostname" + default n + help + If enabled, a portion of MAC address is added to the hostname, this is used + for evaluation of tests in CI + + config MDNS_BUTTON_GPIO + int "Button GPIO to trigger querries" + range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX + default 0 + help + Set the GPIO number used as mDNS test button + + config MDNS_ADD_CUSTOM_NETIF + bool "Add user netif to mdns service" + default n + help + If enabled, we try to add a custom netif to mdns service. + Note that for using with common connection example code, we have to disable + all predefined interfaces in mdns component setup (since we're adding one + of the default interfaces) + +endmenu diff --git a/code/components/mdns/examples/query_advertise/main/idf_component.yml b/code/components/mdns/examples/query_advertise/main/idf_component.yml new file mode 100644 index 00000000..e9277dfc --- /dev/null +++ b/code/components/mdns/examples/query_advertise/main/idf_component.yml @@ -0,0 +1,8 @@ +dependencies: + ## Required IDF version + idf: ">=5.0" + espressif/mdns: + version: "^1.0.0" + override_path: "../../../" + protocol_examples_common: + path: ${IDF_PATH}/examples/common_components/protocol_examples_common diff --git a/code/components/mdns/examples/query_advertise/main/mdns_example_main.c b/code/components/mdns/examples/query_advertise/main/mdns_example_main.c new file mode 100644 index 00000000..a6aaea2d --- /dev/null +++ b/code/components/mdns/examples/query_advertise/main/mdns_example_main.c @@ -0,0 +1,421 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +/* + * MDNS-SD Query and advertise Example +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_netif_ip_addr.h" +#include "esp_mac.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "esp_netif.h" +#include "protocol_examples_common.h" +#include "mdns.h" +#include "driver/gpio.h" +#include "netdb.h" + +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) +/* CONFIG_LWIP_IPV4 was introduced in IDF v5.1, set CONFIG_LWIP_IPV4 to 1 by default for IDF v5.0 */ +#ifndef CONFIG_LWIP_IPV4 +#define CONFIG_LWIP_IPV4 1 +#endif // CONFIG_LWIP_IPV4 +#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) + +#define EXAMPLE_MDNS_INSTANCE CONFIG_MDNS_INSTANCE +#define EXAMPLE_BUTTON_GPIO CONFIG_MDNS_BUTTON_GPIO + +static const char *TAG = "mdns-test"; +static char *generate_hostname(void); + +#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1 +static void query_mdns_host_with_gethostbyname(char *host); +static void query_mdns_host_with_getaddrinfo(char *host); +#endif + +static void initialise_mdns(void) +{ + char *hostname = generate_hostname(); + + //initialize mDNS + ESP_ERROR_CHECK( mdns_init() ); + //set mDNS hostname (required if you want to advertise services) + ESP_ERROR_CHECK( mdns_hostname_set(hostname) ); + ESP_LOGI(TAG, "mdns hostname set to: [%s]", hostname); + //set default mDNS instance name + ESP_ERROR_CHECK( mdns_instance_name_set(EXAMPLE_MDNS_INSTANCE) ); + + //structure with TXT records + mdns_txt_item_t serviceTxtData[3] = { + {"board", "esp32"}, + {"u", "user"}, + {"p", "password"} + }; + + //initialize service + ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, serviceTxtData, 3) ); + ESP_ERROR_CHECK( mdns_service_subtype_add_for_host("ESP32-WebServer", "_http", "_tcp", NULL, "_server") ); +#if CONFIG_MDNS_MULTIPLE_INSTANCE + ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer1", "_http", "_tcp", 80, NULL, 0) ); +#endif + +#if CONFIG_MDNS_PUBLISH_DELEGATE_HOST + char *delegated_hostname; + if (-1 == asprintf(&delegated_hostname, "%s-delegated", hostname)) { + abort(); + } + + mdns_ip_addr_t addr4, addr6; + esp_netif_str_to_ip4("10.0.0.1", &addr4.addr.u_addr.ip4); + addr4.addr.type = ESP_IPADDR_TYPE_V4; + esp_netif_str_to_ip6("fd11:22::1", &addr6.addr.u_addr.ip6); + addr6.addr.type = ESP_IPADDR_TYPE_V6; + addr4.next = &addr6; + addr6.next = NULL; + ESP_ERROR_CHECK( mdns_delegate_hostname_add(delegated_hostname, &addr4) ); + ESP_ERROR_CHECK( mdns_service_add_for_host("test0", "_http", "_tcp", delegated_hostname, 1234, serviceTxtData, 3) ); + free(delegated_hostname); +#endif // CONFIG_MDNS_PUBLISH_DELEGATE_HOST + + //add another TXT item + ESP_ERROR_CHECK( mdns_service_txt_item_set("_http", "_tcp", "path", "/foobar") ); + //change TXT item value + ESP_ERROR_CHECK( mdns_service_txt_item_set_with_explicit_value_len("_http", "_tcp", "u", "admin", strlen("admin")) ); + free(hostname); +} + +/* these strings match mdns_ip_protocol_t enumeration */ +static const char *ip_protocol_str[] = {"V4", "V6", "MAX"}; + +static void mdns_print_results(mdns_result_t *results) +{ + mdns_result_t *r = results; + mdns_ip_addr_t *a = NULL; + int i = 1, t; + while (r) { + if (r->esp_netif) { + printf("%d: Interface: %s, Type: %s, TTL: %" PRIu32 "\n", i++, esp_netif_get_ifkey(r->esp_netif), + ip_protocol_str[r->ip_protocol], r->ttl); + } + if (r->instance_name) { + printf(" PTR : %s.%s.%s\n", r->instance_name, r->service_type, r->proto); + } + if (r->hostname) { + printf(" SRV : %s.local:%u\n", r->hostname, r->port); + } + if (r->txt_count) { + printf(" TXT : [%zu] ", r->txt_count); + for (t = 0; t < r->txt_count; t++) { + printf("%s=%s(%d); ", r->txt[t].key, r->txt[t].value ? r->txt[t].value : "NULL", r->txt_value_len[t]); + } + printf("\n"); + } + a = r->addr; + while (a) { + if (a->addr.type == ESP_IPADDR_TYPE_V6) { + printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6)); + } else { + printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4))); + } + a = a->next; + } + r = r->next; + } +} + +static void query_mdns_service(const char *service_name, const char *proto) +{ + ESP_LOGI(TAG, "Query PTR: %s.%s.local", service_name, proto); + + mdns_result_t *results = NULL; + esp_err_t err = mdns_query_ptr(service_name, proto, 3000, 20, &results); + if (err) { + ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err)); + return; + } + if (!results) { + ESP_LOGW(TAG, "No results found!"); + return; + } + + mdns_print_results(results); + mdns_query_results_free(results); +} + +#if CONFIG_MDNS_PUBLISH_DELEGATE_HOST +static void lookup_mdns_delegated_service(const char *service_name, const char *proto) +{ + ESP_LOGI(TAG, "Lookup delegated service: %s.%s.local", service_name, proto); + + mdns_result_t *results = NULL; + esp_err_t err = mdns_lookup_delegated_service(NULL, service_name, proto, 20, &results); + if (err) { + ESP_LOGE(TAG, "Lookup Failed: %s", esp_err_to_name(err)); + return; + } + if (!results) { + ESP_LOGW(TAG, "No results found!"); + return; + } + + mdns_print_results(results); + mdns_query_results_free(results); +} +#endif // CONFIG_MDNS_PUBLISH_DELEGATE_HOST + +static void lookup_mdns_selfhosted_service(const char *service_name, const char *proto) +{ + ESP_LOGI(TAG, "Lookup selfhosted service: %s.%s.local", service_name, proto); + mdns_result_t *results = NULL; + esp_err_t err = mdns_lookup_selfhosted_service(NULL, service_name, proto, 20, &results); + if (err) { + ESP_LOGE(TAG, "Lookup Failed: %s", esp_err_to_name(err)); + return; + } + if (!results) { + ESP_LOGW(TAG, "No results found!"); + return; + } + mdns_print_results(results); + mdns_query_results_free(results); +} + +static bool check_and_print_result(mdns_search_once_t *search) +{ + // Check if any result is available + mdns_result_t *result = NULL; + if (!mdns_query_async_get_results(search, 0, &result, NULL)) { + return false; + } + + if (!result) { // search timeout, but no result + return true; + } + + // If yes, print the result + mdns_ip_addr_t *a = result->addr; + while (a) { + if (a->addr.type == ESP_IPADDR_TYPE_V6) { + printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6)); + } else { + printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4))); + } + a = a->next; + } + // and free the result + mdns_query_results_free(result); + return true; +} + +static void query_mdns_hosts_async(const char *host_name) +{ + ESP_LOGI(TAG, "Query both A and AAA: %s.local", host_name); + + mdns_search_once_t *s_a = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_A, 1000, 1, NULL); + mdns_search_once_t *s_aaaa = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_AAAA, 1000, 1, NULL); + while (s_a || s_aaaa) { + if (s_a && check_and_print_result(s_a)) { + ESP_LOGI(TAG, "Query A %s.local finished", host_name); + mdns_query_async_delete(s_a); + s_a = NULL; + } + if (s_aaaa && check_and_print_result(s_aaaa)) { + ESP_LOGI(TAG, "Query AAAA %s.local finished", host_name); + mdns_query_async_delete(s_aaaa); + s_aaaa = NULL; + } + vTaskDelay(50 / portTICK_PERIOD_MS); + } +} +#ifdef CONFIG_LWIP_IPV4 +static void query_mdns_host(const char *host_name) +{ + ESP_LOGI(TAG, "Query A: %s.local", host_name); + + struct esp_ip4_addr addr; + addr.addr = 0; + + esp_err_t err = mdns_query_a(host_name, 2000, &addr); + if (err) { + if (err == ESP_ERR_NOT_FOUND) { + ESP_LOGW(TAG, "%s: Host was not found!", esp_err_to_name(err)); + return; + } + ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err)); + return; + } + + ESP_LOGI(TAG, "Query A: %s.local resolved to: " IPSTR, host_name, IP2STR(&addr)); +} +#endif // CONFIG_LWIP_IPV4 + +static void initialise_button(void) +{ + gpio_config_t io_conf = {0}; + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.pin_bit_mask = BIT64(EXAMPLE_BUTTON_GPIO); + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_up_en = 1; + io_conf.pull_down_en = 0; + gpio_config(&io_conf); +} + +static void check_button(void) +{ + static bool old_level = true; + bool new_level = gpio_get_level(EXAMPLE_BUTTON_GPIO); + if (!new_level && old_level) { + query_mdns_hosts_async("esp32-mdns"); +#ifdef CONFIG_LWIP_IPV4 + query_mdns_host("esp32"); +#endif + query_mdns_service("_arduino", "_tcp"); + query_mdns_service("_http", "_tcp"); + query_mdns_service("_printer", "_tcp"); + query_mdns_service("_ipp", "_tcp"); + query_mdns_service("_afpovertcp", "_tcp"); + query_mdns_service("_smb", "_tcp"); + query_mdns_service("_ftp", "_tcp"); + query_mdns_service("_nfs", "_tcp"); +#if CONFIG_MDNS_PUBLISH_DELEGATE_HOST + lookup_mdns_delegated_service("_http", "_tcp"); +#endif // CONFIG_MDNS_PUBLISH_DELEGATE_HOST + lookup_mdns_selfhosted_service("_http", "_tcp"); + } + old_level = new_level; +} + +static void mdns_example_task(void *pvParameters) +{ +#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1 + /* Send initial queries that are started by CI tester */ +#ifdef CONFIG_LWIP_IPV4 + query_mdns_host("tinytester"); +#endif + query_mdns_host_with_gethostbyname("tinytester-lwip.local"); + query_mdns_host_with_getaddrinfo("tinytester-lwip.local"); +#endif + + while (1) { + check_button(); + vTaskDelay(50 / portTICK_PERIOD_MS); + } +} + +void app_main(void) +{ + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + initialise_mdns(); + + /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + */ + ESP_ERROR_CHECK(example_connect()); + +#if defined(CONFIG_MDNS_ADD_CUSTOM_NETIF) && !defined(CONFIG_MDNS_PREDEF_NETIF_STA) && !defined(CONFIG_MDNS_PREDEF_NETIF_ETH) + /* Demonstration of adding a custom netif to mdns service, but we're adding the default example one, + * so we must disable all predefined interfaces (PREDEF_NETIF_STA, AP and ETH) first + */ + ESP_ERROR_CHECK(mdns_register_netif(EXAMPLE_INTERFACE)); + /* It is not enough to just register the interface, we have to enable is manually. + * This is typically performed in "GOT_IP" event handler, but we call it here directly + * since the `EXAMPLE_INTERFACE` netif is connected already, to keep the example simple. + */ + ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ENABLE_IP4 | MDNS_EVENT_ENABLE_IP6)); + ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_ANNOUNCE_IP4 | MDNS_EVENT_ANNOUNCE_IP6)); + +#if defined(CONFIG_MDNS_RESPOND_REVERSE_QUERIES) + ESP_ERROR_CHECK(mdns_netif_action(EXAMPLE_INTERFACE, MDNS_EVENT_IP4_REVERSE_LOOKUP | MDNS_EVENT_IP6_REVERSE_LOOKUP)); +#endif + +#endif // CONFIG_MDNS_ADD_CUSTOM_NETIF + + initialise_button(); + xTaskCreate(&mdns_example_task, "mdns_example_task", 2048, NULL, 5, NULL); +} + +/** Generate host name based on sdkconfig, optionally adding a portion of MAC address to it. + * @return host name string allocated from the heap + */ +static char *generate_hostname(void) +{ +#ifndef CONFIG_MDNS_ADD_MAC_TO_HOSTNAME + return strdup(CONFIG_MDNS_HOSTNAME); +#else + uint8_t mac[6]; + char *hostname; + esp_read_mac(mac, ESP_MAC_WIFI_STA); + if (-1 == asprintf(&hostname, "%s-%02X%02X%02X", CONFIG_MDNS_HOSTNAME, mac[3], mac[4], mac[5])) { + abort(); + } + return hostname; +#endif +} + +#if CONFIG_MDNS_RESOLVE_TEST_SERVICES == 1 +/** + * @brief Executes gethostbyname and displays list of resolved addresses. + * Note: This function is used only to test advertised mdns hostnames resolution + */ +static void query_mdns_host_with_gethostbyname(char *host) +{ + struct hostent *res = gethostbyname(host); + if (res) { + unsigned int i = 0; + while (res->h_addr_list[i] != NULL) { + ESP_LOGI(TAG, "gethostbyname: %s resolved to: %s", host, +#if defined(CONFIG_LWIP_IPV6) && defined(CONFIG_LWIP_IPV4) + res->h_addrtype == AF_INET ? inet_ntoa(*(struct in_addr *) (res->h_addr_list[i])) : + inet6_ntoa(*(struct in6_addr *) (res->h_addr_list[i])) +#elif defined(CONFIG_LWIP_IPV6) + inet6_ntoa(*(struct in6_addr *) (res->h_addr_list[i])) +#else + inet_ntoa(*(struct in_addr *) (res->h_addr_list[i])) +#endif + ); + i++; + } + } +} + +/** + * @brief Executes getaddrinfo and displays list of resolved addresses. + * Note: This function is used only to test advertised mdns hostnames resolution + */ +static void query_mdns_host_with_getaddrinfo(char *host) +{ + struct addrinfo hints; + struct addrinfo *res; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if (!getaddrinfo(host, NULL, &hints, &res)) { + while (res) { + char *resolved_addr; +#if defined(CONFIG_LWIP_IPV6) && defined(CONFIG_LWIP_IPV4) + resolved_addr = res->ai_family == AF_INET ? + inet_ntoa(((struct sockaddr_in *) res->ai_addr)->sin_addr) : + inet6_ntoa(((struct sockaddr_in6 *) res->ai_addr)->sin6_addr); +#elif defined(CONFIG_LWIP_IPV6) + resolved_addr = inet6_ntoa(((struct sockaddr_in6 *) res->ai_addr)->sin6_addr); +#else + resolved_addr = inet_ntoa(((struct sockaddr_in *) res->ai_addr)->sin_addr); +#endif // CONFIG_LWIP_IPV6 + ESP_LOGI(TAG, "getaddrinfo: %s resolved to: %s", host, resolved_addr); + res = res->ai_next; + } + } +} +#endif diff --git a/code/components/mdns/examples/query_advertise/pytest_mdns.py b/code/components/mdns/examples/query_advertise/pytest_mdns.py new file mode 100644 index 00000000..e78e094d --- /dev/null +++ b/code/components/mdns/examples/query_advertise/pytest_mdns.py @@ -0,0 +1,203 @@ +# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 +import re +import select +import socket +import struct +import subprocess +import time +from threading import Event, Thread + +try: + import dpkt + import dpkt.dns +except ImportError: + pass + + +def get_dns_query_for_esp(esp_host): + dns = dpkt.dns.DNS( + b'\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01' + ) + dns.qd[0].name = esp_host + u'.local' + print('Created query for esp host: {} '.format(dns.__repr__())) + return dns.pack() + + +def get_dns_answer_to_mdns(tester_host): + dns = dpkt.dns.DNS( + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + ) + dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA + dns.rcode = dpkt.dns.DNS_RCODE_NOERR + arr = dpkt.dns.DNS.RR() + arr.cls = dpkt.dns.DNS_IN + arr.type = dpkt.dns.DNS_A + arr.name = tester_host + arr.ip = socket.inet_aton('127.0.0.1') + dns.an.append(arr) + print('Created answer to mdns query: {} '.format(dns.__repr__())) + return dns.pack() + + +def get_dns_answer_to_mdns_lwip(tester_host, id): + dns = dpkt.dns.DNS( + b'\x5e\x39\x84\x00\x00\x01\x00\x01\x00\x00\x00\x00\x0a\x64\x61\x76\x69\x64' + b'\x2d\x63\x6f\x6d\x70\x05\x6c\x6f\x63\x61\x6c\x00\x00\x01\x00\x01\xc0\x0c' + b'\x00\x01\x00\x01\x00\x00\x00\x0a\x00\x04\xc0\xa8\x0a\x6c') + dns.qd[0].name = tester_host + dns.an[0].name = tester_host + dns.an[0].ip = socket.inet_aton('127.0.0.1') + dns.an[0].rdata = socket.inet_aton('127.0.0.1') + dns.id = id + print('Created answer to mdns (lwip) query: {} '.format(dns.__repr__())) + return dns.pack() + + +def mdns_server(esp_host, events): + UDP_IP = '0.0.0.0' + UDP_PORT = 5353 + MCAST_GRP = '224.0.0.251' + TESTER_NAME = u'tinytester.local' + TESTER_NAME_LWIP = u'tinytester-lwip.local' + QUERY_TIMEOUT = 0.2 + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + sock.setblocking(False) + sock.bind((UDP_IP, UDP_PORT)) + mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + last_query_timepoint = time.time() + while not events['stop'].is_set(): + try: + current_time = time.time() + if current_time - last_query_timepoint > QUERY_TIMEOUT: + last_query_timepoint = current_time + if not events['esp_answered'].is_set(): + sock.sendto(get_dns_query_for_esp(esp_host), + (MCAST_GRP, UDP_PORT)) + if not events['esp_delegated_answered'].is_set(): + sock.sendto(get_dns_query_for_esp(esp_host + '-delegated'), + (MCAST_GRP, UDP_PORT)) + timeout = max( + 0, QUERY_TIMEOUT - (current_time - last_query_timepoint)) + read_socks, _, _ = select.select([sock], [], [], timeout) + if not read_socks: + continue + data, addr = sock.recvfrom(1024) + dns = dpkt.dns.DNS(data) + if len(dns.qd) > 0: + for dns_query in dns.qd: + if dns_query.type == dpkt.dns.DNS_A: + if dns_query.name == TESTER_NAME: + print('Received query: {} '.format(dns.__repr__())) + sock.sendto(get_dns_answer_to_mdns(TESTER_NAME), + (MCAST_GRP, UDP_PORT)) + elif dns_query.name == TESTER_NAME_LWIP: + print('Received query: {} '.format(dns.__repr__())) + sock.sendto( + get_dns_answer_to_mdns_lwip(TESTER_NAME_LWIP, dns.id), + addr) + if len(dns.an) > 0: + for dns_answer in dns.an: + if dns_answer.type == dpkt.dns.DNS_A: + print('Received answer from {}'.format(dns_answer.name)) + if dns_answer.name == esp_host + u'.local': + print('Received answer to esp32-mdns query: {}'.format( + dns.__repr__())) + events['esp_answered'].set() + if dns_answer.name == esp_host + u'-delegated.local': + print('Received answer to esp32-mdns-delegate query: {}'.format( + dns.__repr__())) + events['esp_delegated_answered'].set() + except socket.timeout: + break + except dpkt.UnpackError: + continue + + +def test_examples_protocol_mdns(dut): + """ + steps: | + 1. obtain IP address + init mdns example + 2. get the dut host name (and IP address) + 3. check the mdns name is accessible + 4. check DUT output if mdns advertized host is resolved + 5. check if DUT responds to dig + 6. check the DUT is searchable via reverse IP lookup + """ + + specific_host = dut.expect(r'mdns hostname set to: \[(.*?)\]')[1].decode() + + mdns_server_events = { + 'stop': Event(), + 'esp_answered': Event(), + 'esp_delegated_answered': Event() + } + mdns_responder = Thread(target=mdns_server, + args=(str(specific_host), mdns_server_events)) + ip_addresses = [] + if dut.app.sdkconfig.get('LWIP_IPV4') is True: + ipv4 = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', + timeout=30)[1].decode() + ip_addresses.append(ipv4) + if dut.app.sdkconfig.get('LWIP_IPV6') is True: + ipv6_r = r':'.join((r'[0-9a-fA-F]{4}', ) * 8) + ipv6 = dut.expect(ipv6_r, timeout=30)[0].decode() + ip_addresses.append(ipv6) + print('Connected with IP addresses: {}'.format(','.join(ip_addresses))) + try: + # TODO: Add test for example disabling IPV4 + mdns_responder.start() + if dut.app.sdkconfig.get('LWIP_IPV4') is True: + # 3. check the mdns name is accessible. + if not mdns_server_events['esp_answered'].wait(timeout=30): + raise ValueError( + 'Test has failed: did not receive mdns answer within timeout') + if not mdns_server_events['esp_delegated_answered'].wait(timeout=30): + raise ValueError( + 'Test has failed: did not receive mdns answer for delegated host within timeout' + ) + # 4. check DUT output if mdns advertized host is resolved + dut.expect( + re.compile( + b'mdns-test: Query A: tinytester.local resolved to: 127.0.0.1') + ) + dut.expect( + re.compile( + b'mdns-test: gethostbyname: tinytester-lwip.local resolved to: 127.0.0.1' + )) + dut.expect( + re.compile( + b'mdns-test: getaddrinfo: tinytester-lwip.local resolved to: 127.0.0.1' + )) + # 5. check the DUT answers to `dig` command + dig_output = subprocess.check_output([ + 'dig', '+short', '-p', '5353', '@224.0.0.251', + '{}.local'.format(specific_host) + ]) + print('Resolving {} using "dig" succeeded with:\n{}'.format( + specific_host, dig_output)) + if not ipv4.encode('utf-8') in dig_output: + raise ValueError( + 'Test has failed: Incorrectly resolved DUT hostname using dig' + "Output should've contained DUT's IP address:{}".format(ipv4)) + # 6. check the DUT reverse lookup + if dut.app.sdkconfig.get('MDNS_RESPOND_REVERSE_QUERIES') is True: + for ip_address in ip_addresses: + dig_output = subprocess.check_output([ + 'dig', '+short', '-p', '5353', '@224.0.0.251', '-x', + '{}'.format(ip_address) + ]) + print('Reverse lookup for {} using "dig" succeeded with:\n{}'. + format(ip_address, dig_output)) + if specific_host not in dig_output.decode(): + raise ValueError( + 'Test has failed: Incorrectly resolved DUT IP address using dig' + "Output should've contained DUT's name:{}".format( + specific_host)) + + finally: + mdns_server_events['stop'].set() + mdns_responder.join() diff --git a/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_custom_netif b/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_custom_netif new file mode 100644 index 00000000..b9d71204 --- /dev/null +++ b/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_custom_netif @@ -0,0 +1,20 @@ +CONFIG_IDF_TARGET="esp32" +CONFIG_MDNS_RESOLVE_TEST_SERVICES=y +CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y +CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y +CONFIG_MDNS_PREDEF_NETIF_STA=n +CONFIG_MDNS_PREDEF_NETIF_AP=n +CONFIG_MDNS_PREDEF_NETIF_ETH=n +CONFIG_MDNS_ADD_CUSTOM_NETIF=y +CONFIG_MDNS_RESPOND_REVERSE_QUERIES=y +CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y +CONFIG_EXAMPLE_CONNECT_ETHERNET=y +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y +CONFIG_EXAMPLE_ETH_PHY_IP101=y +CONFIG_EXAMPLE_ETH_MDC_GPIO=23 +CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 +CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 +CONFIG_EXAMPLE_ETH_PHY_ADDR=1 +CONFIG_EXAMPLE_CONNECT_IPV6=y +CONFIG_MDNS_BUTTON_GPIO=32 diff --git a/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_def b/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_def new file mode 100644 index 00000000..703b4543 --- /dev/null +++ b/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_def @@ -0,0 +1,15 @@ +CONFIG_IDF_TARGET="esp32" +CONFIG_MDNS_RESOLVE_TEST_SERVICES=y +CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y +CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y +CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y +CONFIG_EXAMPLE_CONNECT_ETHERNET=y +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y +CONFIG_EXAMPLE_ETH_PHY_IP101=y +CONFIG_EXAMPLE_ETH_MDC_GPIO=23 +CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 +CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 +CONFIG_EXAMPLE_ETH_PHY_ADDR=1 +CONFIG_EXAMPLE_CONNECT_IPV6=y +CONFIG_MDNS_BUTTON_GPIO=32 diff --git a/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_no_ipv4 b/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_no_ipv4 new file mode 100644 index 00000000..b77a7527 --- /dev/null +++ b/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_no_ipv4 @@ -0,0 +1,15 @@ +CONFIG_IDF_TARGET="esp32" +CONFIG_MDNS_RESOLVE_TEST_SERVICES=y +CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y +CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y +CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y +CONFIG_LWIP_IPV4=n +CONFIG_EXAMPLE_CONNECT_ETHERNET=y +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y +CONFIG_EXAMPLE_ETH_PHY_IP101=y +CONFIG_EXAMPLE_ETH_MDC_GPIO=23 +CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 +CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 +CONFIG_EXAMPLE_ETH_PHY_ADDR=1 +CONFIG_MDNS_BUTTON_GPIO=32 diff --git a/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_no_ipv6 b/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_no_ipv6 new file mode 100644 index 00000000..d669ec07 --- /dev/null +++ b/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_no_ipv6 @@ -0,0 +1,16 @@ +CONFIG_IDF_TARGET="esp32" +CONFIG_MDNS_RESOLVE_TEST_SERVICES=y +CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y +CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y +CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y +CONFIG_LWIP_IPV6=n +CONFIG_EXAMPLE_CONNECT_IPV6=n +CONFIG_EXAMPLE_CONNECT_ETHERNET=y +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y +CONFIG_EXAMPLE_ETH_PHY_IP101=y +CONFIG_EXAMPLE_ETH_MDC_GPIO=23 +CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 +CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 +CONFIG_EXAMPLE_ETH_PHY_ADDR=1 +CONFIG_MDNS_BUTTON_GPIO=32 diff --git a/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_socket b/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_socket new file mode 100644 index 00000000..5d924052 --- /dev/null +++ b/code/components/mdns/examples/query_advertise/sdkconfig.ci.eth_socket @@ -0,0 +1,16 @@ +CONFIG_IDF_TARGET="esp32" +CONFIG_MDNS_RESOLVE_TEST_SERVICES=y +CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y +CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y +CONFIG_MDNS_NETWORKING_SOCKET=y +CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y +CONFIG_EXAMPLE_CONNECT_ETHERNET=y +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y +CONFIG_EXAMPLE_ETH_PHY_IP101=y +CONFIG_EXAMPLE_ETH_MDC_GPIO=23 +CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 +CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 +CONFIG_EXAMPLE_ETH_PHY_ADDR=1 +CONFIG_EXAMPLE_CONNECT_IPV6=y +CONFIG_MDNS_BUTTON_GPIO=32 diff --git a/code/components/mdns/idf_component.yml b/code/components/mdns/idf_component.yml new file mode 100644 index 00000000..1e43ed70 --- /dev/null +++ b/code/components/mdns/idf_component.yml @@ -0,0 +1,9 @@ +version: "1.4.3" +description: "Multicast UDP service used to provide local network service and host discovery." +url: "https://github.com/espressif/esp-protocols/tree/master/components/mdns" +issues: "https://github.com/espressif/esp-protocols/issues" +documentation: "https://docs.espressif.com/projects/esp-protocols/mdns/docs/latest/en/index.html" +repository: "https://github.com/espressif/esp-protocols.git" +dependencies: + idf: + version: ">=5.0" diff --git a/code/components/mdns/include/mdns.h b/code/components/mdns/include/mdns.h new file mode 100644 index 00000000..cc9a39d1 --- /dev/null +++ b/code/components/mdns/include/mdns.h @@ -0,0 +1,937 @@ +/* + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ESP_MDNS_H_ +#define ESP_MDNS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sdkconfig.h" +#include + +#define MDNS_TYPE_A 0x0001 +#define MDNS_TYPE_PTR 0x000C +#define MDNS_TYPE_TXT 0x0010 +#define MDNS_TYPE_AAAA 0x001C +#define MDNS_TYPE_SRV 0x0021 +#define MDNS_TYPE_OPT 0x0029 +#define MDNS_TYPE_NSEC 0x002F +#define MDNS_TYPE_ANY 0x00FF + +#if defined(CONFIG_LWIP_IPV6) && defined(CONFIG_MDNS_RESPOND_REVERSE_QUERIES) +#define MDNS_NAME_MAX_LEN (64+4) // Need to account for IPv6 reverse queries (64 char address + ".ip6" ) +#else +#define MDNS_NAME_MAX_LEN 64 // Maximum string length of hostname, instance, service and proto +#endif +#define MDNS_NAME_BUF_LEN (MDNS_NAME_MAX_LEN+1) // Maximum char buffer size to hold hostname, instance, service or proto + +/** + * @brief Asynchronous query handle + */ +typedef struct mdns_search_once_s mdns_search_once_t; + +/** + * @brief Daemon query handle + */ +typedef struct mdns_browse_s mdns_browse_t; + +typedef enum { + MDNS_EVENT_ENABLE_IP4 = 1 << 1, + MDNS_EVENT_ENABLE_IP6 = 1 << 2, + MDNS_EVENT_ANNOUNCE_IP4 = 1 << 3, + MDNS_EVENT_ANNOUNCE_IP6 = 1 << 4, + MDNS_EVENT_DISABLE_IP4 = 1 << 5, + MDNS_EVENT_DISABLE_IP6 = 1 << 6, + MDNS_EVENT_IP4_REVERSE_LOOKUP = 1 << 7, + MDNS_EVENT_IP6_REVERSE_LOOKUP = 1 << 8, +} mdns_event_actions_t; + +/** + * @brief mDNS enum to specify the ip_protocol type + */ +typedef enum { + MDNS_IP_PROTOCOL_V4, + MDNS_IP_PROTOCOL_V6, + MDNS_IP_PROTOCOL_MAX +} mdns_ip_protocol_t; + +/** + * @brief mDNS basic text item structure + * Used in mdns_service_add() + */ +typedef struct { + const char *key; /*!< item key name */ + const char *value; /*!< item value string */ +} mdns_txt_item_t; + +/** + * @brief mDNS basic subtype item structure + * Used in mdns_service_subtype_xxx() APIs + */ +typedef struct { + const char *subtype; /*!< subtype name */ +} mdns_subtype_item_t; + +/** + * @brief mDNS query linked list IP item + */ +typedef struct mdns_ip_addr_s { + esp_ip_addr_t addr; /*!< IP address */ + struct mdns_ip_addr_s *next; /*!< next IP, or NULL for the last IP in the list */ +} mdns_ip_addr_t; + +/** + * @brief mDNS query type to be explicitly set to either Unicast or Multicast + */ +typedef enum { + MDNS_QUERY_UNICAST, + MDNS_QUERY_MULTICAST, +} mdns_query_transmission_type_t; + +/** + * @brief mDNS query result structure + */ +typedef struct mdns_result_s { + struct mdns_result_s *next; /*!< next result, or NULL for the last result in the list */ + + esp_netif_t *esp_netif; /*!< ptr to corresponding esp-netif */ + uint32_t ttl; /*!< time to live */ + + mdns_ip_protocol_t ip_protocol; /*!< ip_protocol type of the interface (v4/v6) */ + // PTR + char *instance_name; /*!< instance name */ + char *service_type; /*!< service type */ + char *proto; /*!< srevice protocol */ + // SRV + char *hostname; /*!< hostname */ + uint16_t port; /*!< service port */ + // TXT + mdns_txt_item_t *txt; /*!< txt record */ + uint8_t *txt_value_len; /*!< array of txt value len of each record */ + size_t txt_count; /*!< number of txt items */ + // A and AAAA + mdns_ip_addr_t *addr; /*!< linked list of IP addresses found */ +} mdns_result_t; + +typedef void (*mdns_query_notify_t)(mdns_search_once_t *search); +typedef void (*mdns_browse_notify_t)(mdns_result_t *result); + +/** + * @brief Initialize mDNS on given interface + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE when failed to register event handler + * - ESP_ERR_NO_MEM on memory error + * - ESP_FAIL when failed to start mdns task + */ +esp_err_t mdns_init(void); + +/** + * @brief Stop and free mDNS server + * + */ +void mdns_free(void); + +/** + * @brief Set the hostname for mDNS server + * required if you want to advertise services + * + * @param hostname Hostname to set + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_hostname_set(const char *hostname); + +/** + * @brief Get the hostname for mDNS server + * + * @param hostname pointer to the hostname, it should be allocated + * and hold at least MDNS_NAME_BUF_LEN chars + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_INVALID_STATE when mdns is not initialized + */ +esp_err_t mdns_hostname_get(char *hostname); + +/** + * @brief Adds a hostname and address to be delegated + * A/AAAA queries will be replied for the hostname and + * services can be added to this host. + * + * @param hostname Hostname to add + * @param address_list The IP address list of the host + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + * + */ +esp_err_t mdns_delegate_hostname_add(const char *hostname, const mdns_ip_addr_t *address_list); + +/** + * @brief Set the address to a delegated hostname + * + * @param hostname Hostname to set + * @param address_list The IP address list of the host + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + * + */ +esp_err_t mdns_delegate_hostname_set_address(const char *hostname, const mdns_ip_addr_t *address_list); + +/** + * @brief Remove a delegated hostname + * All the services added to this host will also be removed. + * + * @param hostname Hostname to remove + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + * + */ +esp_err_t mdns_delegate_hostname_remove(const char *hostname); + +/** + * @brief Query whether a hostname has been added + * + * @param hostname Hostname to query + * + * @return + * - true The hostname has been added. + * - false The hostname has not been added. + * + */ +bool mdns_hostname_exists(const char *hostname); + +/** + * @brief Set the default instance name for mDNS server + * + * @param instance_name Instance name to set + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_instance_name_set(const char *instance_name); + +/** + * @brief Add service to mDNS server + * + * @note The value length of txt items will be automatically decided by strlen + * + * @param instance_name instance name to set. If NULL, + * global instance name or hostname will be used. + * Note that MDNS_MULTIPLE_INSTANCE config option + * needs to be enabled for adding multiple instances + * with the same instance type. + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param port service port + * @param txt string array of TXT data (eg. {{"var","val"},{"other","2"}}) + * @param num_items number of items in TXT data + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + * - ESP_FAIL failed to add service + */ +esp_err_t mdns_service_add(const char *instance_name, const char *service_type, const char *proto, uint16_t port, mdns_txt_item_t txt[], size_t num_items); + +/** + * @brief Add service to mDNS server with a delegated hostname + * + * @note The value length of txt items will be automatically decided by strlen + * + * @param instance_name instance name to set. If NULL, + * global instance name or hostname will be used + * Note that MDNS_MULTIPLE_INSTANCE config option + * needs to be enabled for adding multiple instances + * with the same instance type. + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param port service port + * @param txt string array of TXT data (eg. {{"var","val"},{"other","2"}}) + * @param num_items number of items in TXT data + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM memory error + * - ESP_FAIL failed to add service + */ +esp_err_t mdns_service_add_for_host(const char *instance_name, const char *service_type, const char *proto, + const char *hostname, uint16_t port, mdns_txt_item_t txt[], size_t num_items); + +/** + * @brief Check whether a service has been added. + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, checks for the local hostname. + * + * @return + * - true Correspondding service has been added. + * - false Service not found. + */ +bool mdns_service_exists(const char *service_type, const char *proto, const char *hostname); + +/** + * @brief Check whether a service has been added. + * + * @param instance instance name + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, checks for the local hostname. + * + * @return + * - true Correspondding service has been added. + * - false Service not found. + */ +bool mdns_service_exists_with_instance(const char *instance, const char *service_type, const char *proto, + const char *hostname); + +/** + * @brief Remove service from mDNS server + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_remove(const char *service_type, const char *proto); + +/** + * @brief Remove service from mDNS server with hostname + * + * @param instance instance name + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_remove_for_host(const char *instance, const char *service_type, const char *proto, const char *hostname); + +/** + * @brief Set instance name for service + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param instance_name instance name to set + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_instance_name_set(const char *service_type, const char *proto, const char *instance_name); + +/** + * @brief Set instance name for service with hostname + * + * @param instance_old original instance name + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param instance_name instance name to set + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_instance_name_set_for_host(const char *instance_old, const char *service_type, const char *proto, const char *hostname, + const char *instance_name); + +/** + * @brief Set service port + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param port service port + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_port_set(const char *service_type, const char *proto, uint16_t port); + + +/** + * @brief Set service port with hostname + * + * @param instance instance name + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param port service port + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_port_set_for_host(const char *instance, const char *service_type, const char *proto, const char *hostname, + uint16_t port); + +/** + * @brief Replace all TXT items for service + * + * @note The value length of txt items will be automatically decided by strlen + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param txt array of TXT data (eg. {{"var","val"},{"other","2"}}) + * @param num_items number of items in TXT data + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_txt_set(const char *service_type, const char *proto, mdns_txt_item_t txt[], uint8_t num_items); + +/** + * @brief Replace all TXT items for service with hostname + * + * @note The value length of txt items will be automatically decided by strlen + * + * @param instance instance name + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param txt array of TXT data (eg. {{"var","val"},{"other","2"}}) + * @param num_items number of items in TXT data + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_txt_set_for_host(const char *instance, const char *service_type, const char *proto, const char *hostname, + mdns_txt_item_t txt[], uint8_t num_items); + +/** + * @brief Set/Add TXT item for service TXT record + * + * @note The value length will be automatically decided by strlen + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param key the key that you want to add/update + * @param value the new value of the key + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_txt_item_set(const char *service_type, const char *proto, const char *key, const char *value); + +/** + * @brief Set/Add TXT item for service TXT record + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param key the key that you want to add/update + * @param value the new value of the key + * @param value_len the length of the value + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_txt_item_set_with_explicit_value_len(const char *service_type, const char *proto, + const char *key, const char *value, uint8_t value_len); + +/** + * @brief Set/Add TXT item for service TXT record with hostname + * + * @note The value length will be automatically decided by strlen + * + * @param instance instance name + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param key the key that you want to add/update + * @param value the new value of the key + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_txt_item_set_for_host(const char *instance, const char *service_type, const char *proto, const char *hostname, + const char *key, const char *value); + +/** + * @brief Set/Add TXT item for service TXT record with hostname and txt value length + * + * @param instance instance name + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param key the key that you want to add/update + * @param value the new value of the key + * @param value_len the length of the value + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_txt_item_set_for_host_with_explicit_value_len(const char *instance, const char *service_type, const char *proto, + const char *hostname, const char *key, + const char *value, uint8_t value_len); + +/** + * @brief Remove TXT item for service TXT record + * + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param key the key that you want to remove + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_txt_item_remove(const char *service_type, const char *proto, const char *key); + +/** + * @brief Remove TXT item for service TXT record with hostname + * + * @param instance instance name + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param key the key that you want to remove + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_txt_item_remove_for_host(const char *instance, const char *service_type, const char *proto, const char *hostname, + const char *key); + +/** + * @brief Add a subtype for service. + * + * @param instance_name instance name. If NULL, will find the first service with the same service type and protocol. + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param subtype The subtype to add. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_subtype_add_for_host(const char *instance_name, const char *service_type, const char *proto, + const char *hostname, const char *subtype); + +/** + * @brief Remove a subtype for service. + * + * @param instance_name instance name. If NULL, will find the first service with the same service type and protocol. + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param subtype The subtype to remove. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + */ +esp_err_t mdns_service_subtype_remove_for_host(const char *instance_name, const char *service_type, const char *proto, + const char *hostname, const char *subtype); + +/** + * @brief Add multiple subtypes for service at once. + * + * @param instance_name instance name. If NULL, will find the first service with the same service type and protocol. + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param subtype the pointer of subtype array to add. + * @param num_items number of items in subtype array + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_subtype_add_multiple_items_for_host(const char *instance_name, const char *service_type, const char *proto, + const char *hostname, mdns_subtype_item_t subtype[], uint8_t num_items); + +/** + * @brief Update subtype for service. + * + * @param instance_name instance name. If NULL, will find the first service with the same service type and protocol. + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param hostname service hostname. If NULL, local hostname will be used. + * @param subtype the pointer of subtype array to add. + * @param num_items number of items in subtype array + * + * @note If `num_items` is 0, then remove all subtypes. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NOT_FOUND Service not found + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_service_subtype_update_multiple_items_for_host(const char *instance_name, const char *service_type, const char *proto, + const char *hostname, mdns_subtype_item_t subtype[], uint8_t num_items); +/** + * @brief Remove and free all services from mDNS server + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t mdns_service_remove_all(void); + +/** + * @brief Deletes the finished query. Call this only after the search has ended! + * + * @param search pointer to search object + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE search has not finished + * - ESP_ERR_INVALID_ARG pointer to search object is NULL + */ +esp_err_t mdns_query_async_delete(mdns_search_once_t *search); + +/** + * @brief Get results from search pointer. Results available as a pointer to the output parameter. + * Pointer to search object has to be deleted via `mdns_query_async_delete` once the query has finished. + * The results although have to be freed manually. + * + * @param search pointer to search object + * @param timeout time in milliseconds to wait for answers + * @param results pointer to the results of the query + * @param num_results pointer to the number of the actual result items (set to NULL to ignore this return value) + * + * @return + * True if search has finished before or at timeout + * False if search timeout is over + */ +bool mdns_query_async_get_results(mdns_search_once_t *search, uint32_t timeout, mdns_result_t **results, uint8_t *num_results); + +/** + * @brief Query mDNS for host or service asynchronousely. + * Search has to be tested for progress and deleted manually! + * + * @param name service instance or host name (NULL for PTR queries) + * @param service_type service type (_http, _arduino, _ftp etc.) (NULL for host queries) + * @param proto service protocol (_tcp, _udp, etc.) (NULL for host queries) + * @param type type of query (MDNS_TYPE_*) + * @param timeout time in milliseconds during which mDNS query is active + * @param max_results maximum results to be collected + * @param notifier Notification function to be called when the result is ready, can be NULL + * + * @return mdns_search_once_s pointer to new search object if query initiated successfully. + * NULL otherwise. + */ +mdns_search_once_t *mdns_query_async_new(const char *name, const char *service_type, const char *proto, uint16_t type, + uint32_t timeout, size_t max_results, mdns_query_notify_t notifier); + +/** + * @brief Generic mDNS query + * All following query methods are derived from this one + * + * @param name service instance or host name (NULL for PTR queries) + * @param service_type service type (_http, _arduino, _ftp etc.) (NULL for host queries) + * @param proto service protocol (_tcp, _udp, etc.) (NULL for host queries) + * @param type type of query (MDNS_TYPE_*) + * @param transmission_type either Unicast query, or Multicast query + * @param timeout time in milliseconds to wait for answers. + * @param max_results maximum results to be collected + * @param results pointer to the results of the query + * results must be freed using mdns_query_results_free below + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_NO_MEM memory error + * - ESP_ERR_INVALID_ARG timeout was not given + */ +esp_err_t mdns_query_generic(const char *name, const char *service_type, const char *proto, uint16_t type, + mdns_query_transmission_type_t transmission_type, uint32_t timeout, size_t max_results, mdns_result_t **results); + +/** + * @brief Query mDNS for host or service + * + * Note that querying PTR types sends Multicast query, all other types send Unicast queries + * + * @param name service instance or host name (NULL for PTR queries) + * @param service_type service type (_http, _arduino, _ftp etc.) (NULL for host queries) + * @param proto service protocol (_tcp, _udp, etc.) (NULL for host queries) + * @param type type of query (MDNS_TYPE_*) + * @param timeout time in milliseconds to wait for answers. + * @param max_results maximum results to be collected + * @param results pointer to the results of the query + * results must be freed using mdns_query_results_free below + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_NO_MEM memory error + * - ESP_ERR_INVALID_ARG timeout was not given + */ +esp_err_t mdns_query(const char *name, const char *service_type, const char *proto, uint16_t type, uint32_t timeout, size_t max_results, mdns_result_t **results); + +/** + * @brief Free query results + * + * @param results linked list of results to be freed + */ +void mdns_query_results_free(mdns_result_t *results); + +/** + * @brief Query mDNS for service + * + * @param service_type service type (_http, _arduino, _ftp etc.) + * @param proto service protocol (_tcp, _udp, etc.) + * @param timeout time in milliseconds to wait for answer. + * @param max_results maximum results to be collected + * @param results pointer to the results of the query + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_NO_MEM memory error + * - ESP_ERR_INVALID_ARG parameter error + */ +esp_err_t mdns_query_ptr(const char *service_type, const char *proto, uint32_t timeout, size_t max_results, mdns_result_t **results); + +/** + * @brief Query mDNS for SRV record + * + * @param instance_name service instance name + * @param service_type service type (_http, _arduino, _ftp etc.) + * @param proto service protocol (_tcp, _udp, etc.) + * @param timeout time in milliseconds to wait for answer. + * @param result pointer to the result of the query + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_NO_MEM memory error + * - ESP_ERR_INVALID_ARG parameter error + */ +esp_err_t mdns_query_srv(const char *instance_name, const char *service_type, const char *proto, uint32_t timeout, mdns_result_t **result); + +/** + * @brief Query mDNS for TXT record + * + * @param instance_name service instance name + * @param service_type service type (_http, _arduino, _ftp etc.) + * @param proto service protocol (_tcp, _udp, etc.) + * @param timeout time in milliseconds to wait for answer. + * @param result pointer to the result of the query + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_NO_MEM memory error + * - ESP_ERR_INVALID_ARG parameter error + */ +esp_err_t mdns_query_txt(const char *instance_name, const char *service_type, const char *proto, uint32_t timeout, mdns_result_t **result); + +/** + * @brief Look up delegated services. + * + * @param instance instance name (NULL for uncertain instance) + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param max_results maximum results to be collected + * @param result pointer to the result of the search + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_NO_MEM memory error + * - ESP_ERR_INVALID_ARG parameter error + */ +esp_err_t mdns_lookup_delegated_service(const char *instance, const char *service_type, const char *proto, size_t max_results, + mdns_result_t **result); + +/** + * @brief Look up self hosted services. + * + * @param instance instance name (NULL for uncertain instance) + * @param service_type service type (_http, _ftp, etc) + * @param proto service protocol (_tcp, _udp) + * @param max_results maximum results to be collected + * @param result pointer to the result of the search + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_NO_MEM memory error + * - ESP_ERR_INVALID_ARG parameter error + */ +esp_err_t mdns_lookup_selfhosted_service(const char *instance, const char *service_type, const char *proto, size_t max_results, + mdns_result_t **result); + +/** + * @brief Query mDNS for A record + * + * @param host_name host name to look for + * @param timeout time in milliseconds to wait for answer. + * @param addr pointer to the resulting IP4 address + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_NO_MEM memory error + * - ESP_ERR_INVALID_ARG parameter error + */ +esp_err_t mdns_query_a(const char *host_name, uint32_t timeout, esp_ip4_addr_t *addr); + +#if CONFIG_LWIP_IPV6 +/** + * @brief Query mDNS for A record + * + * Please note that hostname must not contain domain name, as mDNS uses '.local' domain. + * + * @param host_name host name to look for + * @param timeout time in milliseconds to wait for answer. If 0, max_results needs to be defined + * @param addr pointer to the resulting IP6 address + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_NO_MEM memory error + * - ESP_ERR_INVALID_ARG parameter error + */ +esp_err_t mdns_query_aaaa(const char *host_name, uint32_t timeout, esp_ip6_addr_t *addr); +#endif + + +/** + * @brief Register custom esp_netif with mDNS functionality + * mDNS service runs by default on preconfigured interfaces (STA, AP, ETH). + * This API enables running the service on any customized interface, + * either using standard WiFi or Ethernet driver or any kind of user defined driver. + * + * @param esp_netif Pointer to esp-netif interface + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running or this netif is already registered + * - ESP_ERR_NO_MEM not enough memory for this in interface in the netif list (see CONFIG_MDNS_MAX_INTERFACES) + */ +esp_err_t mdns_register_netif(esp_netif_t *esp_netif); + +/** + * @brief Unregister esp-netif already registered in mDNS service + * + * @param esp_netif Pointer to esp-netif interface + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running + * - ESP_ERR_NOT_FOUND this esp-netif was not registered in mDNS service + */ +esp_err_t mdns_unregister_netif(esp_netif_t *esp_netif); + +/** + * @brief Set esp_netif to a desired state, or perform a desired action, such as enable/disable this interface + * or send announcement packets to this netif + * + * * This function is used to enable (probe, resolve conflicts and announce), announce, or disable (send bye) mDNS + * services on the specified network interface. + * * This function must be called if users registers a specific interface using mdns_register_netif() + * to enable mDNS services on that interface. + * * This function could be used in IP/connection event handlers to automatically enable/announce mDNS services + * when network properties change and/or disable them on disconnection. + * + * @param esp_netif Pointer to esp-netif interface + * @param event_action Disable/Enable/Announce on this interface over IPv4/IPv6 protocol. + * Actions enumerated in mdns_event_actions_t type. + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_STATE mDNS is not running or this netif is not registered + * - ESP_ERR_NO_MEM memory error + */ +esp_err_t mdns_netif_action(esp_netif_t *esp_netif, mdns_event_actions_t event_action); + +/** + * @brief Browse mDNS for a service `_service._proto`. + * + * @param service Pointer to the `_service` which will be browsed. + * @param proto Pointer to the `_proto` which will be browsed. + * @param notifier The callback which will be called when the browsing service changed. + * @return mdns_browse_t pointer to new browse object if initiated successfully. + * NULL otherwise. + */ +mdns_browse_t *mdns_browse_new(const char *service, const char *proto, mdns_browse_notify_t notifier); + +/** + * @brief Stop the `_service._proto` browse. + * @param service Pointer to the `_service` which will be browsed. + * @param proto Pointer to the `_proto` which will be browsed. + * @return + * - ESP_OK success. + * - ESP_ERR_FAIL mDNS is not running or the browsing of `_service._proto` is never started. + * - ESP_ERR_NO_MEM memory error. + */ +esp_err_t mdns_browse_delete(const char *service, const char *proto); + +#ifdef __cplusplus +} +#endif + +#endif /* ESP_MDNS_H_ */ diff --git a/code/components/mdns/include/mdns_console.h b/code/components/mdns/include/mdns_console.h new file mode 100644 index 00000000..cb28eca7 --- /dev/null +++ b/code/components/mdns/include/mdns_console.h @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _MDNS_CONSOLE_H_ +#define _MDNS_CONSOLE_H_ + +/** + * @brief Register MDNS functions with the console component + */ +void mdns_console_register(void); + +#endif /* _MDNS_CONSOLE_H_ */ diff --git a/code/components/mdns/mdns.c b/code/components/mdns/mdns.c new file mode 100644 index 00000000..c1d41de0 --- /dev/null +++ b/code/components/mdns/mdns.c @@ -0,0 +1,7699 @@ +/* + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_event.h" +#include "esp_random.h" +#include "esp_check.h" +#include "mdns.h" +#include "mdns_private.h" +#include "mdns_networking.h" + +static void _mdns_browse_item_free(mdns_browse_t *browse); +static esp_err_t _mdns_send_browse_action(mdns_action_type_t type, mdns_browse_t *browse); +static esp_err_t _mdns_sync_browse_action(mdns_action_type_t type, mdns_browse_sync_t *browse_sync); +static void _mdns_browse_sync(mdns_browse_sync_t *browse_sync); +static void _mdns_browse_finish(mdns_browse_t *browse); +static void _mdns_browse_add(mdns_browse_t *browse); +static void _mdns_browse_send(mdns_browse_t *browse, mdns_if_t interface); + +#if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH +#include "esp_eth.h" +#endif + +#if ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(5, 1, 0) +#define MDNS_ESP_WIFI_ENABLED CONFIG_SOC_WIFI_SUPPORTED +#else +#define MDNS_ESP_WIFI_ENABLED CONFIG_ESP_WIFI_ENABLED +#endif + +#if MDNS_ESP_WIFI_ENABLED && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP) +#include "esp_wifi.h" +#endif + +#ifdef MDNS_ENABLE_DEBUG +void mdns_debug_packet(const uint8_t *data, size_t len); +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) +#endif + +// Internal size of IPv6 address is defined here as size of AAAA record in mdns packet +// since the ip6_addr_t is defined in lwip and depends on using IPv6 zones +#define _MDNS_SIZEOF_IP6_ADDR (MDNS_ANSWER_AAAA_SIZE) + +static const char *MDNS_DEFAULT_DOMAIN = "local"; +static const char *MDNS_SUB_STR = "_sub"; + +mdns_server_t *_mdns_server = NULL; +static mdns_host_item_t *_mdns_host_list = NULL; +static mdns_host_item_t _mdns_self_host; + +static const char *TAG = "mdns"; + +static volatile TaskHandle_t _mdns_service_task_handle = NULL; +static SemaphoreHandle_t _mdns_service_semaphore = NULL; + +static void _mdns_search_finish_done(void); +static mdns_search_once_t *_mdns_search_find_from(mdns_search_once_t *search, mdns_name_t *name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol); +static mdns_browse_t *_mdns_browse_find_from(mdns_browse_t *b, mdns_name_t *name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol); +static void _mdns_browse_result_add_srv(mdns_browse_t *browse, const char *hostname, const char *instance, const char *service, const char *proto, + uint16_t port, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl, mdns_browse_sync_t *out_sync_browse); +static void _mdns_browse_result_add_ip(mdns_browse_t *browse, const char *hostname, esp_ip_addr_t *ip, + mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl, mdns_browse_sync_t *out_sync_browse); +static void _mdns_browse_result_add_txt(mdns_browse_t *browse, const char *instance, const char *service, const char *proto, + mdns_txt_item_t *txt, uint8_t *txt_value_len, size_t txt_count, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, + uint32_t ttl, mdns_browse_sync_t *out_sync_browse); +#ifdef MDNS_ENABLE_DEBUG +static void debug_printf_browse_result(mdns_result_t *r_t, mdns_browse_t *b_t); +static void debug_printf_browse_result_all(mdns_result_t *r_t); +#endif // MDNS_ENABLE_DEBUG +static void _mdns_search_result_add_ip(mdns_search_once_t *search, const char *hostname, esp_ip_addr_t *ip, + mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl); +static void _mdns_search_result_add_srv(mdns_search_once_t *search, const char *hostname, uint16_t port, + mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl); +static void _mdns_search_result_add_txt(mdns_search_once_t *search, mdns_txt_item_t *txt, uint8_t *txt_value_len, + size_t txt_count, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, + uint32_t ttl); +static mdns_result_t *_mdns_search_result_add_ptr(mdns_search_once_t *search, const char *instance, + const char *service_type, const char *proto, mdns_if_t tcpip_if, + mdns_ip_protocol_t ip_protocol, uint32_t ttl); +static bool _mdns_append_host_list_in_services(mdns_out_answer_t **destination, mdns_srv_item_t *services[], size_t services_len, bool flush, bool bye); +static bool _mdns_append_host_list(mdns_out_answer_t **destination, bool flush, bool bye); +static void _mdns_remap_self_service_hostname(const char *old_hostname, const char *new_hostname); +static esp_err_t mdns_post_custom_action_tcpip_if(mdns_if_t mdns_if, mdns_event_actions_t event_action); + +static void _mdns_query_results_free(mdns_result_t *results); +typedef enum { + MDNS_IF_STA = 0, + MDNS_IF_AP = 1, + MDNS_IF_ETH = 2, +} mdns_predef_if_t; + +typedef struct mdns_interfaces mdns_interfaces_t; + +struct mdns_interfaces { + const bool predefined; + esp_netif_t *netif; + const mdns_predef_if_t predef_if; + mdns_if_t duplicate; +}; + +/* + * @brief Internal collection of mdns supported interfaces + * + */ +static mdns_interfaces_t s_esp_netifs[MDNS_MAX_INTERFACES] = { +#if CONFIG_MDNS_PREDEF_NETIF_STA + { .predefined = true, .netif = NULL, .predef_if = MDNS_IF_STA, .duplicate = MDNS_MAX_INTERFACES }, +#endif +#if CONFIG_MDNS_PREDEF_NETIF_AP + { .predefined = true, .netif = NULL, .predef_if = MDNS_IF_AP, .duplicate = MDNS_MAX_INTERFACES }, +#endif +#if CONFIG_MDNS_PREDEF_NETIF_ETH + { .predefined = true, .netif = NULL, .predef_if = MDNS_IF_ETH, .duplicate = MDNS_MAX_INTERFACES }, +#endif +}; + + +/** + * @brief Convert Predefined interface to the netif id from the internal netif list + * @param predef_if Predefined interface enum + * @return Ordinal number of internal list of mdns network interface. + * Returns MDNS_MAX_INTERFACES if the predefined interface wasn't found in the list + */ +static mdns_if_t mdns_if_from_preset_if(mdns_predef_if_t predef_if) +{ + for (int i = 0; i < MDNS_MAX_INTERFACES; ++i) { + if (s_esp_netifs[i].predefined && s_esp_netifs[i].predef_if == predef_if) { + return i; + } + } + return MDNS_MAX_INTERFACES; +} + +/** + * @brief Convert Predefined interface to esp-netif handle + * @param predef_if Predefined interface enum + * @return esp_netif pointer from system list of network interfaces + */ +static inline esp_netif_t *esp_netif_from_preset_if(mdns_predef_if_t predef_if) +{ + switch (predef_if) { + case MDNS_IF_STA: + return esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); + case MDNS_IF_AP: + return esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"); +#if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH + case MDNS_IF_ETH: + return esp_netif_get_handle_from_ifkey("ETH_DEF"); +#endif + default: + return NULL; + } +} + +/** + * @brief Gets the actual esp_netif pointer from the internal network interface list + * + * The supplied ordinal number could + * - point to a predef netif -> "STA", "AP", "ETH" + * - if no entry in the list (NULL) -> check if the system added this netif + * - point to a custom netif -> just return the entry in the list + * - users is responsible for the lifetime of this netif (to be valid between mdns-init -> deinit) + * + * @param tcpip_if Ordinal number of the interface + * @return Pointer ot the esp_netif object if the interface is available, NULL otherwise + */ +esp_netif_t *_mdns_get_esp_netif(mdns_if_t tcpip_if) +{ + if (tcpip_if < MDNS_MAX_INTERFACES) { + if (s_esp_netifs[tcpip_if].netif == NULL && s_esp_netifs[tcpip_if].predefined) { + // If the local copy is NULL and this netif is predefined -> we can find it in the global netif list + s_esp_netifs[tcpip_if].netif = esp_netif_from_preset_if(s_esp_netifs[tcpip_if].predef_if); + // failing to find it means that the netif is *not* available -> return NULL + } + return s_esp_netifs[tcpip_if].netif; + } + return NULL; +} + + +/* + * @brief Clean internal mdns interface's pointer + */ +static inline void _mdns_clean_netif_ptr(mdns_if_t tcpip_if) +{ + if (tcpip_if < MDNS_MAX_INTERFACES) { + s_esp_netifs[tcpip_if].netif = NULL; + } +} + + +/* + * @brief Convert esp-netif handle to mdns if + */ +static mdns_if_t _mdns_get_if_from_esp_netif(esp_netif_t *esp_netif) +{ + for (int i = 0; i < MDNS_MAX_INTERFACES; ++i) { + // The predefined netifs in the static array are NULL when firstly calling this function + // if IPv4 is disabled. Set these netifs here. + if (s_esp_netifs[i].netif == NULL && s_esp_netifs[i].predefined) { + s_esp_netifs[i].netif = esp_netif_from_preset_if(s_esp_netifs[i].predef_if); + } + if (esp_netif == s_esp_netifs[i].netif) { + return i; + } + } + return MDNS_MAX_INTERFACES; +} + + + +static inline bool _str_null_or_empty(const char *str) +{ + return (str == NULL || *str == 0); +} + +/* + * @brief Appends/increments a number to name/instance in case of collision + * */ +static char *_mdns_mangle_name(char *in) +{ + char *p = strrchr(in, '-'); + int suffix = 0; + if (p == NULL) { + //No - in ``in`` + suffix = 2; + } else { + char *endp = NULL; + suffix = strtol(p + 1, &endp, 10); + if (*endp != 0) { + //suffix is not numerical + suffix = 2; + p = NULL; //so we append -suffix to the entire string + } + } + char *ret; + if (p == NULL) { + //need to add -2 to string + ret = malloc(strlen(in) + 3); + if (ret == NULL) { + HOOK_MALLOC_FAILED; + return NULL; + } + sprintf(ret, "%s-2", in); + } else { + ret = malloc(strlen(in) + 2); //one extra byte in case 9-10 or 99-100 etc + if (ret == NULL) { + HOOK_MALLOC_FAILED; + return NULL; + } + strcpy(ret, in); + int baseLen = p - in; //length of 'bla' in 'bla-123' + //overwrite suffix with new suffix + sprintf(ret + baseLen, "-%d", suffix + 1); + } + return ret; +} + +static bool _mdns_service_match(const mdns_service_t *srv, const char *service, const char *proto, + const char *hostname) +{ + if (!service || !proto || !srv->hostname) { + return false; + } + return !strcasecmp(srv->service, service) && !strcasecmp(srv->proto, proto) && + (_str_null_or_empty(hostname) || !strcasecmp(srv->hostname, hostname)); +} + +/** + * @brief finds service from given service type + * @param server the server + * @param service service type to match + * @param proto proto to match + * @param hostname hostname of the service (if non-null) + * + * @return the service item if found or NULL on error + */ +static mdns_srv_item_t *_mdns_get_service_item(const char *service, const char *proto, const char *hostname) +{ + mdns_srv_item_t *s = _mdns_server->services; + while (s) { + if (_mdns_service_match(s->service, service, proto, hostname)) { + return s; + } + s = s->next; + } + return NULL; +} + +static mdns_srv_item_t *_mdns_get_service_item_subtype(const char *subtype, const char *service, const char *proto) +{ + mdns_srv_item_t *s = _mdns_server->services; + while (s) { + if (_mdns_service_match(s->service, service, proto, NULL)) { + mdns_subtype_t *subtype_item = s->service->subtype; + while (subtype_item) { + if (!strcasecmp(subtype_item->subtype, subtype)) { + return s; + } + subtype_item = subtype_item->next; + } + } + s = s->next; + } + return NULL; +} + +static mdns_host_item_t *mdns_get_host_item(const char *hostname) +{ + if (hostname == NULL || strcasecmp(hostname, _mdns_server->hostname) == 0) { + return &_mdns_self_host; + } + mdns_host_item_t *host = _mdns_host_list; + while (host != NULL) { + if (strcasecmp(host->hostname, hostname) == 0) { + return host; + } + host = host->next; + } + return NULL; +} + +static bool _mdns_can_add_more_services(void) +{ +#if MDNS_MAX_SERVICES == 0 + return false; +#else + mdns_srv_item_t *s = _mdns_server->services; + uint16_t service_num = 0; + while (s) { + service_num ++; + s = s->next; + if (service_num >= MDNS_MAX_SERVICES) { + return false; + } + } + return true; +#endif +} + +esp_err_t _mdns_send_rx_action(mdns_rx_packet_t *packet) +{ + mdns_action_t *action = NULL; + + action = (mdns_action_t *)malloc(sizeof(mdns_action_t)); + if (!action) { + HOOK_MALLOC_FAILED; + return ESP_ERR_NO_MEM; + } + + action->type = ACTION_RX_HANDLE; + action->data.rx_handle.packet = packet; + if (xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0) != pdPASS) { + free(action); + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +static const char *_mdns_get_default_instance_name(void) +{ + if (_mdns_server && !_str_null_or_empty(_mdns_server->instance)) { + return _mdns_server->instance; + } + + if (_mdns_server && !_str_null_or_empty(_mdns_server->hostname)) { + return _mdns_server->hostname; + } + + return NULL; +} + +/** + * @brief Get the service name of a service + */ +static const char *_mdns_get_service_instance_name(const mdns_service_t *service) +{ + if (service && !_str_null_or_empty(service->instance)) { + return service->instance; + } + + return _mdns_get_default_instance_name(); +} + +static bool _mdns_instance_name_match(const char *lhs, const char *rhs) +{ + if (lhs == NULL) { + lhs = _mdns_get_default_instance_name(); + } + if (rhs == NULL) { + rhs = _mdns_get_default_instance_name(); + } + return !strcasecmp(lhs, rhs); +} + +static bool _mdns_service_match_instance(const mdns_service_t *srv, const char *instance, const char *service, + const char *proto, const char *hostname) +{ + // service and proto must be supplied, if not this instance won't match + if (!service || !proto) { + return false; + } + // instance==NULL -> _mdns_instance_name_match() will check the default instance + // hostname==NULL -> matches if instance, service and proto matches + return !strcasecmp(srv->service, service) && _mdns_instance_name_match(srv->instance, instance) && + !strcasecmp(srv->proto, proto) && (_str_null_or_empty(hostname) || !strcasecmp(srv->hostname, hostname)); +} + +static mdns_srv_item_t *_mdns_get_service_item_instance(const char *instance, const char *service, const char *proto, + const char *hostname) +{ + mdns_srv_item_t *s = _mdns_server->services; + while (s) { + if (instance) { + if (_mdns_service_match_instance(s->service, instance, service, proto, hostname)) { + return s; + } + } else { + if (_mdns_service_match(s->service, service, proto, hostname)) { + return s; + } + } + s = s->next; + } + return NULL; +} + +/** + * @brief reads MDNS FQDN into mdns_name_t structure + * FQDN is in format: [hostname.|[instance.]_service._proto.]local. + * + * @param packet MDNS packet + * @param start Starting point of FQDN + * @param name mdns_name_t structure to populate + * @param buf temporary char buffer + * + * @return the address after the parsed FQDN in the packet or NULL on error + */ +static const uint8_t *_mdns_read_fqdn(const uint8_t *packet, const uint8_t *start, mdns_name_t *name, char *buf, size_t packet_len) +{ + size_t index = 0; + const uint8_t *packet_end = packet + packet_len; + while (start + index < packet_end && start[index]) { + if (name->parts == 4) { + name->invalid = true; + } + uint8_t len = start[index++]; + if (len < 0xC0) { + if (len > 63) { + //length can not be more than 63 + return NULL; + } + uint8_t i; + for (i = 0; i < len; i++) { + if (start + index >= packet_end) { + return NULL; + } + buf[i] = start[index++]; + } + buf[len] = '\0'; + if (name->parts == 1 && buf[0] != '_' + && (strcasecmp(buf, MDNS_DEFAULT_DOMAIN) != 0) + && (strcasecmp(buf, "arpa") != 0) +#ifndef CONFIG_MDNS_RESPOND_REVERSE_QUERIES + && (strcasecmp(buf, "ip6") != 0) + && (strcasecmp(buf, "in-addr") != 0) +#endif + ) { + strlcat(name->host, ".", sizeof(name->host)); + strlcat(name->host, buf, sizeof(name->host)); + } else if (strcasecmp(buf, MDNS_SUB_STR) == 0) { + name->sub = 1; + } else if (!name->invalid) { + char *mdns_name_ptrs[] = {name->host, name->service, name->proto, name->domain}; + memcpy(mdns_name_ptrs[name->parts++], buf, len + 1); + } + } else { + size_t address = (((uint16_t)len & 0x3F) << 8) | start[index++]; + if ((packet + address) >= start) { + //reference address can not be after where we are + return NULL; + } + if (_mdns_read_fqdn(packet, packet + address, name, buf, packet_len)) { + return start + index; + } + return NULL; + } + } + return start + index + 1; +} + +/** + * @brief sets uint16_t value in a packet + * + * @param packet MDNS packet + * @param index offset of uint16_t value + * @param value the value to set + */ +static inline void _mdns_set_u16(uint8_t *packet, uint16_t index, uint16_t value) +{ + if ((index + 1) >= MDNS_MAX_PACKET_SIZE) { + return; + } + packet[index] = (value >> 8) & 0xFF; + packet[index + 1] = value & 0xFF; +} + +/** + * @brief appends byte in a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param value the value to set + * + * @return length of added data: 0 on error or 1 on success + */ +static inline uint8_t _mdns_append_u8(uint8_t *packet, uint16_t *index, uint8_t value) +{ + if (*index >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + packet[*index] = value; + *index += 1; + return 1; +} + +/** + * @brief appends uint16_t in a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param value the value to set + * + * @return length of added data: 0 on error or 2 on success + */ +static inline uint8_t _mdns_append_u16(uint8_t *packet, uint16_t *index, uint16_t value) +{ + if ((*index + 1) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, (value >> 8) & 0xFF); + _mdns_append_u8(packet, index, value & 0xFF); + return 2; +} + +/** + * @brief appends uint32_t in a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param value the value to set + * + * @return length of added data: 0 on error or 4 on success + */ +static inline uint8_t _mdns_append_u32(uint8_t *packet, uint16_t *index, uint32_t value) +{ + if ((*index + 3) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, (value >> 24) & 0xFF); + _mdns_append_u8(packet, index, (value >> 16) & 0xFF); + _mdns_append_u8(packet, index, (value >> 8) & 0xFF); + _mdns_append_u8(packet, index, value & 0xFF); + return 4; +} + +/** + * @brief appends answer type, class, ttl and data length to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param type answer type + * @param ttl answer ttl + * + * @return length of added data: 0 on error or 10 on success + */ +static inline uint8_t _mdns_append_type(uint8_t *packet, uint16_t *index, uint8_t type, bool flush, uint32_t ttl) +{ + if ((*index + 10) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + uint16_t mdns_class = MDNS_CLASS_IN; + if (flush) { + mdns_class = MDNS_CLASS_IN_FLUSH_CACHE; + } + if (type == MDNS_ANSWER_PTR) { + _mdns_append_u16(packet, index, MDNS_TYPE_PTR); + _mdns_append_u16(packet, index, mdns_class); + } else if (type == MDNS_ANSWER_TXT) { + _mdns_append_u16(packet, index, MDNS_TYPE_TXT); + _mdns_append_u16(packet, index, mdns_class); + } else if (type == MDNS_ANSWER_SRV) { + _mdns_append_u16(packet, index, MDNS_TYPE_SRV); + _mdns_append_u16(packet, index, mdns_class); + } else if (type == MDNS_ANSWER_A) { + _mdns_append_u16(packet, index, MDNS_TYPE_A); + _mdns_append_u16(packet, index, mdns_class); + } else if (type == MDNS_ANSWER_AAAA) { + _mdns_append_u16(packet, index, MDNS_TYPE_AAAA); + _mdns_append_u16(packet, index, mdns_class); + } else { + return 0; + } + _mdns_append_u32(packet, index, ttl); + _mdns_append_u16(packet, index, 0); + return 10; +} + +static inline uint8_t _mdns_append_string_with_len(uint8_t *packet, uint16_t *index, const char *string, uint8_t len) +{ + if ((*index + len + 1) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, len); + memcpy(packet + *index, string, len); + *index += len; + return len + 1; +} + +/** + * @brief appends single string to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param string the string to append + * + * @return length of added data: 0 on error or length of the string + 1 on success + */ +static inline uint8_t _mdns_append_string(uint8_t *packet, uint16_t *index, const char *string) +{ + uint8_t len = strlen(string); + if ((*index + len + 1) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, len); + memcpy(packet + *index, string, len); + *index += len; + return len + 1; +} + +/** + * @brief appends one TXT record ("key=value" or "key") + * + * @param packet MDNS packet + * @param index offset in the packet + * @param txt one txt record + * + * @return length of added data: length of the added txt value + 1 on success + * 0 if data won't fit the packet + * -1 if invalid TXT entry + */ +static inline int append_one_txt_record_entry(uint8_t *packet, uint16_t *index, mdns_txt_linked_item_t *txt) +{ + if (txt == NULL || txt->key == NULL) { + return -1; + } + size_t key_len = strlen(txt->key); + size_t len = key_len + txt->value_len + (txt->value ? 1 : 0); + if ((*index + len + 1) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, len); + memcpy(packet + *index, txt->key, key_len); + if (txt->value) { + packet[*index + key_len] = '='; + memcpy(packet + *index + key_len + 1, txt->value, txt->value_len); + } + *index += len; + return len + 1; +} + +#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES +static inline int append_single_str(uint8_t *packet, uint16_t *index, const char *str, int len) +{ + if ((*index + len + 1) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + if (!_mdns_append_u8(packet, index, len)) { + return 0; + } + memcpy(packet + *index, str, len); + *index += len; + return *index; +} + +/** + * @brief appends FQDN to a packet from hostname separated by dots. This API works the same way as + * _mdns_append_fqdn(), but refrains from DNS compression (as it's mainly used for IP addresses (many short items), + * where we gain very little (or compression even gets counter-productive mainly for IPv6 addresses) + * + * @param packet MDNS packet + * @param index offset in the packet + * @param name name representing FQDN in '.' separated parts + * @param last true if appending the last part (domain, typically "arpa") + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t append_fqdn_dots(uint8_t *packet, uint16_t *index, const char *name, bool last) +{ + int len = strlen(name); + char *host = (char *)name; + char *end = host; + char *start = host; + do { + end = memchr(start, '.', host + len - start); + end = end ? end : host + len; + int part_len = end - start; + if (!append_single_str(packet, index, start, part_len)) { + return 0; + } + start = ++end; + } while (end < name + len); + + if (!append_single_str(packet, index, "arpa", sizeof("arpa") - 1)) { + return 0; + } + + //empty string so terminate + if (!_mdns_append_u8(packet, index, 0)) { + return 0; + } + return *index; +} +#endif /* CONFIG_MDNS_RESPOND_REVERSE_QUERIES */ + +/** + * @brief appends FQDN to a packet, incrementing the index and + * compressing the output if previous occurrence of the string (or part of it) has been found + * + * @param packet MDNS packet + * @param index offset in the packet + * @param strings string array containing the parts of the FQDN + * @param count number of strings in the array + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_fqdn(uint8_t *packet, uint16_t *index, const char *strings[], uint8_t count, size_t packet_len) +{ + if (!count) { + //empty string so terminate + return _mdns_append_u8(packet, index, 0); + } + mdns_name_t name; + static char buf[MDNS_NAME_BUF_LEN]; + uint8_t len = strlen(strings[0]); + //try to find first the string length in the packet (if it exists) + uint8_t *len_location = (uint8_t *)memchr(packet, (char)len, *index); + while (len_location) { + //check if the string after len_location is the string that we are looking for + if (memcmp(len_location + 1, strings[0], len)) { //not continuing with our string +search_next: + //try and find the length byte further in the packet + len_location = (uint8_t *)memchr(len_location + 1, (char)len, *index - (len_location + 1 - packet)); + continue; + } + //seems that we might have found the string that we are looking for + //read the destination into name and compare + name.parts = 0; + name.sub = 0; + name.invalid = false; + name.host[0] = 0; + name.service[0] = 0; + name.proto[0] = 0; + name.domain[0] = 0; + const uint8_t *content = _mdns_read_fqdn(packet, len_location, &name, buf, packet_len); + if (!content) { + //not a readable fqdn? + goto search_next; // could be our unfinished fqdn, continue searching + } + if (name.parts == count) { + uint8_t i; + for (i = 0; i < count; i++) { + if (strcasecmp(strings[i], (const char *)&name + (i * (MDNS_NAME_BUF_LEN)))) { + //not our string! let's search more + goto search_next; + } + } + //we actually have found the string + break; + } else { + goto search_next; + } + } + //string is not yet in the packet, so let's add it + if (!len_location) { + uint8_t written = _mdns_append_string(packet, index, strings[0]); + if (!written) { + return 0; + } + //run the same for the other strings in the name + return written + _mdns_append_fqdn(packet, index, &strings[1], count - 1, packet_len); + } + + //we have found the string so let's insert a pointer to it instead + uint16_t offset = len_location - packet; + offset |= MDNS_NAME_REF; + return _mdns_append_u16(packet, index, offset); +} + +/** + * @brief appends PTR record for service to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param server the server that is hosting the service + * @param service the service to add record for + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_ptr_record(uint8_t *packet, uint16_t *index, const char *instance, const char *service, const char *proto, bool flush, bool bye) +{ + const char *str[4]; + uint16_t record_length = 0; + uint8_t part_length; + + if (service == NULL) { + return 0; + } + + str[0] = instance; + str[1] = service; + str[2] = proto; + str[3] = MDNS_DEFAULT_DOMAIN; + + part_length = _mdns_append_fqdn(packet, index, str + 1, 3, MDNS_MAX_PACKET_SIZE); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_PTR, false, bye ? 0 : MDNS_ANSWER_PTR_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + part_length = _mdns_append_fqdn(packet, index, str, 4, MDNS_MAX_PACKET_SIZE); + if (!part_length) { + return 0; + } + _mdns_set_u16(packet, data_len_location, part_length); + record_length += part_length; + return record_length; +} + +/** + * @brief appends PTR record for a subtype to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param instance the service instance name + * @param subtype the service subtype + * @param proto the service protocol + * @param flush whether to set the flush flag + * @param bye whether to set the bye flag + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_subtype_ptr_record(uint8_t *packet, uint16_t *index, const char *instance, + const char *subtype, const char *service, const char *proto, bool flush, + bool bye) +{ + const char *subtype_str[5] = {subtype, MDNS_SUB_STR, service, proto, MDNS_DEFAULT_DOMAIN}; + const char *instance_str[4] = {instance, service, proto, MDNS_DEFAULT_DOMAIN}; + uint16_t record_length = 0; + uint8_t part_length; + + if (service == NULL) { + return 0; + } + + part_length = _mdns_append_fqdn(packet, index, subtype_str, ARRAY_SIZE(subtype_str), MDNS_MAX_PACKET_SIZE); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_PTR, false, bye ? 0 : MDNS_ANSWER_PTR_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + part_length = _mdns_append_fqdn(packet, index, instance_str, ARRAY_SIZE(instance_str), MDNS_MAX_PACKET_SIZE); + if (!part_length) { + return 0; + } + _mdns_set_u16(packet, data_len_location, part_length); + record_length += part_length; + return record_length; +} + +/** + * @brief appends DNS-SD PTR record for service to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param server the server that is hosting the service + * @param service the service to add record for + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_sdptr_record(uint8_t *packet, uint16_t *index, mdns_service_t *service, bool flush, bool bye) +{ + const char *str[3]; + const char *sd_str[4]; + uint16_t record_length = 0; + uint8_t part_length; + + if (service == NULL) { + return 0; + } + + sd_str[0] = (char *)"_services"; + sd_str[1] = (char *)"_dns-sd"; + sd_str[2] = (char *)"_udp"; + sd_str[3] = MDNS_DEFAULT_DOMAIN; + + str[0] = service->service; + str[1] = service->proto; + str[2] = MDNS_DEFAULT_DOMAIN; + + part_length = _mdns_append_fqdn(packet, index, sd_str, 4, MDNS_MAX_PACKET_SIZE); + + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_PTR, flush, MDNS_ANSWER_PTR_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + part_length = _mdns_append_fqdn(packet, index, str, 3, MDNS_MAX_PACKET_SIZE); + if (!part_length) { + return 0; + } + _mdns_set_u16(packet, data_len_location, part_length); + record_length += part_length; + return record_length; +} + +/** + * @brief appends TXT record for service to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param server the server that is hosting the service + * @param service the service to add record for + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_txt_record(uint8_t *packet, uint16_t *index, mdns_service_t *service, bool flush, bool bye) +{ + const char *str[4]; + uint16_t record_length = 0; + uint8_t part_length; + + if (service == NULL) { + return 0; + } + + str[0] = _mdns_get_service_instance_name(service); + str[1] = service->service; + str[2] = service->proto; + str[3] = MDNS_DEFAULT_DOMAIN; + + if (!str[0]) { + return 0; + } + + part_length = _mdns_append_fqdn(packet, index, str, 4, MDNS_MAX_PACKET_SIZE); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_TXT, flush, bye ? 0 : MDNS_ANSWER_TXT_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + uint16_t data_len = 0; + + mdns_txt_linked_item_t *txt = service->txt; + while (txt) { + int l = append_one_txt_record_entry(packet, index, txt); + if (l > 0) { + data_len += l; + } else if (l == 0) { // TXT entry won't fit into the mdns packet + return 0; + } + txt = txt->next; + } + if (!data_len) { + data_len = 1; + packet[*index] = 0; + *index = *index + 1; + } + _mdns_set_u16(packet, data_len_location, data_len); + record_length += data_len; + return record_length; +} + +/** + * @brief appends SRV record for service to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param server the server that is hosting the service + * @param service the service to add record for + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_srv_record(uint8_t *packet, uint16_t *index, mdns_service_t *service, bool flush, bool bye) +{ + const char *str[4]; + uint16_t record_length = 0; + uint8_t part_length; + + if (service == NULL) { + return 0; + } + + str[0] = _mdns_get_service_instance_name(service); + str[1] = service->service; + str[2] = service->proto; + str[3] = MDNS_DEFAULT_DOMAIN; + + if (!str[0]) { + return 0; + } + + part_length = _mdns_append_fqdn(packet, index, str, 4, MDNS_MAX_PACKET_SIZE); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_SRV, flush, bye ? 0 : MDNS_ANSWER_SRV_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + + part_length = 0; + part_length += _mdns_append_u16(packet, index, service->priority); + part_length += _mdns_append_u16(packet, index, service->weight); + part_length += _mdns_append_u16(packet, index, service->port); + if (part_length != 6) { + return 0; + } + + if (service->hostname) { + str[0] = service->hostname; + } else { + str[0] = _mdns_server->hostname; + } + str[1] = MDNS_DEFAULT_DOMAIN; + + if (_str_null_or_empty(str[0])) { + return 0; + } + + part_length = _mdns_append_fqdn(packet, index, str, 2, MDNS_MAX_PACKET_SIZE); + if (!part_length) { + return 0; + } + _mdns_set_u16(packet, data_len_location, part_length + 6); + + record_length += part_length + 6; + return record_length; +} + +#ifdef CONFIG_LWIP_IPV4 +/** + * @brief appends A record to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param hostname the hostname address to add + * @param ip the IP address to add + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_a_record(uint8_t *packet, uint16_t *index, const char *hostname, uint32_t ip, bool flush, bool bye) +{ + const char *str[2]; + uint16_t record_length = 0; + uint8_t part_length; + + str[0] = hostname; + str[1] = MDNS_DEFAULT_DOMAIN; + + if (_str_null_or_empty(str[0])) { + return 0; + } + + part_length = _mdns_append_fqdn(packet, index, str, 2, MDNS_MAX_PACKET_SIZE); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_A, flush, bye ? 0 : MDNS_ANSWER_A_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + + if ((*index + 3) >= MDNS_MAX_PACKET_SIZE) { + return 0; + } + _mdns_append_u8(packet, index, ip & 0xFF); + _mdns_append_u8(packet, index, (ip >> 8) & 0xFF); + _mdns_append_u8(packet, index, (ip >> 16) & 0xFF); + _mdns_append_u8(packet, index, (ip >> 24) & 0xFF); + _mdns_set_u16(packet, data_len_location, 4); + + record_length += 4; + return record_length; +} +#endif /* CONFIG_LWIP_IPV4 */ + +#ifdef CONFIG_LWIP_IPV6 +/** + * @brief appends AAAA record to a packet, incrementing the index + * + * @param packet MDNS packet + * @param index offset in the packet + * @param hostname the hostname address to add + * @param ipv6 the IPv6 address to add + * + * @return length of added data: 0 on error or length on success + */ +static uint16_t _mdns_append_aaaa_record(uint8_t *packet, uint16_t *index, const char *hostname, uint8_t *ipv6, bool flush, bool bye) +{ + const char *str[2]; + uint16_t record_length = 0; + uint8_t part_length; + + str[0] = hostname; + str[1] = MDNS_DEFAULT_DOMAIN; + + if (_str_null_or_empty(str[0])) { + return 0; + } + + + part_length = _mdns_append_fqdn(packet, index, str, 2, MDNS_MAX_PACKET_SIZE); + if (!part_length) { + return 0; + } + record_length += part_length; + + part_length = _mdns_append_type(packet, index, MDNS_ANSWER_AAAA, flush, bye ? 0 : MDNS_ANSWER_AAAA_TTL); + if (!part_length) { + return 0; + } + record_length += part_length; + + uint16_t data_len_location = *index - 2; + + if ((*index + MDNS_ANSWER_AAAA_SIZE) > MDNS_MAX_PACKET_SIZE) { + return 0; + } + + part_length = MDNS_ANSWER_AAAA_SIZE; + memcpy(packet + *index, ipv6, part_length); + *index += part_length; + _mdns_set_u16(packet, data_len_location, part_length); + record_length += part_length; + return record_length; +} +#endif + +/** + * @brief Append question to packet + */ +static uint16_t _mdns_append_question(uint8_t *packet, uint16_t *index, mdns_out_question_t *q) +{ + uint8_t part_length; +#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES + if (q->host && (strstr(q->host, "in-addr") || strstr(q->host, "ip6"))) { + part_length = append_fqdn_dots(packet, index, q->host, false); + if (!part_length) { + return 0; + } + } else +#endif /* CONFIG_MDNS_RESPOND_REVERSE_QUERIES */ + { + const char *str[4]; + uint8_t str_index = 0; + if (q->host) { + str[str_index++] = q->host; + } + if (q->service) { + str[str_index++] = q->service; + } + if (q->proto) { + str[str_index++] = q->proto; + } + if (q->domain) { + str[str_index++] = q->domain; + } + part_length = _mdns_append_fqdn(packet, index, str, str_index, MDNS_MAX_PACKET_SIZE); + if (!part_length) { + return 0; + } + } + + part_length += _mdns_append_u16(packet, index, q->type); + part_length += _mdns_append_u16(packet, index, q->unicast ? 0x8001 : 0x0001); + return part_length; +} + +/** + * @brief Helper to get either ETH or STA if the other is provided + * Used when two interfaces are on the same subnet + */ +static mdns_if_t _mdns_get_other_if (mdns_if_t tcpip_if) +{ + if (tcpip_if < MDNS_MAX_INTERFACES) { + return s_esp_netifs[tcpip_if].duplicate; + } + return MDNS_MAX_INTERFACES; +} + +/** + * @brief Check if interface is duplicate (two interfaces on the same subnet) + */ +static bool _mdns_if_is_dup(mdns_if_t tcpip_if) +{ + mdns_if_t other_if = _mdns_get_other_if (tcpip_if); + if (other_if == MDNS_MAX_INTERFACES) { + return false; + } + if (_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].state == PCB_DUP + || _mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].state == PCB_DUP + || _mdns_server->interfaces[other_if].pcbs[MDNS_IP_PROTOCOL_V4].state == PCB_DUP + || _mdns_server->interfaces[other_if].pcbs[MDNS_IP_PROTOCOL_V6].state == PCB_DUP + ) { + return true; + } + return false; +} + +#ifdef CONFIG_LWIP_IPV6 +/** + * @brief Check if IPv6 address is NULL + */ +static bool _ipv6_address_is_zero(esp_ip6_addr_t ip6) +{ + uint8_t i; + uint8_t *data = (uint8_t *)ip6.addr; + for (i = 0; i < _MDNS_SIZEOF_IP6_ADDR; i++) { + if (data[i]) { + return false; + } + } + return true; +} +#endif /* CONFIG_LWIP_IPV6 */ + +static uint8_t _mdns_append_host_answer(uint8_t *packet, uint16_t *index, mdns_host_item_t *host, + uint8_t address_type, bool flush, bool bye) +{ + mdns_ip_addr_t *addr = host->address_list; + uint8_t num_records = 0; + + while (addr != NULL) { + if (addr->addr.type == address_type) { +#ifdef CONFIG_LWIP_IPV4 + if (address_type == ESP_IPADDR_TYPE_V4 && + _mdns_append_a_record(packet, index, host->hostname, addr->addr.u_addr.ip4.addr, flush, bye) <= 0) { + break; + } +#endif /* CONFIG_LWIP_IPV4 */ +#ifdef CONFIG_LWIP_IPV6 + if (address_type == ESP_IPADDR_TYPE_V6 && + _mdns_append_aaaa_record(packet, index, host->hostname, (uint8_t *)addr->addr.u_addr.ip6.addr, flush, + bye) <= 0) { + break; + } +#endif /* CONFIG_LWIP_IPV6 */ + num_records++; + } + addr = addr->next; + } + return num_records; +} + +#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES +/** + * @brief Appends reverse lookup PTR record + */ +static uint8_t _mdns_append_reverse_ptr_record(uint8_t *packet, uint16_t *index, const char *name) +{ + if (strstr(name, "in-addr") == NULL && strstr(name, "ip6") == NULL) { + return 0; + } + + if (!append_fqdn_dots(packet, index, name, false)) { + return 0; + } + + if (!_mdns_append_type(packet, index, MDNS_ANSWER_PTR, false, 10 /* TTL set to 10s*/ )) { + return 0; + } + + uint16_t data_len_location = *index - 2; /* store the position of size (2=16bis) of this record */ + const char *str[2] = { _mdns_self_host.hostname, MDNS_DEFAULT_DOMAIN }; + + int part_length = _mdns_append_fqdn(packet, index, str, 2, MDNS_MAX_PACKET_SIZE); + if (!part_length) { + return 0; + } + + _mdns_set_u16(packet, data_len_location, part_length); + return 1; /* appending only 1 record */ +} +#endif /* CONFIG_MDNS_RESPOND_REVERSE_QUERIES */ + +/** + * @brief Append PTR answers to packet + * + * @return number of answers added to the packet + */ +static uint8_t _mdns_append_service_ptr_answers(uint8_t *packet, uint16_t *index, mdns_service_t *service, bool flush, + bool bye) +{ + uint8_t appended_answers = 0; + + if (_mdns_append_ptr_record(packet, index, _mdns_get_service_instance_name(service), service->service, + service->proto, flush, bye) <= 0) { + return appended_answers; + } + appended_answers++; + + mdns_subtype_t *subtype = service->subtype; + while (subtype) { + appended_answers += + (_mdns_append_subtype_ptr_record(packet, index, _mdns_get_service_instance_name(service), subtype->subtype, + service->service, service->proto, flush, bye) > 0); + subtype = subtype->next; + } + + return appended_answers; +} + + +/** + * @brief Append answer to packet + * + * @return number of answers added to the packet + */ +static uint8_t _mdns_append_answer(uint8_t *packet, uint16_t *index, mdns_out_answer_t *answer, mdns_if_t tcpip_if) +{ + if (answer->host) { + bool is_host_valid = (&_mdns_self_host == answer->host); + mdns_host_item_t *target_host = _mdns_host_list; + while (target_host && !is_host_valid) { + if (target_host == answer->host) { + is_host_valid = true; + } + target_host = target_host->next; + } + if (!is_host_valid) { + return 0; + } + } + + if (answer->type == MDNS_TYPE_PTR) { + if (answer->service) { + return _mdns_append_service_ptr_answers(packet, index, answer->service, answer->flush, answer->bye); +#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES + } else if (answer->host && answer->host->hostname && + (strstr(answer->host->hostname, "in-addr") || strstr(answer->host->hostname, "ip6"))) { + return _mdns_append_reverse_ptr_record(packet, index, answer->host->hostname) > 0; +#endif /* CONFIG_MDNS_RESPOND_REVERSE_QUERIES */ + } else { + return _mdns_append_ptr_record(packet, index, + answer->custom_instance, answer->custom_service, answer->custom_proto, + answer->flush, answer->bye) > 0; + } + } else if (answer->type == MDNS_TYPE_SRV) { + return _mdns_append_srv_record(packet, index, answer->service, answer->flush, answer->bye) > 0; + } else if (answer->type == MDNS_TYPE_TXT) { + return _mdns_append_txt_record(packet, index, answer->service, answer->flush, answer->bye) > 0; + } else if (answer->type == MDNS_TYPE_SDPTR) { + return _mdns_append_sdptr_record(packet, index, answer->service, answer->flush, answer->bye) > 0; + } +#ifdef CONFIG_LWIP_IPV4 + else if (answer->type == MDNS_TYPE_A) { + if (answer->host == &_mdns_self_host) { + esp_netif_ip_info_t if_ip_info; + if (!mdns_is_netif_ready(tcpip_if, MDNS_IP_PROTOCOL_V4) && _mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].state != PCB_DUP) { + return 0; + } + if (esp_netif_get_ip_info(_mdns_get_esp_netif(tcpip_if), &if_ip_info)) { + return 0; + } + if (_mdns_append_a_record(packet, index, _mdns_server->hostname, if_ip_info.ip.addr, answer->flush, answer->bye) <= 0) { + return 0; + } + if (!_mdns_if_is_dup(tcpip_if)) { + return 1; + } + mdns_if_t other_if = _mdns_get_other_if (tcpip_if); + if (esp_netif_get_ip_info(_mdns_get_esp_netif(other_if), &if_ip_info)) { + return 1; + } + if (_mdns_append_a_record(packet, index, _mdns_server->hostname, if_ip_info.ip.addr, answer->flush, answer->bye) > 0) { + return 2; + } + return 1; + } else if (answer->host != NULL) { + return _mdns_append_host_answer(packet, index, answer->host, ESP_IPADDR_TYPE_V4, answer->flush, answer->bye); + } + } +#endif /* CONFIG_LWIP_IPV4 */ +#ifdef CONFIG_LWIP_IPV6 + else if (answer->type == MDNS_TYPE_AAAA) { + if (answer->host == &_mdns_self_host) { + struct esp_ip6_addr if_ip6s[NETIF_IPV6_MAX_NUMS]; + uint8_t count = 0; + if (!mdns_is_netif_ready(tcpip_if, MDNS_IP_PROTOCOL_V6) && _mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].state != PCB_DUP) { + return 0; + } + count = esp_netif_get_all_ip6(_mdns_get_esp_netif(tcpip_if), if_ip6s); + assert(count <= NETIF_IPV6_MAX_NUMS); + for (int i = 0; i < count; i++) { + if (_ipv6_address_is_zero(if_ip6s[i])) { + return 0; + } + if (_mdns_append_aaaa_record(packet, index, _mdns_server->hostname, (uint8_t *)if_ip6s[i].addr, + answer->flush, answer->bye) <= 0) { + return 0; + } + } + if (!_mdns_if_is_dup(tcpip_if)) { + return count; + } + + mdns_if_t other_if = _mdns_get_other_if(tcpip_if); + struct esp_ip6_addr other_ip6; + if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(other_if), &other_ip6)) { + return count; + } + if (_mdns_append_aaaa_record(packet, index, _mdns_server->hostname, (uint8_t *)other_ip6.addr, + answer->flush, answer->bye) > 0) { + return 1 + count; + } + return count; + } else if (answer->host != NULL) { + return _mdns_append_host_answer(packet, index, answer->host, ESP_IPADDR_TYPE_V6, answer->flush, + answer->bye); + } + } +#endif /* CONFIG_LWIP_IPV6 */ + return 0; +} + +/** + * @brief sends a packet + * + * @param p the packet + */ +static void _mdns_dispatch_tx_packet(mdns_tx_packet_t *p) +{ + static uint8_t packet[MDNS_MAX_PACKET_SIZE]; + uint16_t index = MDNS_HEAD_LEN; + memset(packet, 0, MDNS_HEAD_LEN); + mdns_out_question_t *q; + mdns_out_answer_t *a; + uint8_t count; + + _mdns_set_u16(packet, MDNS_HEAD_FLAGS_OFFSET, p->flags); + _mdns_set_u16(packet, MDNS_HEAD_ID_OFFSET, p->id); + + count = 0; + q = p->questions; + while (q) { + if (_mdns_append_question(packet, &index, q)) { + count++; + } + q = q->next; + } + _mdns_set_u16(packet, MDNS_HEAD_QUESTIONS_OFFSET, count); + + count = 0; + a = p->answers; + while (a) { + count += _mdns_append_answer(packet, &index, a, p->tcpip_if); + a = a->next; + } + _mdns_set_u16(packet, MDNS_HEAD_ANSWERS_OFFSET, count); + + count = 0; + a = p->servers; + while (a) { + count += _mdns_append_answer(packet, &index, a, p->tcpip_if); + a = a->next; + } + _mdns_set_u16(packet, MDNS_HEAD_SERVERS_OFFSET, count); + + count = 0; + a = p->additional; + while (a) { + count += _mdns_append_answer(packet, &index, a, p->tcpip_if); + a = a->next; + } + _mdns_set_u16(packet, MDNS_HEAD_ADDITIONAL_OFFSET, count); + +#ifdef MDNS_ENABLE_DEBUG + _mdns_dbg_printf("\nTX[%lu][%lu]: ", (unsigned long)p->tcpip_if, (unsigned long)p->ip_protocol); +#ifdef CONFIG_LWIP_IPV4 + if (p->dst.type == ESP_IPADDR_TYPE_V4) { + _mdns_dbg_printf("To: " IPSTR ":%u, ", IP2STR(&p->dst.u_addr.ip4), p->port); + } +#endif +#ifdef CONFIG_LWIP_IPV6 + if (p->dst.type == ESP_IPADDR_TYPE_V6) { + _mdns_dbg_printf("To: " IPV6STR ":%u, ", IPV62STR(p->dst.u_addr.ip6), p->port); + } +#endif + mdns_debug_packet(packet, index); +#endif + + _mdns_udp_pcb_write(p->tcpip_if, p->ip_protocol, &p->dst, p->port, packet, index); +} + +/** + * @brief frees a packet + * + * @param packet the packet + */ +static void _mdns_free_tx_packet(mdns_tx_packet_t *packet) +{ + if (!packet) { + return; + } + mdns_out_question_t *q = packet->questions; + while (q) { + mdns_out_question_t *next = q->next; + if (q->own_dynamic_memory) { + free((char *)q->host); + free((char *)q->service); + free((char *)q->proto); + free((char *)q->domain); + } + free(q); + q = next; + } + queueFree(mdns_out_answer_t, packet->answers); + queueFree(mdns_out_answer_t, packet->servers); + queueFree(mdns_out_answer_t, packet->additional); + free(packet); +} + +/** + * @brief schedules a packet to be sent after given milliseconds + * + * @param packet the packet + * @param ms_after number of milliseconds after which the packet should be dispatched + */ +static void _mdns_schedule_tx_packet(mdns_tx_packet_t *packet, uint32_t ms_after) +{ + if (!packet) { + return; + } + packet->send_at = (xTaskGetTickCount() * portTICK_PERIOD_MS) + ms_after; + packet->next = NULL; + if (!_mdns_server->tx_queue_head || _mdns_server->tx_queue_head->send_at > packet->send_at) { + packet->next = _mdns_server->tx_queue_head; + _mdns_server->tx_queue_head = packet; + return; + } + mdns_tx_packet_t *q = _mdns_server->tx_queue_head; + while (q->next && q->next->send_at <= packet->send_at) { + q = q->next; + } + packet->next = q->next; + q->next = packet; +} + +/** + * @brief free all packets scheduled for sending + */ +static void _mdns_clear_tx_queue_head(void) +{ + mdns_tx_packet_t *q; + while (_mdns_server->tx_queue_head) { + q = _mdns_server->tx_queue_head; + _mdns_server->tx_queue_head = _mdns_server->tx_queue_head->next; + _mdns_free_tx_packet(q); + } +} + +/** + * @brief clear packets scheduled for sending on a specific interface + * + * @param tcpip_if the interface + * @param ip_protocol pcb type V4/V6 + */ +static void _mdns_clear_pcb_tx_queue_head(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + mdns_tx_packet_t *q, * p; + while (_mdns_server->tx_queue_head && _mdns_server->tx_queue_head->tcpip_if == tcpip_if && _mdns_server->tx_queue_head->ip_protocol == ip_protocol) { + q = _mdns_server->tx_queue_head; + _mdns_server->tx_queue_head = _mdns_server->tx_queue_head->next; + _mdns_free_tx_packet(q); + } + if (_mdns_server->tx_queue_head) { + q = _mdns_server->tx_queue_head; + while (q->next) { + if (q->next->tcpip_if == tcpip_if && q->next->ip_protocol == ip_protocol) { + p = q->next; + q->next = p->next; + _mdns_free_tx_packet(p); + } else { + q = q->next; + } + } + } +} + +/** + * @brief get the next packet scheduled for sending on a specific interface + * + * @param tcpip_if the interface + * @param ip_protocol pcb type V4/V6 + */ +static mdns_tx_packet_t *_mdns_get_next_pcb_packet(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + mdns_tx_packet_t *q = _mdns_server->tx_queue_head; + while (q) { + if (q->tcpip_if == tcpip_if && q->ip_protocol == ip_protocol) { + return q; + } + q = q->next; + } + return NULL; +} + +/** + * @brief Find, remove and free answer from the scheduled packets + */ +static void _mdns_remove_scheduled_answer(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint16_t type, mdns_srv_item_t *service) +{ + mdns_srv_item_t s = {NULL, NULL}; + if (!service) { + service = &s; + } + mdns_tx_packet_t *q = _mdns_server->tx_queue_head; + while (q) { + if (q->tcpip_if == tcpip_if && q->ip_protocol == ip_protocol && q->distributed) { + mdns_out_answer_t *a = q->answers; + if (a) { + if (a->type == type && a->service == service->service) { + q->answers = q->answers->next; + free(a); + } else { + while (a->next) { + if (a->next->type == type && a->next->service == service->service) { + mdns_out_answer_t *b = a->next; + a->next = b->next; + free(b); + break; + } + a = a->next; + } + } + } + } + q = q->next; + } +} + +/** + * @brief Remove and free answer from answer list (destination) + */ +static void _mdns_dealloc_answer(mdns_out_answer_t **destination, uint16_t type, mdns_srv_item_t *service) +{ + mdns_out_answer_t *d = *destination; + if (!d) { + return; + } + mdns_srv_item_t s = {NULL, NULL}; + if (!service) { + service = &s; + } + if (d->type == type && d->service == service->service) { + *destination = d->next; + free(d); + return; + } + while (d->next) { + mdns_out_answer_t *a = d->next; + if (a->type == type && a->service == service->service) { + d->next = a->next; + free(a); + return; + } + d = d->next; + } +} + +/** + * @brief Allocate new answer and add it to answer list (destination) + */ +static bool _mdns_alloc_answer(mdns_out_answer_t **destination, uint16_t type, mdns_service_t *service, + mdns_host_item_t *host, bool flush, bool bye) +{ + mdns_out_answer_t *d = *destination; + while (d) { + if (d->type == type && d->service == service && d->host == host) { + return true; + } + d = d->next; + } + + mdns_out_answer_t *a = (mdns_out_answer_t *)malloc(sizeof(mdns_out_answer_t)); + if (!a) { + HOOK_MALLOC_FAILED; + return false; + } + a->type = type; + a->service = service; + a->host = host; + a->custom_service = NULL; + a->bye = bye; + a->flush = flush; + a->next = NULL; + queueToEnd(mdns_out_answer_t, *destination, a); + return true; +} + +/** + * @brief Allocate new packet for sending + */ +static mdns_tx_packet_t *_mdns_alloc_packet_default(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + mdns_tx_packet_t *packet = (mdns_tx_packet_t *)malloc(sizeof(mdns_tx_packet_t)); + if (!packet) { + HOOK_MALLOC_FAILED; + return NULL; + } + memset((uint8_t *)packet, 0, sizeof(mdns_tx_packet_t)); + packet->tcpip_if = tcpip_if; + packet->ip_protocol = ip_protocol; + packet->port = MDNS_SERVICE_PORT; +#ifdef CONFIG_LWIP_IPV4 + if (ip_protocol == MDNS_IP_PROTOCOL_V4) { + esp_ip_addr_t addr = ESP_IP4ADDR_INIT(224, 0, 0, 251); + memcpy(&packet->dst, &addr, sizeof(esp_ip_addr_t)); + } +#endif +#ifdef CONFIG_LWIP_IPV6 + if (ip_protocol == MDNS_IP_PROTOCOL_V6) { + esp_ip_addr_t addr = ESP_IP6ADDR_INIT(0x000002ff, 0, 0, 0xfb000000); + memcpy(&packet->dst, &addr, sizeof(esp_ip_addr_t)); + } +#endif + return packet; +} + +static bool _mdns_create_answer_from_service(mdns_tx_packet_t *packet, mdns_service_t *service, + mdns_parsed_question_t *question, bool shared, bool send_flush) +{ + mdns_host_item_t *host = mdns_get_host_item(service->hostname); + bool is_delegated = (host != &_mdns_self_host); + if (question->type == MDNS_TYPE_PTR || question->type == MDNS_TYPE_ANY) { + // According to RFC6763-section12.1, for DNS-SD, SRV, TXT and all address records + // should be included in additional records. + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, service, NULL, false, false) || + !_mdns_alloc_answer(is_delegated ? &packet->additional : &packet->answers, MDNS_TYPE_SRV, service, NULL, send_flush, false) || + !_mdns_alloc_answer(is_delegated ? &packet->additional : &packet->answers, MDNS_TYPE_TXT, service, NULL, send_flush, false) || + !_mdns_alloc_answer((shared || is_delegated) ? &packet->additional : &packet->answers, MDNS_TYPE_A, service, host, send_flush, + false) || + !_mdns_alloc_answer((shared || is_delegated) ? &packet->additional : &packet->answers, MDNS_TYPE_AAAA, service, host, + send_flush, false)) { + return false; + } + } else if (question->type == MDNS_TYPE_SRV) { + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, service, NULL, send_flush, false) || + !_mdns_alloc_answer(&packet->additional, MDNS_TYPE_A, service, host, send_flush, false) || + !_mdns_alloc_answer(&packet->additional, MDNS_TYPE_AAAA, service, host, send_flush, false)) { + return false; + } + } else if (question->type == MDNS_TYPE_TXT) { + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, service, NULL, send_flush, false)) { + return false; + } + } else if (question->type == MDNS_TYPE_SDPTR) { + shared = true; + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SDPTR, service, NULL, false, false)) { + return false; + } + } + return true; +} + +static bool _mdns_create_answer_from_hostname(mdns_tx_packet_t *packet, const char *hostname, bool send_flush) +{ + mdns_host_item_t *host = mdns_get_host_item(hostname); + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_A, NULL, host, send_flush, false) || + !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_AAAA, NULL, host, send_flush, false)) { + return false; + } + return true; +} + +static bool _mdns_service_match_ptr_question(const mdns_service_t *service, const mdns_parsed_question_t *question) +{ + if (!_mdns_service_match(service, question->service, question->proto, NULL)) { + return false; + } + // The question parser stores anything before _type._proto in question->host + // So the question->host can be subtype or instance name based on its content + if (question->sub) { + mdns_subtype_t *subtype = service->subtype; + while (subtype) { + if (!strcasecmp(subtype->subtype, question->host)) { + return true; + } + subtype = subtype->next; + } + return false; + } + if (question->host) { + if (strcasecmp(_mdns_get_service_instance_name(service), question->host) != 0) { + return false; + } + } + return true; +} + +/** + * @brief Create answer packet to questions from parsed packet + */ +static void _mdns_create_answer_from_parsed_packet(mdns_parsed_packet_t *parsed_packet) +{ + if (!parsed_packet->questions) { + return; + } + bool send_flush = parsed_packet->src_port == MDNS_SERVICE_PORT; + bool unicast = false; + bool shared = false; + mdns_tx_packet_t *packet = _mdns_alloc_packet_default(parsed_packet->tcpip_if, parsed_packet->ip_protocol); + if (!packet) { + return; + } + packet->flags = MDNS_FLAGS_QR_AUTHORITATIVE; + packet->distributed = parsed_packet->distributed; + packet->id = parsed_packet->id; + + mdns_parsed_question_t *q = parsed_packet->questions; + uint32_t out_record_nums = 0; + while (q) { + shared = q->type == MDNS_TYPE_PTR || q->type == MDNS_TYPE_SDPTR || !parsed_packet->probe; + if (q->type == MDNS_TYPE_SRV || q->type == MDNS_TYPE_TXT) { + mdns_srv_item_t *service = _mdns_get_service_item_instance(q->host, q->service, q->proto, NULL); + if (service == NULL || !_mdns_create_answer_from_service(packet, service->service, q, shared, send_flush)) { + _mdns_free_tx_packet(packet); + return; + } else { + out_record_nums++; + } + } else if (q->service && q->proto) { + mdns_srv_item_t *service = _mdns_server->services; + while (service) { + if (_mdns_service_match_ptr_question(service->service, q)) { + mdns_parsed_record_t *r = parsed_packet->records; + bool is_record_exist = false; + while (r) { + if (service->service->instance && r->host) { + if (_mdns_service_match_instance(service->service, r->host, r->service, r->proto, NULL) && r->ttl > (MDNS_ANSWER_PTR_TTL / 2)) { + is_record_exist = true; + break; + } + } else if (!service->service->instance && !r->host) { + if (_mdns_service_match(service->service, r->service, r->proto, NULL) && r->ttl > (MDNS_ANSWER_PTR_TTL / 2)) { + is_record_exist = true; + break; + } + } + r = r->next; + } + if (!is_record_exist) { + if (!_mdns_create_answer_from_service(packet, service->service, q, shared, send_flush)) { + _mdns_free_tx_packet(packet); + return; + } else { + out_record_nums++; + } + } + } + service = service->next; + } + } else if (q->type == MDNS_TYPE_A || q->type == MDNS_TYPE_AAAA) { + if (!_mdns_create_answer_from_hostname(packet, q->host, send_flush)) { + _mdns_free_tx_packet(packet); + return; + } else { + out_record_nums++; + } + } else if (q->type == MDNS_TYPE_ANY) { + if (!_mdns_append_host_list(&packet->answers, send_flush, false)) { + _mdns_free_tx_packet(packet); + return; + } else { + out_record_nums++; + } +#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES + } else if (q->type == MDNS_TYPE_PTR) { + mdns_host_item_t *host = mdns_get_host_item(q->host); + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, NULL, host, send_flush, false)) { + _mdns_free_tx_packet(packet); + return; + } else { + out_record_nums++; + } +#endif /* CONFIG_MDNS_RESPOND_REVERSE_QUERIES */ + } else if (!_mdns_alloc_answer(&packet->answers, q->type, NULL, NULL, send_flush, false)) { + _mdns_free_tx_packet(packet); + return; + } else { + out_record_nums++; + } + + if (parsed_packet->src_port != MDNS_SERVICE_PORT && // Repeat the queries only for "One-Shot mDNS queries" + (q->type == MDNS_TYPE_ANY || q->type == MDNS_TYPE_A || q->type == MDNS_TYPE_AAAA +#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES + || q->type == MDNS_TYPE_PTR +#endif /* CONFIG_MDNS_RESPOND_REVERSE_QUERIES */ + )) { + mdns_out_question_t *out_question = malloc(sizeof(mdns_out_question_t)); + if (out_question == NULL) { + HOOK_MALLOC_FAILED; + _mdns_free_tx_packet(packet); + return; + } + out_question->type = q->type; + out_question->unicast = q->unicast; + out_question->host = q->host; + q->host = NULL; + out_question->service = q->service; + q->service = NULL; + out_question->proto = q->proto; + q->proto = NULL; + out_question->domain = q->domain; + q->domain = NULL; + out_question->next = NULL; + out_question->own_dynamic_memory = true; + queueToEnd(mdns_out_question_t, packet->questions, out_question); + } + if (q->unicast) { + unicast = true; + } + q = q->next; + } + if (out_record_nums == 0) { + _mdns_free_tx_packet(packet); + return; + } + if (unicast || !send_flush) { + memcpy(&packet->dst, &parsed_packet->src, sizeof(esp_ip_addr_t)); + packet->port = parsed_packet->src_port; + } + + static uint8_t share_step = 0; + if (shared) { + _mdns_schedule_tx_packet(packet, 25 + (share_step * 25)); + share_step = (share_step + 1) & 0x03; + } else { + _mdns_dispatch_tx_packet(packet); + _mdns_free_tx_packet(packet); + } +} + +/** + * @brief Check if question is already in the list + */ +static bool _mdns_question_exists(mdns_out_question_t *needle, mdns_out_question_t *haystack) +{ + while (haystack) { + if (haystack->type == needle->type + && haystack->host == needle->host + && haystack->service == needle->service + && haystack->proto == needle->proto) { + return true; + } + haystack = haystack->next; + } + return false; +} + +static bool _mdns_append_host(mdns_out_answer_t **destination, mdns_host_item_t *host, bool flush, bool bye) +{ + if (!_mdns_alloc_answer(destination, MDNS_TYPE_A, NULL, host, flush, bye)) { + return false; + } + if (!_mdns_alloc_answer(destination, MDNS_TYPE_AAAA, NULL, host, flush, bye)) { + return false; + } + return true; +} + +static bool _mdns_append_host_list_in_services(mdns_out_answer_t **destination, mdns_srv_item_t *services[], + size_t services_len, bool flush, bool bye) +{ + if (services == NULL) { + mdns_host_item_t *host = mdns_get_host_item(_mdns_server->hostname); + if (host != NULL) { + return _mdns_append_host(destination, host, flush, bye); + } + return true; + } + for (size_t i = 0; i < services_len; i++) { + mdns_host_item_t *host = mdns_get_host_item(services[i]->service->hostname); + if (!_mdns_append_host(destination, host, flush, bye)) { + return false; + } + } + return true; +} + +static bool _mdns_append_host_list(mdns_out_answer_t **destination, bool flush, bool bye) +{ + if (!_str_null_or_empty(_mdns_server->hostname)) { + mdns_host_item_t *self_host = mdns_get_host_item(_mdns_server->hostname); + if (!_mdns_append_host(destination, self_host, flush, bye)) { + return false; + } + } + mdns_host_item_t *host = _mdns_host_list; + while (host != NULL) { + host = host->next; + if (!_mdns_append_host(destination, host, flush, bye)) { + return false; + } + } + return true; +} + +static bool _mdns_append_host_question(mdns_out_question_t **questions, const char *hostname, bool unicast) +{ + mdns_out_question_t *q = (mdns_out_question_t *)malloc(sizeof(mdns_out_question_t)); + if (!q) { + HOOK_MALLOC_FAILED; + return false; + } + q->next = NULL; + q->unicast = unicast; + q->type = MDNS_TYPE_ANY; + q->host = hostname; + q->service = NULL; + q->proto = NULL; + q->domain = MDNS_DEFAULT_DOMAIN; + q->own_dynamic_memory = false; + if (_mdns_question_exists(q, *questions)) { + free(q); + } else { + queueToEnd(mdns_out_question_t, *questions, q); + } + return true; +} + +static bool _mdns_append_host_questions_for_services(mdns_out_question_t **questions, mdns_srv_item_t *services[], + size_t len, bool unicast) +{ + if (!_str_null_or_empty(_mdns_server->hostname) && + !_mdns_append_host_question(questions, _mdns_server->hostname, unicast)) { + return false; + } + for (size_t i = 0; i < len; i++) { + if (!_mdns_append_host_question(questions, services[i]->service->hostname, unicast)) { + return false; + } + } + return true; +} + +/** + * @brief Create probe packet for particular services on particular PCB + */ +static mdns_tx_packet_t *_mdns_create_probe_packet(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t *services[], size_t len, bool first, bool include_ip) +{ + mdns_tx_packet_t *packet = _mdns_alloc_packet_default(tcpip_if, ip_protocol); + if (!packet) { + return NULL; + } + + size_t i; + for (i = 0; i < len; i++) { + mdns_out_question_t *q = (mdns_out_question_t *)malloc(sizeof(mdns_out_question_t)); + if (!q) { + HOOK_MALLOC_FAILED; + _mdns_free_tx_packet(packet); + return NULL; + } + q->next = NULL; + q->unicast = first; + q->type = MDNS_TYPE_ANY; + q->host = _mdns_get_service_instance_name(services[i]->service); + q->service = services[i]->service->service; + q->proto = services[i]->service->proto; + q->domain = MDNS_DEFAULT_DOMAIN; + q->own_dynamic_memory = false; + if (!q->host || _mdns_question_exists(q, packet->questions)) { + free(q); + continue; + } else { + queueToEnd(mdns_out_question_t, packet->questions, q); + } + + if (!q->host || !_mdns_alloc_answer(&packet->servers, MDNS_TYPE_SRV, services[i]->service, NULL, false, false)) { + _mdns_free_tx_packet(packet); + return NULL; + } + } + + if (include_ip) { + if (!_mdns_append_host_questions_for_services(&packet->questions, services, len, first)) { + _mdns_free_tx_packet(packet); + return NULL; + } + + if (!_mdns_append_host_list_in_services(&packet->servers, services, len, false, false)) { + _mdns_free_tx_packet(packet); + return NULL; + } + } + + return packet; +} + +/** + * @brief Create announce packet for particular services on particular PCB + */ +static mdns_tx_packet_t *_mdns_create_announce_packet(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t *services[], size_t len, bool include_ip) +{ + mdns_tx_packet_t *packet = _mdns_alloc_packet_default(tcpip_if, ip_protocol); + if (!packet) { + return NULL; + } + packet->flags = MDNS_FLAGS_QR_AUTHORITATIVE; + + uint8_t i; + for (i = 0; i < len; i++) { + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SDPTR, services[i]->service, NULL, false, false) + || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, services[i]->service, NULL, false, false) + || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, services[i]->service, NULL, true, false) + || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, services[i]->service, NULL, true, false)) { + _mdns_free_tx_packet(packet); + return NULL; + } + } + if (include_ip) { + if (!_mdns_append_host_list_in_services(&packet->servers, services, len, true, false)) { + _mdns_free_tx_packet(packet); + return NULL; + } + } + return packet; +} + +/** + * @brief Convert probe packet to announce + */ +static mdns_tx_packet_t *_mdns_create_announce_from_probe(mdns_tx_packet_t *probe) +{ + mdns_tx_packet_t *packet = _mdns_alloc_packet_default(probe->tcpip_if, probe->ip_protocol); + if (!packet) { + return NULL; + } + packet->flags = MDNS_FLAGS_QR_AUTHORITATIVE; + + mdns_out_answer_t *s = probe->servers; + while (s) { + if (s->type == MDNS_TYPE_SRV) { + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SDPTR, s->service, NULL, false, false) + || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, s->service, NULL, false, false) + || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_SRV, s->service, NULL, true, false) + || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_TXT, s->service, NULL, true, false)) { + _mdns_free_tx_packet(packet); + return NULL; + } + mdns_host_item_t *host = mdns_get_host_item(s->service->hostname); + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_A, NULL, host, true, false) + || !_mdns_alloc_answer(&packet->answers, MDNS_TYPE_AAAA, NULL, host, true, false)) { + _mdns_free_tx_packet(packet); + return NULL; + } + + } else if (s->type == MDNS_TYPE_A || s->type == MDNS_TYPE_AAAA) { + if (!_mdns_alloc_answer(&packet->answers, s->type, NULL, s->host, true, false)) { + _mdns_free_tx_packet(packet); + return NULL; + } + } + + s = s->next; + } + return packet; +} + +/** + * @brief Send by for particular services on particular PCB + */ +static void _mdns_pcb_send_bye(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t **services, size_t len, bool include_ip) +{ + mdns_tx_packet_t *packet = _mdns_alloc_packet_default(tcpip_if, ip_protocol); + if (!packet) { + return; + } + packet->flags = MDNS_FLAGS_QR_AUTHORITATIVE; + size_t i; + for (i = 0; i < len; i++) { + if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, services[i]->service, NULL, true, true)) { + _mdns_free_tx_packet(packet); + return; + } + } + if (include_ip) { + _mdns_append_host_list_in_services(&packet->answers, services, len, true, true); + } + _mdns_dispatch_tx_packet(packet); + _mdns_free_tx_packet(packet); +} + +/** + * @brief Send probe for additional services on particular PCB + */ +static void _mdns_init_pcb_probe_new_service(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t **services, size_t len, bool probe_ip) +{ + mdns_pcb_t *pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol]; + size_t services_final_len = len; + + if (PCB_STATE_IS_PROBING(pcb)) { + services_final_len += pcb->probe_services_len; + } + mdns_srv_item_t **_services = NULL; + if (services_final_len) { + _services = (mdns_srv_item_t **)malloc(sizeof(mdns_srv_item_t *) * services_final_len); + if (!_services) { + HOOK_MALLOC_FAILED; + return; + } + + size_t i; + for (i = 0; i < len; i++) { + _services[i] = services[i]; + } + if (pcb->probe_services) { + for (i = 0; i < pcb->probe_services_len; i++) { + _services[len + i] = pcb->probe_services[i]; + } + free(pcb->probe_services); + } + } + + probe_ip = pcb->probe_ip || probe_ip; + + pcb->probe_ip = false; + pcb->probe_services = NULL; + pcb->probe_services_len = 0; + pcb->probe_running = false; + + mdns_tx_packet_t *packet = _mdns_create_probe_packet(tcpip_if, ip_protocol, _services, services_final_len, true, probe_ip); + if (!packet) { + free(_services); + return; + } + + pcb->probe_ip = probe_ip; + pcb->probe_services = _services; + pcb->probe_services_len = services_final_len; + pcb->probe_running = true; + _mdns_schedule_tx_packet(packet, ((pcb->failed_probes > 5) ? 1000 : 120) + (esp_random() & 0x7F)); + pcb->state = PCB_PROBE_1; +} + +/** + * @brief Send probe for particular services on particular PCB + * + * Tests possible duplication on probing service structure and probes only for new entries. + * - If pcb probing then add only non-probing services and restarts probing + * - If pcb not probing, run probing for all specified services + */ +static void _mdns_init_pcb_probe(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t **services, size_t len, bool probe_ip) +{ + mdns_pcb_t *pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol]; + + _mdns_clear_pcb_tx_queue_head(tcpip_if, ip_protocol); + + if (_str_null_or_empty(_mdns_server->hostname)) { + pcb->state = PCB_RUNNING; + return; + } + + if (PCB_STATE_IS_PROBING(pcb)) { + // Looking for already probing services to resolve duplications + mdns_srv_item_t *new_probe_services[len]; + int new_probe_service_len = 0; + bool found; + for (size_t j = 0; j < len; ++j) { + found = false; + for (int i = 0; i < pcb->probe_services_len; ++i) { + if (pcb->probe_services[i] == services[j]) { + found = true; + break; + } + } + if (!found) { + new_probe_services[new_probe_service_len++] = services[j]; + } + } + // init probing for newly added services + _mdns_init_pcb_probe_new_service(tcpip_if, ip_protocol, + new_probe_service_len ? new_probe_services : NULL, new_probe_service_len, probe_ip); + } else { + // not probing, so init for all services + _mdns_init_pcb_probe_new_service(tcpip_if, ip_protocol, services, len, probe_ip); + } +} + +/** + * @brief Restart the responder on particular PCB + */ +static void _mdns_restart_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + size_t srv_count = 0; + mdns_srv_item_t *a = _mdns_server->services; + while (a) { + srv_count++; + a = a->next; + } + mdns_srv_item_t *services[srv_count]; + size_t i = 0; + a = _mdns_server->services; + while (a) { + services[i++] = a; + a = a->next; + } + _mdns_init_pcb_probe(tcpip_if, ip_protocol, services, srv_count, true); +} + +/** + * @brief Send by for particular services + */ +static void _mdns_send_bye(mdns_srv_item_t **services, size_t len, bool include_ip) +{ + uint8_t i, j; + if (_str_null_or_empty(_mdns_server->hostname)) { + return; + } + + for (i = 0; i < MDNS_MAX_INTERFACES; i++) { + for (j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) { + if (mdns_is_netif_ready(i, j) && _mdns_server->interfaces[i].pcbs[j].state == PCB_RUNNING) { + _mdns_pcb_send_bye((mdns_if_t)i, (mdns_ip_protocol_t)j, services, len, include_ip); + } + } + } +} + +/** + * @brief Send announcement on particular PCB + */ +static void _mdns_announce_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t **services, size_t len, bool include_ip) +{ + mdns_pcb_t *_pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol]; + size_t i; + if (mdns_is_netif_ready(tcpip_if, ip_protocol)) { + if (PCB_STATE_IS_PROBING(_pcb)) { + _mdns_init_pcb_probe(tcpip_if, ip_protocol, services, len, include_ip); + } else if (PCB_STATE_IS_ANNOUNCING(_pcb)) { + mdns_tx_packet_t *p = _mdns_get_next_pcb_packet(tcpip_if, ip_protocol); + if (p) { + for (i = 0; i < len; i++) { + if (!_mdns_alloc_answer(&p->answers, MDNS_TYPE_SDPTR, services[i]->service, NULL, false, false) + || !_mdns_alloc_answer(&p->answers, MDNS_TYPE_PTR, services[i]->service, NULL, false, false) + || !_mdns_alloc_answer(&p->answers, MDNS_TYPE_SRV, services[i]->service, NULL, true, false) + || !_mdns_alloc_answer(&p->answers, MDNS_TYPE_TXT, services[i]->service, NULL, true, false)) { + break; + } + } + if (include_ip) { + _mdns_dealloc_answer(&p->additional, MDNS_TYPE_A, NULL); + _mdns_dealloc_answer(&p->additional, MDNS_TYPE_AAAA, NULL); + _mdns_append_host_list_in_services(&p->answers, services, len, true, false); + } + _pcb->state = PCB_ANNOUNCE_1; + } + } else if (_pcb->state == PCB_RUNNING) { + + if (_str_null_or_empty(_mdns_server->hostname)) { + return; + } + + _pcb->state = PCB_ANNOUNCE_1; + mdns_tx_packet_t *p = _mdns_create_announce_packet(tcpip_if, ip_protocol, services, len, include_ip); + if (p) { + _mdns_schedule_tx_packet(p, 0); + } + } + } +} + +/** + * @brief Send probe on all active PCBs + */ +static void _mdns_probe_all_pcbs(mdns_srv_item_t **services, size_t len, bool probe_ip, bool clear_old_probe) +{ + uint8_t i, j; + for (i = 0; i < MDNS_MAX_INTERFACES; i++) { + for (j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) { + if (mdns_is_netif_ready(i, j)) { + mdns_pcb_t *_pcb = &_mdns_server->interfaces[i].pcbs[j]; + if (clear_old_probe) { + free(_pcb->probe_services); + _pcb->probe_services = NULL; + _pcb->probe_services_len = 0; + _pcb->probe_running = false; + } + _mdns_init_pcb_probe((mdns_if_t)i, (mdns_ip_protocol_t)j, services, len, probe_ip); + } + } + } +} + +/** + * @brief Send announcement on all active PCBs + */ +static void _mdns_announce_all_pcbs(mdns_srv_item_t **services, size_t len, bool include_ip) +{ + uint8_t i, j; + for (i = 0; i < MDNS_MAX_INTERFACES; i++) { + for (j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) { + _mdns_announce_pcb((mdns_if_t)i, (mdns_ip_protocol_t)j, services, len, include_ip); + } + } +} + +/** + * @brief Restart the responder on all active PCBs + */ +static void _mdns_send_final_bye(bool include_ip) +{ + //collect all services and start probe + size_t srv_count = 0; + mdns_srv_item_t *a = _mdns_server->services; + while (a) { + srv_count++; + a = a->next; + } + if (!srv_count) { + return; + } + mdns_srv_item_t *services[srv_count]; + size_t i = 0; + a = _mdns_server->services; + while (a) { + services[i++] = a; + a = a->next; + } + _mdns_send_bye(services, srv_count, include_ip); +} + +/** + * @brief Stop the responder on all services without instance + */ +static void _mdns_send_bye_all_pcbs_no_instance(bool include_ip) +{ + size_t srv_count = 0; + mdns_srv_item_t *a = _mdns_server->services; + while (a) { + if (!a->service->instance) { + srv_count++; + } + a = a->next; + } + if (!srv_count) { + return; + } + mdns_srv_item_t *services[srv_count]; + size_t i = 0; + a = _mdns_server->services; + while (a) { + if (!a->service->instance) { + services[i++] = a; + } + a = a->next; + } + _mdns_send_bye(services, srv_count, include_ip); +} + +/** + * @brief Restart the responder on all services without instance + */ +static void _mdns_restart_all_pcbs_no_instance(void) +{ + size_t srv_count = 0; + mdns_srv_item_t *a = _mdns_server->services; + while (a) { + if (!a->service->instance) { + srv_count++; + } + a = a->next; + } + if (!srv_count) { + return; + } + mdns_srv_item_t *services[srv_count]; + size_t i = 0; + a = _mdns_server->services; + while (a) { + if (!a->service->instance) { + services[i++] = a; + } + a = a->next; + } + _mdns_probe_all_pcbs(services, srv_count, false, true); +} + +/** + * @brief Restart the responder on all active PCBs + */ +static void _mdns_restart_all_pcbs(void) +{ + _mdns_clear_tx_queue_head(); + size_t srv_count = 0; + mdns_srv_item_t *a = _mdns_server->services; + while (a) { + srv_count++; + a = a->next; + } + mdns_srv_item_t *services[srv_count]; + size_t l = 0; + a = _mdns_server->services; + while (a) { + services[l++] = a; + a = a->next; + } + + _mdns_probe_all_pcbs(services, srv_count, true, true); +} + + + +/** + * @brief creates/allocates new text item list + * @param num_items service number of txt items or 0 + * @param txt service txt items array or NULL + * + * @return pointer to the linked txt item list or NULL + */ +static mdns_txt_linked_item_t *_mdns_allocate_txt(size_t num_items, mdns_txt_item_t txt[]) +{ + mdns_txt_linked_item_t *new_txt = NULL; + size_t i = 0; + if (num_items) { + for (i = 0; i < num_items; i++) { + mdns_txt_linked_item_t *new_item = (mdns_txt_linked_item_t *)malloc(sizeof(mdns_txt_linked_item_t)); + if (!new_item) { + HOOK_MALLOC_FAILED; + break; + } + new_item->key = strdup(txt[i].key); + if (!new_item->key) { + free(new_item); + break; + } + new_item->value = strdup(txt[i].value); + if (!new_item->value) { + free((char *)new_item->key); + free(new_item); + break; + } + new_item->value_len = strlen(new_item->value); + new_item->next = new_txt; + new_txt = new_item; + } + } + return new_txt; +} + +/** + * @brief Deallocate the txt linked list + * @param txt pointer to the txt pointer to free, noop if txt==NULL + */ +static void _mdns_free_linked_txt(mdns_txt_linked_item_t *txt) +{ + mdns_txt_linked_item_t *t; + while (txt) { + t = txt; + txt = txt->next; + free((char *)t->value); + free((char *)t->key); + free(t); + } +} + +/** + * @brief creates/allocates new service + * @param service service type + * @param proto service proto + * @param hostname service hostname + * @param port service port + * @param instance service instance + * @param num_items service number of txt items or 0 + * @param txt service txt items array or NULL + * + * @return pointer to the service or NULL on error + */ +static mdns_service_t *_mdns_create_service(const char *service, const char *proto, const char *hostname, + uint16_t port, const char *instance, size_t num_items, + mdns_txt_item_t txt[]) +{ + mdns_service_t *s = (mdns_service_t *)calloc(1, sizeof(mdns_service_t)); + if (!s) { + HOOK_MALLOC_FAILED; + return NULL; + } + + mdns_txt_linked_item_t *new_txt = _mdns_allocate_txt(num_items, txt); + if (num_items && new_txt == NULL) { + goto fail; + } + + s->priority = 0; + s->weight = 0; + s->instance = instance ? strndup(instance, MDNS_NAME_BUF_LEN - 1) : NULL; + s->txt = new_txt; + s->port = port; + s->subtype = NULL; + + if (hostname) { + s->hostname = strndup(hostname, MDNS_NAME_BUF_LEN - 1); + if (!s->hostname) { + goto fail; + } + } else { + s->hostname = NULL; + } + + s->service = strndup(service, MDNS_NAME_BUF_LEN - 1); + if (!s->service) { + goto fail; + } + + s->proto = strndup(proto, MDNS_NAME_BUF_LEN - 1); + if (!s->proto) { + goto fail; + } + return s; + +fail: + _mdns_free_linked_txt(s->txt); + free((char *)s->instance); + free((char *)s->service); + free((char *)s->proto); + free((char *)s->hostname); + free(s); + + return NULL; +} + +/** + * @brief Remove and free service answer from answer list (destination) + */ +static void _mdns_dealloc_scheduled_service_answers(mdns_out_answer_t **destination, mdns_service_t *service) +{ + mdns_out_answer_t *d = *destination; + if (!d) { + return; + } + while (d && d->service == service) { + *destination = d->next; + free(d); + d = *destination; + } + while (d && d->next) { + mdns_out_answer_t *a = d->next; + if (a->service == service) { + d->next = a->next; + free(a); + } else { + d = d->next; + } + } +} + +/** + * @brief Find, remove and free answers and scheduled packets for service + */ +static void _mdns_remove_scheduled_service_packets(mdns_service_t *service) +{ + if (!service) { + return; + } + mdns_tx_packet_t *p = NULL; + mdns_tx_packet_t *q = _mdns_server->tx_queue_head; + while (q) { + bool had_answers = (q->answers != NULL); + + _mdns_dealloc_scheduled_service_answers(&(q->answers), service); + _mdns_dealloc_scheduled_service_answers(&(q->additional), service); + _mdns_dealloc_scheduled_service_answers(&(q->servers), service); + + + mdns_pcb_t *_pcb = &_mdns_server->interfaces[q->tcpip_if].pcbs[q->ip_protocol]; + if (mdns_is_netif_ready(q->tcpip_if, q->ip_protocol)) { + if (PCB_STATE_IS_PROBING(_pcb)) { + uint8_t i; + //check if we are probing this service + for (i = 0; i < _pcb->probe_services_len; i++) { + mdns_srv_item_t *s = _pcb->probe_services[i]; + if (s->service == service) { + break; + } + } + if (i < _pcb->probe_services_len) { + if (_pcb->probe_services_len > 1) { + uint8_t n; + for (n = (i + 1); n < _pcb->probe_services_len; n++) { + _pcb->probe_services[n - 1] = _pcb->probe_services[n]; + } + _pcb->probe_services_len--; + } else { + _pcb->probe_services_len = 0; + free(_pcb->probe_services); + _pcb->probe_services = NULL; + if (!_pcb->probe_ip) { + _pcb->probe_running = false; + _pcb->state = PCB_RUNNING; + } + } + + if (q->questions) { + mdns_out_question_t *qsn = NULL; + mdns_out_question_t *qs = q->questions; + if (qs->type == MDNS_TYPE_ANY + && qs->service && strcmp(qs->service, service->service) == 0 + && qs->proto && strcmp(qs->proto, service->proto) == 0) { + q->questions = q->questions->next; + free(qs); + } else while (qs->next) { + qsn = qs->next; + if (qsn->type == MDNS_TYPE_ANY + && qsn->service && strcmp(qsn->service, service->service) == 0 + && qsn->proto && strcmp(qsn->proto, service->proto) == 0) { + qs->next = qsn->next; + free(qsn); + break; + } + qs = qs->next; + } + } + } + } else if (PCB_STATE_IS_ANNOUNCING(_pcb)) { + //if answers were cleared, set to running + if (had_answers && q->answers == NULL) { + _pcb->state = PCB_RUNNING; + } + } + } + + p = q; + q = q->next; + if (!p->questions && !p->answers && !p->additional && !p->servers) { + queueDetach(mdns_tx_packet_t, _mdns_server->tx_queue_head, p); + _mdns_free_tx_packet(p); + } + } +} + +static void _mdns_free_service_subtype(mdns_service_t *service) +{ + while (service->subtype) { + mdns_subtype_t *next = service->subtype->next; + free((char *)service->subtype->subtype); + free(service->subtype); + service->subtype = next; + } +} + +/** + * @brief free service memory + * + * @param service the service + */ +static void _mdns_free_service(mdns_service_t *service) +{ + if (!service) { + return; + } + free((char *)service->instance); + free((char *)service->service); + free((char *)service->proto); + free((char *)service->hostname); + while (service->txt) { + mdns_txt_linked_item_t *s = service->txt; + service->txt = service->txt->next; + free((char *)s->key); + free((char *)s->value); + free(s); + } + _mdns_free_service_subtype(service); + free(service); +} + + +/* + * Received Packet Handling + * */ + +/** + * @brief Detect SRV collision + */ +static int _mdns_check_srv_collision(mdns_service_t *service, uint16_t priority, uint16_t weight, uint16_t port, const char *host, const char *domain) +{ + if (_str_null_or_empty(_mdns_server->hostname)) { + return 0; + } + + size_t our_host_len = strlen(_mdns_server->hostname); + size_t our_len = 14 + our_host_len; + + size_t their_host_len = strlen(host); + size_t their_domain_len = strlen(domain); + size_t their_len = 9 + their_host_len + their_domain_len; + + if (their_len > our_len) { + return 1;//they win + } else if (their_len < our_len) { + return -1;//we win + } + + uint16_t our_index = 0; + uint8_t our_data[our_len]; + _mdns_append_u16(our_data, &our_index, service->priority); + _mdns_append_u16(our_data, &our_index, service->weight); + _mdns_append_u16(our_data, &our_index, service->port); + our_data[our_index++] = our_host_len; + memcpy(our_data + our_index, _mdns_server->hostname, our_host_len); + our_index += our_host_len; + our_data[our_index++] = 5; + memcpy(our_data + our_index, MDNS_DEFAULT_DOMAIN, 5); + our_index += 5; + our_data[our_index++] = 0; + + uint16_t their_index = 0; + uint8_t their_data[their_len]; + _mdns_append_u16(their_data, &their_index, priority); + _mdns_append_u16(their_data, &their_index, weight); + _mdns_append_u16(their_data, &their_index, port); + their_data[their_index++] = their_host_len; + memcpy(their_data + their_index, host, their_host_len); + their_index += their_host_len; + their_data[their_index++] = their_domain_len; + memcpy(their_data + their_index, domain, their_domain_len); + their_index += their_domain_len; + their_data[their_index++] = 0; + + int ret = memcmp(our_data, their_data, our_len); + if (ret > 0) { + return -1;//we win + } else if (ret < 0) { + return 1;//they win + } + return 0;//same +} + +/** + * @brief Detect TXT collision + */ +static int _mdns_check_txt_collision(mdns_service_t *service, const uint8_t *data, size_t len) +{ + size_t data_len = 0; + if (len == 1 && service->txt) { + return -1;//we win + } else if (len > 1 && !service->txt) { + return 1;//they win + } else if (len == 1 && !service->txt) { + return 0;//same + } + + mdns_txt_linked_item_t *txt = service->txt; + while (txt) { + data_len += 1 /* record-len */ + strlen(txt->key) + txt->value_len + (txt->value ? 1 : 0 /* "=" */); + txt = txt->next; + } + + if (len > data_len) { + return 1;//they win + } else if (len < data_len) { + return -1;//we win + } + + uint8_t ours[len]; + uint16_t index = 0; + + txt = service->txt; + while (txt) { + append_one_txt_record_entry(ours, &index, txt); + txt = txt->next; + } + + int ret = memcmp(ours, data, len); + if (ret > 0) { + return -1;//we win + } else if (ret < 0) { + return 1;//they win + } + return 0;//same +} + +static esp_err_t mdns_pcb_deinit_local(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_proto) +{ + esp_err_t err = _mdns_pcb_deinit(tcpip_if, ip_proto); + mdns_pcb_t *_pcb = &_mdns_server->interfaces[tcpip_if].pcbs[ip_proto]; + if (_pcb == NULL || err != ESP_OK) { + return err; + } + free(_pcb->probe_services); + _pcb->state = PCB_OFF; + _pcb->probe_ip = false; + _pcb->probe_services = NULL; + _pcb->probe_services_len = 0; + _pcb->probe_running = false; + _pcb->failed_probes = 0; + return ESP_OK; +} +/** + * @brief Set interface as duplicate if another is found on the same subnet + */ +static void _mdns_dup_interface(mdns_if_t tcpip_if) +{ + uint8_t i; + mdns_if_t other_if = _mdns_get_other_if (tcpip_if); + if (other_if == MDNS_MAX_INTERFACES) { + return; // no other interface found + } + for (i = 0; i < MDNS_IP_PROTOCOL_MAX; i++) { + if (mdns_is_netif_ready(other_if, i)) { + //stop this interface and mark as dup + if (mdns_is_netif_ready(tcpip_if, i)) { + _mdns_clear_pcb_tx_queue_head(tcpip_if, i); + mdns_pcb_deinit_local(tcpip_if, i); + } + _mdns_server->interfaces[tcpip_if].pcbs[i].state = PCB_DUP; + _mdns_announce_pcb(other_if, i, NULL, 0, true); + } + } +} + +#ifdef CONFIG_LWIP_IPV4 +/** + * @brief Detect IPv4 address collision + */ +static int _mdns_check_a_collision(esp_ip4_addr_t *ip, mdns_if_t tcpip_if) +{ + esp_netif_ip_info_t if_ip_info; + esp_netif_ip_info_t other_ip_info; + if (!ip->addr) { + return 1;//denial! they win + } + if (esp_netif_get_ip_info(_mdns_get_esp_netif(tcpip_if), &if_ip_info)) { + return 1;//they win + } + int ret = memcmp((uint8_t *)&if_ip_info.ip.addr, (uint8_t *)&ip->addr, sizeof(esp_ip4_addr_t)); + if (ret > 0) { + return -1;//we win + } else if (ret < 0) { + //is it the other interface? + mdns_if_t other_if = _mdns_get_other_if (tcpip_if); + if (other_if == MDNS_MAX_INTERFACES) { + return 1;//AP interface! They win + } + if (esp_netif_get_ip_info(_mdns_get_esp_netif(other_if), &other_ip_info)) { + return 1;//IPv4 not active! They win + } + if (ip->addr != other_ip_info.ip.addr) { + return 1;//IPv4 not ours! They win + } + _mdns_dup_interface(tcpip_if); + return 2;//they win + } + return 0;//same +} +#endif /* CONFIG_LWIP_IPV4 */ + +#ifdef CONFIG_LWIP_IPV6 +/** + * @brief Detect IPv6 address collision + */ +static int _mdns_check_aaaa_collision(esp_ip6_addr_t *ip, mdns_if_t tcpip_if) +{ + struct esp_ip6_addr if_ip6; + struct esp_ip6_addr other_ip6; + if (_ipv6_address_is_zero(*ip)) { + return 1;//denial! they win + } + if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(tcpip_if), &if_ip6)) { + return 1;//they win + } + int ret = memcmp((uint8_t *)&if_ip6.addr, (uint8_t *)ip->addr, _MDNS_SIZEOF_IP6_ADDR); + if (ret > 0) { + return -1;//we win + } else if (ret < 0) { + //is it the other interface? + mdns_if_t other_if = _mdns_get_other_if (tcpip_if); + if (other_if == MDNS_MAX_INTERFACES) { + return 1;//AP interface! They win + } + if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(other_if), &other_ip6)) { + return 1;//IPv6 not active! They win + } + if (memcmp((uint8_t *)&other_ip6.addr, (uint8_t *)ip->addr, _MDNS_SIZEOF_IP6_ADDR)) { + return 1;//IPv6 not ours! They win + } + _mdns_dup_interface(tcpip_if); + return 2;//they win + } + return 0;//same +} +#endif /* CONFIG_LWIP_IPV6 */ + +static bool _hostname_is_ours(const char *hostname) +{ + if (!_str_null_or_empty(_mdns_server->hostname) && + strcasecmp(hostname, _mdns_server->hostname) == 0) { + return true; + } + mdns_host_item_t *host = _mdns_host_list; + while (host != NULL) { + if (strcasecmp(hostname, host->hostname) == 0) { + return true; + } + host = host->next; + } + return false; +} + +/** + * @brief Adds a delegated hostname to the linked list + * @param hostname Host name pointer + * @param address_list Address list + * @return true on success + * false if the host wasn't attached (this is our hostname, or alloc failure) so we have to free the structs + */ +static bool _mdns_delegate_hostname_add(const char *hostname, mdns_ip_addr_t *address_list) +{ + if (_hostname_is_ours(hostname)) { + return false; + } + + mdns_host_item_t *host = (mdns_host_item_t *)malloc(sizeof(mdns_host_item_t)); + + if (host == NULL) { + return false; + } + host->address_list = address_list; + host->hostname = hostname; + host->next = _mdns_host_list; + _mdns_host_list = host; + return true; +} + +static void free_address_list(mdns_ip_addr_t *address_list) +{ + while (address_list != NULL) { + mdns_ip_addr_t *next = address_list->next; + free(address_list); + address_list = next; + } +} + + +static bool _mdns_delegate_hostname_set_address(const char *hostname, mdns_ip_addr_t *address_list) +{ + if (!_str_null_or_empty(_mdns_server->hostname) && + strcasecmp(hostname, _mdns_server->hostname) == 0) { + return false; + } + mdns_host_item_t *host = _mdns_host_list; + while (host != NULL) { + if (strcasecmp(hostname, host->hostname) == 0) { + // free previous address list + free_address_list(host->address_list); + // set current address list to the host + host->address_list = address_list; + return true; + } + host = host->next; + } + return false; +} + +static mdns_ip_addr_t *copy_address_list(const mdns_ip_addr_t *address_list) +{ + mdns_ip_addr_t *head = NULL; + mdns_ip_addr_t *tail = NULL; + while (address_list != NULL) { + mdns_ip_addr_t *addr = (mdns_ip_addr_t *)malloc(sizeof(mdns_ip_addr_t)); + if (addr == NULL) { + free_address_list(head); + return NULL; + } + addr->addr = address_list->addr; + addr->next = NULL; + if (head == NULL) { + head = addr; + tail = addr; + } else { + tail->next = addr; + tail = tail->next; + } + address_list = address_list->next; + } + return head; +} + +static void free_delegated_hostnames(void) +{ + mdns_host_item_t *host = _mdns_host_list; + while (host != NULL) { + free_address_list(host->address_list); + free((char *)host->hostname); + mdns_host_item_t *item = host; + host = host->next; + free(item); + } + _mdns_host_list = NULL; +} + +static bool _mdns_delegate_hostname_remove(const char *hostname) +{ + mdns_srv_item_t *srv = _mdns_server->services; + mdns_srv_item_t *prev_srv = NULL; + while (srv) { + if (strcasecmp(srv->service->hostname, hostname) == 0) { + mdns_srv_item_t *to_free = srv; + _mdns_send_bye(&srv, 1, false); + _mdns_remove_scheduled_service_packets(srv->service); + if (prev_srv == NULL) { + _mdns_server->services = srv->next; + srv = srv->next; + } else { + prev_srv->next = srv->next; + srv = srv->next; + } + _mdns_free_service(to_free->service); + free(to_free); + } else { + prev_srv = srv; + srv = srv->next; + } + } + mdns_host_item_t *host = _mdns_host_list; + mdns_host_item_t *prev_host = NULL; + while (host != NULL) { + if (strcasecmp(hostname, host->hostname) == 0) { + if (prev_host == NULL) { + _mdns_host_list = host->next; + } else { + prev_host->next = host->next; + } + free_address_list(host->address_list); + free((char *)host->hostname); + free(host); + break; + } else { + prev_host = host; + host = host->next; + } + } + return true; +} + +/** + * @brief Check if parsed name is discovery + */ +static bool _mdns_name_is_discovery(mdns_name_t *name, uint16_t type) +{ + return ( + (name->host[0] && !strcasecmp(name->host, "_services")) + && (name->service[0] && !strcasecmp(name->service, "_dns-sd")) + && (name->proto[0] && !strcasecmp(name->proto, "_udp")) + && (name->domain[0] && !strcasecmp(name->domain, MDNS_DEFAULT_DOMAIN)) + && type == MDNS_TYPE_PTR + ); +} + +/** + * @brief Check if the parsed name is self-hosted, i.e. we should resolve conflicts + */ +static bool _mdns_name_is_selfhosted(mdns_name_t *name) +{ + if (_str_null_or_empty(_mdns_server->hostname)) { // self-hostname needs to be defined + return false; + } + + // hostname only -- check if selfhosted name + if (_str_null_or_empty(name->service) && _str_null_or_empty(name->proto) && + strcasecmp(name->host, _mdns_server->hostname) == 0 ) { + return true; + } + + // service -- check if selfhosted service + mdns_srv_item_t *srv = _mdns_get_service_item(name->service, name->proto, NULL); + if (srv && strcasecmp(_mdns_server->hostname, srv->service->hostname) == 0) { + return true; + } + return false; +} + +/** + * @brief Check if the parsed name is ours (matches service or host name) + */ +static bool _mdns_name_is_ours(mdns_name_t *name) +{ + //domain have to be "local" + if (_str_null_or_empty(name->domain) || ( strcasecmp(name->domain, MDNS_DEFAULT_DOMAIN) +#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES + && strcasecmp(name->domain, "arpa") +#endif /* CONFIG_MDNS_RESPOND_REVERSE_QUERIES */ + ) ) { + return false; + } + + //if service and proto are empty, host must match out hostname + if (_str_null_or_empty(name->service) && _str_null_or_empty(name->proto)) { + if (!_str_null_or_empty(name->host) + && !_str_null_or_empty(_mdns_server->hostname) + && _hostname_is_ours(name->host)) { + return true; + } + return false; + } + + //if service or proto is empty, name is invalid + if (_str_null_or_empty(name->service) || _str_null_or_empty(name->proto)) { + return false; + } + + + //find the service + mdns_srv_item_t *service; + if (name->sub) { + service = _mdns_get_service_item_subtype(name->host, name->service, name->proto); + } else if (_str_null_or_empty(name->host)) { + service = _mdns_get_service_item(name->service, name->proto, NULL); + } else { + service = _mdns_get_service_item_instance(name->host, name->service, name->proto, NULL); + } + if (!service) { + return false; + } + + //if query is PTR query and we have service, we have success + if (name->sub || _str_null_or_empty(name->host)) { + return true; + } + + //OK we have host in the name. find what is the instance of the service + const char *instance = _mdns_get_service_instance_name(service->service); + if (instance == NULL) { + return false; + } + + //compare the instance against the name + if (strcasecmp(name->host, instance) == 0) { + return true; + } + + return false; +} + +/** + * @brief read uint16_t from a packet + * @param packet the packet + * @param index index in the packet where the value starts + * + * @return the value + */ +static inline uint16_t _mdns_read_u16(const uint8_t *packet, uint16_t index) +{ + return (uint16_t)(packet[index]) << 8 | packet[index + 1]; +} + +/** + * @brief read uint32_t from a packet + * @param packet the packet + * @param index index in the packet where the value starts + * + * @return the value + */ +static inline uint32_t _mdns_read_u32(const uint8_t *packet, uint16_t index) +{ + return (uint32_t)(packet[index]) << 24 | (uint32_t)(packet[index + 1]) << 16 | (uint32_t)(packet[index + 2]) << 8 | packet[index + 3]; +} + +/** + * @brief reads and formats MDNS FQDN into mdns_name_t structure + * + * @param packet MDNS packet + * @param start Starting point of FQDN + * @param name mdns_name_t structure to populate + * + * @return the address after the parsed FQDN in the packet or NULL on error + */ +static const uint8_t *_mdns_parse_fqdn(const uint8_t *packet, const uint8_t *start, mdns_name_t *name, size_t packet_len) +{ + name->parts = 0; + name->sub = 0; + name->host[0] = 0; + name->service[0] = 0; + name->proto[0] = 0; + name->domain[0] = 0; + name->invalid = false; + + static char buf[MDNS_NAME_BUF_LEN]; + + const uint8_t *next_data = (uint8_t *)_mdns_read_fqdn(packet, start, name, buf, packet_len); + if (!next_data) { + return 0; + } + if (!name->parts || name->invalid) { + return next_data; + } + if (name->parts == 3) { + memmove((uint8_t *)name + (MDNS_NAME_BUF_LEN), (uint8_t *)name, 3 * (MDNS_NAME_BUF_LEN)); + name->host[0] = 0; + } else if (name->parts == 2) { + memmove((uint8_t *)(name->domain), (uint8_t *)(name->service), (MDNS_NAME_BUF_LEN)); + name->service[0] = 0; + name->proto[0] = 0; + } + if (strcasecmp(name->domain, MDNS_DEFAULT_DOMAIN) == 0 || strcasecmp(name->domain, "arpa") == 0) { + return next_data; + } + name->invalid = true; // mark the current name invalid, but continue with other question + return next_data; +} + +/** + * @brief Called from parser to check if question matches particular service + */ +static bool _mdns_question_matches(mdns_parsed_question_t *question, uint16_t type, mdns_srv_item_t *service) +{ + if (question->type != type) { + return false; + } + if (type == MDNS_TYPE_A || type == MDNS_TYPE_AAAA) { + return true; + } else if (type == MDNS_TYPE_PTR || type == MDNS_TYPE_SDPTR) { + if (question->service && question->proto && question->domain + && !strcasecmp(service->service->service, question->service) + && !strcasecmp(service->service->proto, question->proto) + && !strcasecmp(MDNS_DEFAULT_DOMAIN, question->domain)) { + if (!service->service->instance) { + return true; + } else if (service->service->instance && question->host && !strcasecmp(service->service->instance, question->host)) { + return true; + } + } + } else if (service && (type == MDNS_TYPE_SRV || type == MDNS_TYPE_TXT)) { + const char *name = _mdns_get_service_instance_name(service->service); + if (name && question->host && question->service && question->proto && question->domain + && !strcasecmp(name, question->host) + && !strcasecmp(service->service->service, question->service) + && !strcasecmp(service->service->proto, question->proto) + && !strcasecmp(MDNS_DEFAULT_DOMAIN, question->domain)) { + return true; + } + } + + return false; +} + +/** + * @brief Removes saved question from parsed data + */ +static void _mdns_remove_parsed_question(mdns_parsed_packet_t *parsed_packet, uint16_t type, mdns_srv_item_t *service) +{ + mdns_parsed_question_t *q = parsed_packet->questions; + + if (_mdns_question_matches(q, type, service)) { + parsed_packet->questions = q->next; + free(q->host); + free(q->service); + free(q->proto); + free(q->domain); + free(q); + return; + } + + while (q->next) { + mdns_parsed_question_t *p = q->next; + if (_mdns_question_matches(p, type, service)) { + q->next = p->next; + free(p->host); + free(p->service); + free(p->proto); + free(p->domain); + free(p); + return; + } + q = q->next; + } +} + +/** + * @brief Get number of items in TXT parsed data + */ +static int _mdns_txt_items_count_get(const uint8_t *data, size_t len) +{ + if (len == 1) { + return 0; + } + + int num_items = 0; + uint16_t i = 0; + size_t partLen = 0; + + while (i < len) { + partLen = data[i++]; + if (!partLen) { + break; + } + if ((i + partLen) > len) { + return -1;//error + } + i += partLen; + num_items++; + } + return num_items; +} + +/** + * @brief Get the length of TXT item's key name + */ +static int _mdns_txt_item_name_get_len(const uint8_t *data, size_t len) +{ + if (*data == '=') { + return -1; + } + for (size_t i = 0; i < len; i++) { + if (data[i] == '=') { + return i; + } + } + return len; +} + +/** + * @brief Create TXT result array from parsed TXT data + */ +static void _mdns_result_txt_create(const uint8_t *data, size_t len, mdns_txt_item_t **out_txt, uint8_t **out_value_len, + size_t *out_count) +{ + *out_txt = NULL; + *out_count = 0; + uint16_t i = 0, y; + size_t partLen = 0; + int num_items = _mdns_txt_items_count_get(data, len); + if (num_items < 0 || num_items > SIZE_MAX / sizeof(mdns_txt_item_t)) { + // Error: num_items is incorrect (or too large to allocate) + return; + } + + if (!num_items) { + return; + } + + mdns_txt_item_t *txt = (mdns_txt_item_t *)malloc(sizeof(mdns_txt_item_t) * num_items); + if (!txt) { + HOOK_MALLOC_FAILED; + return; + } + uint8_t *txt_value_len = (uint8_t *)malloc(num_items); + if (!txt_value_len) { + free(txt); + HOOK_MALLOC_FAILED; + return; + } + memset(txt, 0, sizeof(mdns_txt_item_t) * num_items); + memset(txt_value_len, 0, num_items); + size_t txt_num = 0; + + while (i < len) { + partLen = data[i++]; + if (!partLen) { + break; + } + + if ((i + partLen) > len) { + goto handle_error;//error + } + + int name_len = _mdns_txt_item_name_get_len(data + i, partLen); + if (name_len < 0) {//invalid item (no name) + i += partLen; + continue; + } + char *key = (char *)malloc(name_len + 1); + if (!key) { + HOOK_MALLOC_FAILED; + goto handle_error;//error + } + + mdns_txt_item_t *t = &txt[txt_num]; + uint8_t *value_len = &txt_value_len[txt_num]; + txt_num++; + + memcpy(key, data + i, name_len); + key[name_len] = 0; + i += name_len + 1; + t->key = key; + + int new_value_len = partLen - name_len - 1; + if (new_value_len > 0) { + char *value = (char *)malloc(new_value_len + 1); + if (!value) { + HOOK_MALLOC_FAILED; + goto handle_error;//error + } + memcpy(value, data + i, new_value_len); + value[new_value_len] = 0; + *value_len = new_value_len; + i += new_value_len; + t->value = value; + } + } + + *out_txt = txt; + *out_count = txt_num; + *out_value_len = txt_value_len; + return; + +handle_error : + for (y = 0; y < txt_num; y++) { + mdns_txt_item_t *t = &txt[y]; + free((char *)t->key); + free((char *)t->value); + } + free(txt_value_len); + free(txt); +} + +/** + * @brief Duplicate string or return error + */ +static esp_err_t _mdns_strdup_check(char **out, char *in) +{ + if (in && in[0]) { + *out = strdup(in); + if (!*out) { + return ESP_FAIL; + } + return ESP_OK; + } + *out = NULL; + return ESP_OK; +} + +/** + * @brief main packet parser + * + * @param packet the packet + */ +void mdns_parse_packet(mdns_rx_packet_t *packet) +{ + static mdns_name_t n; + mdns_header_t header; + const uint8_t *data = _mdns_get_packet_data(packet); + size_t len = _mdns_get_packet_len(packet); + const uint8_t *content = data + MDNS_HEAD_LEN; + bool do_not_reply = false; + mdns_search_once_t *search_result = NULL; + mdns_browse_t *browse_result = NULL; + char *browse_result_instance = NULL; + char *browse_result_service = NULL; + char *browse_result_proto = NULL; + mdns_browse_sync_t *out_sync_browse = NULL; + +#ifdef MDNS_ENABLE_DEBUG + _mdns_dbg_printf("\nRX[%lu][%lu]: ", (unsigned long)packet->tcpip_if, (unsigned long)packet->ip_protocol); +#ifdef CONFIG_LWIP_IPV4 + if (packet->src.type == ESP_IPADDR_TYPE_V4) { + _mdns_dbg_printf("From: " IPSTR ":%u, To: " IPSTR ", ", IP2STR(&packet->src.u_addr.ip4), packet->src_port, IP2STR(&packet->dest.u_addr.ip4)); + } +#endif +#ifdef CONFIG_LWIP_IPV6 + if (packet->src.type == ESP_IPADDR_TYPE_V6) { + _mdns_dbg_printf("From: " IPV6STR ":%u, To: " IPV6STR ", ", IPV62STR(packet->src.u_addr.ip6), packet->src_port, IPV62STR(packet->dest.u_addr.ip6)); + } +#endif + mdns_debug_packet(data, len); +#endif + +#ifndef CONFIG_MDNS_SKIP_SUPPRESSING_OWN_QUERIES + // Check if the packet wasn't sent by us +#ifdef CONFIG_LWIP_IPV4 + if (packet->ip_protocol == MDNS_IP_PROTOCOL_V4) { + esp_netif_ip_info_t if_ip_info; + if (esp_netif_get_ip_info(_mdns_get_esp_netif(packet->tcpip_if), &if_ip_info) == ESP_OK && + memcmp(&if_ip_info.ip.addr, &packet->src.u_addr.ip4.addr, sizeof(esp_ip4_addr_t)) == 0) { + return; + } + } +#endif /* CONFIG_LWIP_IPV4 */ +#ifdef CONFIG_LWIP_IPV6 + if (packet->ip_protocol == MDNS_IP_PROTOCOL_V6) { + struct esp_ip6_addr if_ip6; + if (esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(packet->tcpip_if), &if_ip6) == ESP_OK && + memcmp(&if_ip6, &packet->src.u_addr.ip6, sizeof(esp_ip6_addr_t)) == 0) { + return; + } + } +#endif /* CONFIG_LWIP_IPV6 */ +#endif // CONFIG_MDNS_SKIP_SUPPRESSING_OWN_QUERIES + + // Check for the minimum size of mdns packet + if (len <= MDNS_HEAD_ADDITIONAL_OFFSET) { + return; + } + + mdns_parsed_packet_t *parsed_packet = (mdns_parsed_packet_t *)malloc(sizeof(mdns_parsed_packet_t)); + if (!parsed_packet) { + HOOK_MALLOC_FAILED; + return; + } + memset(parsed_packet, 0, sizeof(mdns_parsed_packet_t)); + + mdns_name_t *name = &n; + memset(name, 0, sizeof(mdns_name_t)); + + header.id = _mdns_read_u16(data, MDNS_HEAD_ID_OFFSET); + header.flags = _mdns_read_u16(data, MDNS_HEAD_FLAGS_OFFSET); + header.questions = _mdns_read_u16(data, MDNS_HEAD_QUESTIONS_OFFSET); + header.answers = _mdns_read_u16(data, MDNS_HEAD_ANSWERS_OFFSET); + header.servers = _mdns_read_u16(data, MDNS_HEAD_SERVERS_OFFSET); + header.additional = _mdns_read_u16(data, MDNS_HEAD_ADDITIONAL_OFFSET); + + if (header.flags == MDNS_FLAGS_QR_AUTHORITATIVE && packet->src_port != MDNS_SERVICE_PORT) { + free(parsed_packet); + return; + } + + //if we have not set the hostname, we can not answer questions + if (header.questions && !header.answers && _str_null_or_empty(_mdns_server->hostname)) { + free(parsed_packet); + return; + } + + parsed_packet->tcpip_if = packet->tcpip_if; + parsed_packet->ip_protocol = packet->ip_protocol; + parsed_packet->multicast = packet->multicast; + parsed_packet->authoritative = (header.flags == MDNS_FLAGS_QR_AUTHORITATIVE); + parsed_packet->distributed = header.flags == MDNS_FLAGS_DISTRIBUTED; + parsed_packet->id = header.id; + esp_netif_ip_addr_copy(&parsed_packet->src, &packet->src); + parsed_packet->src_port = packet->src_port; + parsed_packet->records = NULL; + + if (header.questions) { + uint8_t qs = header.questions; + + while (qs--) { + content = _mdns_parse_fqdn(data, content, name, len); + if (!content) { + header.answers = 0; + header.additional = 0; + header.servers = 0; + goto clear_rx_packet;//error + } + + if (content + MDNS_CLASS_OFFSET + 1 >= data + len) { + goto clear_rx_packet; // malformed packet, won't read behind it + } + uint16_t type = _mdns_read_u16(content, MDNS_TYPE_OFFSET); + uint16_t mdns_class = _mdns_read_u16(content, MDNS_CLASS_OFFSET); + bool unicast = !!(mdns_class & 0x8000); + mdns_class &= 0x7FFF; + content = content + 4; + + if (mdns_class != 0x0001 || name->invalid) {//bad class or invalid name for this question entry + continue; + } + + if (_mdns_name_is_discovery(name, type)) { + //service discovery + parsed_packet->discovery = true; + mdns_srv_item_t *a = _mdns_server->services; + while (a) { + mdns_parsed_question_t *question = (mdns_parsed_question_t *)calloc(1, sizeof(mdns_parsed_question_t)); + if (!question) { + HOOK_MALLOC_FAILED; + goto clear_rx_packet; + } + question->next = parsed_packet->questions; + parsed_packet->questions = question; + + question->unicast = unicast; + question->type = MDNS_TYPE_SDPTR; + question->host = NULL; + question->service = strdup(a->service->service); + question->proto = strdup(a->service->proto); + question->domain = strdup(MDNS_DEFAULT_DOMAIN); + if (!question->service || !question->proto || !question->domain) { + goto clear_rx_packet; + } + a = a->next; + } + continue; + } + if (!_mdns_name_is_ours(name)) { + continue; + } + + if (type == MDNS_TYPE_ANY && !_str_null_or_empty(name->host)) { + parsed_packet->probe = true; + } + + mdns_parsed_question_t *question = (mdns_parsed_question_t *)calloc(1, sizeof(mdns_parsed_question_t)); + if (!question) { + HOOK_MALLOC_FAILED; + goto clear_rx_packet; + } + question->next = parsed_packet->questions; + parsed_packet->questions = question; + + question->unicast = unicast; + question->type = type; + question->sub = name->sub; + if (_mdns_strdup_check(&(question->host), name->host) + || _mdns_strdup_check(&(question->service), name->service) + || _mdns_strdup_check(&(question->proto), name->proto) + || _mdns_strdup_check(&(question->domain), name->domain)) { + goto clear_rx_packet; + } + } + } + + if (header.questions && !parsed_packet->questions && !parsed_packet->discovery && !header.answers) { + goto clear_rx_packet; + } else if (header.answers || header.servers || header.additional) { + uint16_t recordIndex = 0; + + while (content < (data + len)) { + + content = _mdns_parse_fqdn(data, content, name, len); + if (!content) { + goto clear_rx_packet;//error + } + + if (content + MDNS_LEN_OFFSET + 1 >= data + len) { + goto clear_rx_packet; // malformed packet, won't read behind it + } + uint16_t type = _mdns_read_u16(content, MDNS_TYPE_OFFSET); + uint16_t mdns_class = _mdns_read_u16(content, MDNS_CLASS_OFFSET); + uint32_t ttl = _mdns_read_u32(content, MDNS_TTL_OFFSET); + uint16_t data_len = _mdns_read_u16(content, MDNS_LEN_OFFSET); + const uint8_t *data_ptr = content + MDNS_DATA_OFFSET; + mdns_class &= 0x7FFF; + + content = data_ptr + data_len; + if (content > (data + len)) { + goto clear_rx_packet; + } + + bool discovery = false; + bool ours = false; + mdns_srv_item_t *service = NULL; + mdns_parsed_record_type_t record_type = MDNS_ANSWER; + + if (recordIndex >= (header.answers + header.servers)) { + record_type = MDNS_EXTRA; + } else if (recordIndex >= (header.answers)) { + record_type = MDNS_NS; + } + recordIndex++; + + if (type == MDNS_TYPE_NSEC || type == MDNS_TYPE_OPT) { + //skip NSEC and OPT + continue; + } + + if (parsed_packet->discovery && _mdns_name_is_discovery(name, type)) { + discovery = true; + } else if (!name->sub && _mdns_name_is_ours(name)) { + ours = true; + if (name->service[0] && name->proto[0]) { + service = _mdns_get_service_item(name->service, name->proto, NULL); + } + } else { + if ((header.flags & MDNS_FLAGS_QUERY_REPSONSE) == 0 || record_type == MDNS_NS) { + //skip this record + continue; + } + search_result = _mdns_search_find_from(_mdns_server->search_once, name, type, packet->tcpip_if, packet->ip_protocol); + browse_result = _mdns_browse_find_from(_mdns_server->browse, name, type, packet->tcpip_if, packet->ip_protocol); + if (browse_result) { + if (!out_sync_browse) { + // will be freed in function `_mdns_browse_sync` + out_sync_browse = (mdns_browse_sync_t *)malloc(sizeof(mdns_browse_sync_t)); + if (!out_sync_browse) { + HOOK_MALLOC_FAILED; + goto clear_rx_packet; + } + out_sync_browse->browse = browse_result; + out_sync_browse->sync_result = NULL; + } + if (!browse_result_service) { + browse_result_service = (char *)malloc(MDNS_NAME_BUF_LEN); + if (!browse_result_service) { + HOOK_MALLOC_FAILED; + goto clear_rx_packet; + } + } + memcpy(browse_result_service, browse_result->service, MDNS_NAME_BUF_LEN); + if (!browse_result_proto) { + browse_result_proto = (char *)malloc(MDNS_NAME_BUF_LEN); + if (!browse_result_proto) { + HOOK_MALLOC_FAILED; + goto clear_rx_packet; + } + } + memcpy(browse_result_proto, browse_result->proto, MDNS_NAME_BUF_LEN); + if (type == MDNS_TYPE_SRV || type == MDNS_TYPE_TXT) { + if (!browse_result_instance) { + browse_result_instance = (char *)malloc(MDNS_NAME_BUF_LEN); + if (!browse_result_instance) { + HOOK_MALLOC_FAILED; + goto clear_rx_packet; + } + } + memcpy(browse_result_instance, name->host, MDNS_NAME_BUF_LEN); + } + } + } + + if (type == MDNS_TYPE_PTR) { + if (!_mdns_parse_fqdn(data, data_ptr, name, len)) { + continue;//error + } + if (search_result) { + _mdns_search_result_add_ptr(search_result, name->host, name->service, name->proto, + packet->tcpip_if, packet->ip_protocol, ttl); + } else if ((discovery || ours) && !name->sub && _mdns_name_is_ours(name)) { + if (name->host[0]) { + service = _mdns_get_service_item_instance(name->host, name->service, name->proto, NULL); + } else { + service = _mdns_get_service_item(name->service, name->proto, NULL); + } + if (discovery && service) { + _mdns_remove_parsed_question(parsed_packet, MDNS_TYPE_SDPTR, service); + } else if (service && parsed_packet->questions && !parsed_packet->probe) { + _mdns_remove_parsed_question(parsed_packet, type, service); + } else if (service) { + //check if TTL is more than half of the full TTL value (4500) + if (ttl > (MDNS_ANSWER_PTR_TTL / 2)) { + _mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service); + } + } + if (service) { + mdns_parsed_record_t *record = malloc(sizeof(mdns_parsed_record_t)); + if (!record) { + HOOK_MALLOC_FAILED; + goto clear_rx_packet; + } + record->next = parsed_packet->records; + parsed_packet->records = record; + record->type = MDNS_TYPE_PTR; + record->record_type = MDNS_ANSWER; + record->ttl = ttl; + record->host = NULL; + record->service = NULL; + record->proto = NULL; + if (name->host[0]) { + record->host = malloc(MDNS_NAME_BUF_LEN); + if (!record->host) { + HOOK_MALLOC_FAILED; + goto clear_rx_packet; + } + memcpy(record->host, name->host, MDNS_NAME_BUF_LEN); + } + if (name->service[0]) { + record->service = malloc(MDNS_NAME_BUF_LEN); + if (!record->service) { + HOOK_MALLOC_FAILED; + goto clear_rx_packet; + } + memcpy(record->service, name->service, MDNS_NAME_BUF_LEN); + } + if (name->proto[0]) { + record->proto = malloc(MDNS_NAME_BUF_LEN); + if (!record->proto) { + HOOK_MALLOC_FAILED; + goto clear_rx_packet; + } + memcpy(record->proto, name->proto, MDNS_NAME_BUF_LEN); + } + } + } + } else if (type == MDNS_TYPE_SRV) { + mdns_result_t *result = NULL; + if (search_result && search_result->type == MDNS_TYPE_PTR) { + result = search_result->result; + while (result) { + if (_mdns_get_esp_netif(packet->tcpip_if) == result->esp_netif + && packet->ip_protocol == result->ip_protocol + && result->instance_name && !strcmp(name->host, result->instance_name)) { + break; + } + result = result->next; + } + if (!result) { + result = _mdns_search_result_add_ptr(search_result, name->host, name->service, name->proto, + packet->tcpip_if, packet->ip_protocol, ttl); + if (!result) { + continue;//error + } + } + } + bool is_selfhosted = _mdns_name_is_selfhosted(name); + if (!_mdns_parse_fqdn(data, data_ptr + MDNS_SRV_FQDN_OFFSET, name, len)) { + continue;//error + } + if (data_ptr + MDNS_SRV_PORT_OFFSET + 1 >= data + len) { + goto clear_rx_packet; // malformed packet, won't read behind it + } + uint16_t priority = _mdns_read_u16(data_ptr, MDNS_SRV_PRIORITY_OFFSET); + uint16_t weight = _mdns_read_u16(data_ptr, MDNS_SRV_WEIGHT_OFFSET); + uint16_t port = _mdns_read_u16(data_ptr, MDNS_SRV_PORT_OFFSET); + + if (browse_result) { + _mdns_browse_result_add_srv(browse_result, name->host, browse_result_instance, browse_result_service, + browse_result_proto, port, packet->tcpip_if, packet->ip_protocol, ttl, out_sync_browse); + } + if (search_result) { + if (search_result->type == MDNS_TYPE_PTR) { + if (!result->hostname) { // assign host/port for this entry only if not previously set + result->port = port; + result->hostname = strdup(name->host); + } + } else { + _mdns_search_result_add_srv(search_result, name->host, port, packet->tcpip_if, packet->ip_protocol, ttl); + } + } else if (ours) { + if (parsed_packet->questions && !parsed_packet->probe) { + _mdns_remove_parsed_question(parsed_packet, type, service); + continue; + } else if (parsed_packet->distributed) { + _mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service); + continue; + } + if (!is_selfhosted) { + continue; + } + //detect collision (-1=won, 0=none, 1=lost) + int col = 0; + if (mdns_class > 1) { + col = 1; + } else if (!mdns_class) { + col = -1; + } else if (service) { // only detect srv collision if service existed + col = _mdns_check_srv_collision(service->service, priority, weight, port, name->host, name->domain); + } + if (service && col && (parsed_packet->probe || parsed_packet->authoritative)) { + if (col > 0 || !port) { + do_not_reply = true; + if (_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) { + _mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].failed_probes++; + if (!_str_null_or_empty(service->service->instance)) { + char *new_instance = _mdns_mangle_name((char *)service->service->instance); + if (new_instance) { + free((char *)service->service->instance); + service->service->instance = new_instance; + } + _mdns_probe_all_pcbs(&service, 1, false, false); + } else if (!_str_null_or_empty(_mdns_server->instance)) { + char *new_instance = _mdns_mangle_name((char *)_mdns_server->instance); + if (new_instance) { + free((char *)_mdns_server->instance); + _mdns_server->instance = new_instance; + } + _mdns_restart_all_pcbs_no_instance(); + } else { + char *new_host = _mdns_mangle_name((char *)_mdns_server->hostname); + if (new_host) { + _mdns_remap_self_service_hostname(_mdns_server->hostname, new_host); + free((char *)_mdns_server->hostname); + _mdns_server->hostname = new_host; + _mdns_self_host.hostname = new_host; + } + _mdns_restart_all_pcbs(); + } + } else if (service) { + _mdns_pcb_send_bye(packet->tcpip_if, packet->ip_protocol, &service, 1, false); + _mdns_init_pcb_probe(packet->tcpip_if, packet->ip_protocol, &service, 1, false); + } + } + } else if (ttl > 60 && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions) { + _mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service); + } + } + } else if (type == MDNS_TYPE_TXT) { + mdns_txt_item_t *txt = NULL; + uint8_t *txt_value_len = NULL; + size_t txt_count = 0; + + mdns_result_t *result = NULL; + if (browse_result) { + _mdns_result_txt_create(data_ptr, data_len, &txt, &txt_value_len, &txt_count); + _mdns_browse_result_add_txt(browse_result, browse_result_instance, browse_result_service, browse_result_proto, + txt, txt_value_len, txt_count, packet->tcpip_if, packet->ip_protocol, ttl, out_sync_browse); + } + if (search_result) { + if (search_result->type == MDNS_TYPE_PTR) { + result = search_result->result; + while (result) { + if (_mdns_get_esp_netif(packet->tcpip_if) == result->esp_netif + && packet->ip_protocol == result->ip_protocol + && result->instance_name && !strcmp(name->host, result->instance_name)) { + break; + } + result = result->next; + } + if (!result) { + result = _mdns_search_result_add_ptr(search_result, name->host, name->service, name->proto, + packet->tcpip_if, packet->ip_protocol, ttl); + if (!result) { + continue;//error + } + } + if (!result->txt) { + _mdns_result_txt_create(data_ptr, data_len, &txt, &txt_value_len, &txt_count); + if (txt_count) { + result->txt = txt; + result->txt_count = txt_count; + result->txt_value_len = txt_value_len; + } + } + } else { + _mdns_result_txt_create(data_ptr, data_len, &txt, &txt_value_len, &txt_count); + if (txt_count) { + _mdns_search_result_add_txt(search_result, txt, txt_value_len, txt_count, packet->tcpip_if, packet->ip_protocol, ttl); + } + } + } else if (ours) { + if (parsed_packet->questions && !parsed_packet->probe && service) { + _mdns_remove_parsed_question(parsed_packet, type, service); + continue; + } + if (!_mdns_name_is_selfhosted(name)) { + continue; + } + //detect collision (-1=won, 0=none, 1=lost) + int col = 0; + if (mdns_class > 1) { + col = 1; + } else if (!mdns_class) { + col = -1; + } else if (service) { // only detect txt collision if service existed + col = _mdns_check_txt_collision(service->service, data_ptr, data_len); + } + if (col && !_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running && service) { + do_not_reply = true; + _mdns_init_pcb_probe(packet->tcpip_if, packet->ip_protocol, &service, 1, true); + } else if (ttl > (MDNS_ANSWER_TXT_TTL / 2) && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions && !_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) { + _mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, service); + } + } + + } +#ifdef CONFIG_LWIP_IPV6 + else if (type == MDNS_TYPE_AAAA) {//ipv6 + esp_ip_addr_t ip6; + ip6.type = ESP_IPADDR_TYPE_V6; + memcpy(ip6.u_addr.ip6.addr, data_ptr, MDNS_ANSWER_AAAA_SIZE); + if (browse_result) { + _mdns_browse_result_add_ip(browse_result, name->host, &ip6, packet->tcpip_if, packet->ip_protocol, ttl, out_sync_browse); + } + if (search_result) { + //check for more applicable searches (PTR & A/AAAA at the same time) + while (search_result) { + _mdns_search_result_add_ip(search_result, name->host, &ip6, packet->tcpip_if, packet->ip_protocol, ttl); + search_result = _mdns_search_find_from(search_result->next, name, type, packet->tcpip_if, packet->ip_protocol); + } + } else if (ours) { + if (parsed_packet->questions && !parsed_packet->probe) { + _mdns_remove_parsed_question(parsed_packet, type, NULL); + continue; + } + if (!_mdns_name_is_selfhosted(name)) { + continue; + } + //detect collision (-1=won, 0=none, 1=lost) + int col = 0; + if (mdns_class > 1) { + col = 1; + } else if (!mdns_class) { + col = -1; + } else { + col = _mdns_check_aaaa_collision(&(ip6.u_addr.ip6), packet->tcpip_if); + } + if (col == 2) { + goto clear_rx_packet; + } else if (col == 1) { + do_not_reply = true; + if (_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) { + if (col && (parsed_packet->probe || parsed_packet->authoritative)) { + _mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].failed_probes++; + char *new_host = _mdns_mangle_name((char *)_mdns_server->hostname); + if (new_host) { + _mdns_remap_self_service_hostname(_mdns_server->hostname, new_host); + free((char *)_mdns_server->hostname); + _mdns_server->hostname = new_host; + _mdns_self_host.hostname = new_host; + } + _mdns_restart_all_pcbs(); + } + } else { + _mdns_init_pcb_probe(packet->tcpip_if, packet->ip_protocol, NULL, 0, true); + } + } else if (ttl > 60 && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions && !_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) { + _mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, NULL); + } + } + + } +#endif /* CONFIG_LWIP_IPV6 */ +#ifdef CONFIG_LWIP_IPV4 + else if (type == MDNS_TYPE_A) { + esp_ip_addr_t ip; + ip.type = ESP_IPADDR_TYPE_V4; + memcpy(&(ip.u_addr.ip4.addr), data_ptr, 4); + if (browse_result) { + _mdns_browse_result_add_ip(browse_result, name->host, &ip, packet->tcpip_if, packet->ip_protocol, ttl, out_sync_browse); + } + if (search_result) { + //check for more applicable searches (PTR & A/AAAA at the same time) + while (search_result) { + _mdns_search_result_add_ip(search_result, name->host, &ip, packet->tcpip_if, packet->ip_protocol, ttl); + search_result = _mdns_search_find_from(search_result->next, name, type, packet->tcpip_if, packet->ip_protocol); + } + } else if (ours) { + if (parsed_packet->questions && !parsed_packet->probe) { + _mdns_remove_parsed_question(parsed_packet, type, NULL); + continue; + } + if (!_mdns_name_is_selfhosted(name)) { + continue; + } + //detect collision (-1=won, 0=none, 1=lost) + int col = 0; + if (mdns_class > 1) { + col = 1; + } else if (!mdns_class) { + col = -1; + } else { + col = _mdns_check_a_collision(&(ip.u_addr.ip4), packet->tcpip_if); + } + if (col == 2) { + goto clear_rx_packet; + } else if (col == 1) { + do_not_reply = true; + if (_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) { + if (col && (parsed_packet->probe || parsed_packet->authoritative)) { + _mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].failed_probes++; + char *new_host = _mdns_mangle_name((char *)_mdns_server->hostname); + if (new_host) { + _mdns_remap_self_service_hostname(_mdns_server->hostname, new_host); + free((char *)_mdns_server->hostname); + _mdns_server->hostname = new_host; + _mdns_self_host.hostname = new_host; + } + _mdns_restart_all_pcbs(); + } + } else { + _mdns_init_pcb_probe(packet->tcpip_if, packet->ip_protocol, NULL, 0, true); + } + } else if (ttl > 60 && !col && !parsed_packet->authoritative && !parsed_packet->probe && !parsed_packet->questions && !_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running) { + _mdns_remove_scheduled_answer(packet->tcpip_if, packet->ip_protocol, type, NULL); + } + } + + } +#endif /* CONFIG_LWIP_IPV4 */ + } + //end while + if (parsed_packet->authoritative) { + _mdns_search_finish_done(); + } + } + + if (!do_not_reply && _mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].state > PCB_PROBE_3 && (parsed_packet->questions || parsed_packet->discovery)) { + _mdns_create_answer_from_parsed_packet(parsed_packet); + } + if (out_sync_browse) { +#ifdef MDNS_ENABLE_DEBUG + _mdns_dbg_printf("Browse %s%s total result:", out_sync_browse->browse->service, out_sync_browse->browse->proto); + debug_printf_browse_result_all(out_sync_browse->browse->result); +#endif // MDNS_ENABLE_DEBUG + if (out_sync_browse->sync_result) { +#ifdef MDNS_ENABLE_DEBUG + _mdns_dbg_printf("Changed result:"); + debug_printf_browse_result_all(out_sync_browse->sync_result->result); +#endif // MDNS_ENABLE_DEBUG + _mdns_sync_browse_action(ACTION_BROWSE_SYNC, out_sync_browse); + } else { + free(out_sync_browse); + } + } + +clear_rx_packet: + while (parsed_packet->questions) { + mdns_parsed_question_t *question = parsed_packet->questions; + parsed_packet->questions = parsed_packet->questions->next; + if (question->host) { + free(question->host); + } + if (question->service) { + free(question->service); + } + if (question->proto) { + free(question->proto); + } + if (question->domain) { + free(question->domain); + } + free(question); + } + while (parsed_packet->records) { + mdns_parsed_record_t *record = parsed_packet->records; + parsed_packet->records = parsed_packet->records->next; + if (record->host) { + free(record->host); + } + if (record->service) { + free(record->service); + } + if (record->proto) { + free(record->proto); + } + record->next = NULL; + free(record); + } + free(parsed_packet); + if (browse_result_instance) { + free(browse_result_instance); + } + if (browse_result_service) { + free(browse_result_service); + } + if (browse_result_proto) { + free(browse_result_proto); + } +} + +/** + * @brief Enable mDNS interface + */ +void _mdns_enable_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + if (!mdns_is_netif_ready(tcpip_if, ip_protocol)) { + if (_mdns_pcb_init(tcpip_if, ip_protocol)) { + _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].failed_probes = 0; + return; + } + } + _mdns_restart_pcb(tcpip_if, ip_protocol); +} + +/** + * @brief Disable mDNS interface + */ +void _mdns_disable_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + _mdns_clean_netif_ptr(tcpip_if); + + if (mdns_is_netif_ready(tcpip_if, ip_protocol)) { + _mdns_clear_pcb_tx_queue_head(tcpip_if, ip_protocol); + mdns_pcb_deinit_local(tcpip_if, ip_protocol); + mdns_if_t other_if = _mdns_get_other_if (tcpip_if); + if (other_if != MDNS_MAX_INTERFACES && _mdns_server->interfaces[other_if].pcbs[ip_protocol].state == PCB_DUP) { + _mdns_server->interfaces[other_if].pcbs[ip_protocol].state = PCB_OFF; + _mdns_enable_pcb(other_if, ip_protocol); + } + } + _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].state = PCB_OFF; +} + +#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES +static inline char nibble_to_hex(int var) +{ + return var > 9 ? var - 10 + 'a' : var + '0'; +} +#endif + +/** + * @brief Performs interface changes based on system events or custom commands + */ +static void perform_event_action(mdns_if_t mdns_if, mdns_event_actions_t action) +{ + if (!_mdns_server || mdns_if >= MDNS_MAX_INTERFACES) { + return; + } + if (action & MDNS_EVENT_ENABLE_IP4) { + _mdns_enable_pcb(mdns_if, MDNS_IP_PROTOCOL_V4); + } + if (action & MDNS_EVENT_ENABLE_IP6) { + _mdns_enable_pcb(mdns_if, MDNS_IP_PROTOCOL_V6); + } + if (action & MDNS_EVENT_DISABLE_IP4) { + _mdns_disable_pcb(mdns_if, MDNS_IP_PROTOCOL_V4); + } + if (action & MDNS_EVENT_DISABLE_IP6) { + _mdns_disable_pcb(mdns_if, MDNS_IP_PROTOCOL_V6); + } + if (action & MDNS_EVENT_ANNOUNCE_IP4) { + _mdns_announce_pcb(mdns_if, MDNS_IP_PROTOCOL_V4, NULL, 0, true); + } + if (action & MDNS_EVENT_ANNOUNCE_IP6) { + _mdns_announce_pcb(mdns_if, MDNS_IP_PROTOCOL_V6, NULL, 0, true); + } + +#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES +#ifdef CONFIG_LWIP_IPV4 + if (action & MDNS_EVENT_IP4_REVERSE_LOOKUP) { + esp_netif_ip_info_t if_ip_info; + if (esp_netif_get_ip_info(_mdns_get_esp_netif(mdns_if), &if_ip_info) == ESP_OK) { + esp_ip4_addr_t *ip = &if_ip_info.ip; + char *reverse_query_name = NULL; + if (asprintf(&reverse_query_name, "%d.%d.%d.%d.in-addr", + esp_ip4_addr4_16(ip), esp_ip4_addr3_16(ip), + esp_ip4_addr2_16(ip), esp_ip4_addr1_16(ip)) > 0 && reverse_query_name) { + ESP_LOGD(TAG, "Registered reverse query: %s.arpa", reverse_query_name); + _mdns_delegate_hostname_add(reverse_query_name, NULL); + } + } + } +#endif /* CONFIG_LWIP_IPV4 */ +#ifdef CONFIG_LWIP_IPV6 + if (action & MDNS_EVENT_IP6_REVERSE_LOOKUP) { + esp_ip6_addr_t addr6; + if (!esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(mdns_if), &addr6) && !_ipv6_address_is_zero(addr6)) { + uint8_t *paddr = (uint8_t *)&addr6.addr; + const char sub[] = "ip6"; + const size_t query_name_size = 4 * sizeof(addr6.addr) /* (2 nibbles + 2 dots)/per byte of IP address */ + sizeof(sub); + char *reverse_query_name = malloc(query_name_size); + if (reverse_query_name) { + char *ptr = &reverse_query_name[query_name_size]; // point to the end + memcpy(ptr - sizeof(sub), sub, sizeof(sub)); // copy the IP sub-domain + ptr -= sizeof(sub) + 1; // move before the sub-domain + while (reverse_query_name < ptr) { // continue populating reverse query from the end + *ptr-- = '.'; // nibble by nibble, until we reach the beginning + *ptr-- = nibble_to_hex(((*paddr) >> 4) & 0x0F); + *ptr-- = '.'; + *ptr-- = nibble_to_hex((*paddr) & 0x0F); + paddr++; + } + ESP_LOGD(TAG, "Registered reverse query: %s.arpa", reverse_query_name); + _mdns_delegate_hostname_add(reverse_query_name, NULL); + } + } + } +#endif /* CONFIG_LWIP_IPV6 */ +#endif /* CONFIG_MDNS_RESPOND_REVERSE_QUERIES */ +} + +/** + * @brief Dispatch interface changes based on system events + */ +static inline void post_mdns_disable_pcb(mdns_predef_if_t preset_if, mdns_ip_protocol_t protocol) +{ + mdns_post_custom_action_tcpip_if(mdns_if_from_preset_if(preset_if), protocol == MDNS_IP_PROTOCOL_V4 ? MDNS_EVENT_DISABLE_IP4 : MDNS_EVENT_DISABLE_IP6); +} + +static inline void post_mdns_enable_pcb(mdns_predef_if_t preset_if, mdns_ip_protocol_t protocol) +{ + mdns_post_custom_action_tcpip_if(mdns_if_from_preset_if(preset_if), protocol == MDNS_IP_PROTOCOL_V4 ? MDNS_EVENT_ENABLE_IP4 : MDNS_EVENT_ENABLE_IP6); +} + +static inline void post_mdns_announce_pcb(mdns_predef_if_t preset_if, mdns_ip_protocol_t protocol) +{ + mdns_post_custom_action_tcpip_if(mdns_if_from_preset_if(preset_if), protocol == MDNS_IP_PROTOCOL_V4 ? MDNS_EVENT_ANNOUNCE_IP4 : MDNS_EVENT_ANNOUNCE_IP6); +} + +#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH +void mdns_preset_if_handle_system_event(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (!_mdns_server) { + return; + } + + esp_netif_dhcp_status_t dcst; +#if MDNS_ESP_WIFI_ENABLED && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP) + if (event_base == WIFI_EVENT) { + switch (event_id) { + case WIFI_EVENT_STA_CONNECTED: + if (!esp_netif_dhcpc_get_status(esp_netif_from_preset_if(MDNS_IF_STA), &dcst)) { + if (dcst == ESP_NETIF_DHCP_STOPPED) { + post_mdns_enable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V4); + } + } + break; + case WIFI_EVENT_STA_DISCONNECTED: + post_mdns_disable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V4); + post_mdns_disable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V6); + break; + case WIFI_EVENT_AP_START: + post_mdns_enable_pcb(MDNS_IF_AP, MDNS_IP_PROTOCOL_V4); + break; + case WIFI_EVENT_AP_STOP: + post_mdns_disable_pcb(MDNS_IF_AP, MDNS_IP_PROTOCOL_V4); + post_mdns_disable_pcb(MDNS_IF_AP, MDNS_IP_PROTOCOL_V6); + break; + default: + break; + } + } else +#endif +#if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH + if (event_base == ETH_EVENT) { + switch (event_id) { + case ETHERNET_EVENT_CONNECTED: + if (!esp_netif_dhcpc_get_status(esp_netif_from_preset_if(MDNS_IF_ETH), &dcst)) { + if (dcst == ESP_NETIF_DHCP_STOPPED) { + post_mdns_enable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4); + } + } + break; + case ETHERNET_EVENT_DISCONNECTED: + post_mdns_disable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4); + post_mdns_disable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V6); + break; + default: + break; + } + } else +#endif + if (event_base == IP_EVENT) { + switch (event_id) { + case IP_EVENT_STA_GOT_IP: + post_mdns_enable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V4); + post_mdns_announce_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V6); + break; +#if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH + case IP_EVENT_ETH_GOT_IP: + post_mdns_enable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4); + break; +#endif + case IP_EVENT_GOT_IP6: { + ip_event_got_ip6_t *event = (ip_event_got_ip6_t *) event_data; + mdns_if_t mdns_if = _mdns_get_if_from_esp_netif(event->esp_netif); + if (mdns_if >= MDNS_MAX_INTERFACES) { + return; + } + post_mdns_enable_pcb(mdns_if, MDNS_IP_PROTOCOL_V6); + post_mdns_announce_pcb(mdns_if, MDNS_IP_PROTOCOL_V4); + mdns_browse_t *browse = _mdns_server->browse; + while (browse) { + _mdns_browse_send(browse, mdns_if); + browse = browse->next; + } + } + break; + default: + break; + } + } +} +#endif /* CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH */ + +/* + * MDNS Search + * */ + +/** + * @brief Free search structure (except the results) + */ +static void _mdns_search_free(mdns_search_once_t *search) +{ + free(search->instance); + free(search->service); + free(search->proto); + vSemaphoreDelete(search->done_semaphore); + free(search); +} + +/** + * @brief Allocate new search structure + */ +static mdns_search_once_t *_mdns_search_init(const char *name, const char *service, const char *proto, uint16_t type, bool unicast, + uint32_t timeout, uint8_t max_results, mdns_query_notify_t notifier) +{ + mdns_search_once_t *search = (mdns_search_once_t *)malloc(sizeof(mdns_search_once_t)); + if (!search) { + HOOK_MALLOC_FAILED; + return NULL; + } + memset(search, 0, sizeof(mdns_search_once_t)); + + search->done_semaphore = xSemaphoreCreateBinary(); + if (!search->done_semaphore) { + free(search); + return NULL; + } + + if (!_str_null_or_empty(name)) { + search->instance = strndup(name, MDNS_NAME_BUF_LEN - 1); + if (!search->instance) { + _mdns_search_free(search); + return NULL; + } + } + + if (!_str_null_or_empty(service)) { + search->service = strndup(service, MDNS_NAME_BUF_LEN - 1); + if (!search->service) { + _mdns_search_free(search); + return NULL; + } + } + + if (!_str_null_or_empty(proto)) { + search->proto = strndup(proto, MDNS_NAME_BUF_LEN - 1); + if (!search->proto) { + _mdns_search_free(search); + return NULL; + } + } + + search->type = type; + search->unicast = unicast; + search->timeout = timeout; + search->num_results = 0; + search->max_results = max_results; + search->result = NULL; + search->state = SEARCH_INIT; + search->sent_at = 0; + search->started_at = xTaskGetTickCount() * portTICK_PERIOD_MS; + search->notifier = notifier; + search->next = NULL; + + return search; +} + +/** + * @brief Mark search as finished and remove it from search chain + */ +static void _mdns_search_finish(mdns_search_once_t *search) +{ + search->state = SEARCH_OFF; + queueDetach(mdns_search_once_t, _mdns_server->search_once, search); + if (search->notifier) { + search->notifier(search); + } + xSemaphoreGive(search->done_semaphore); +} + +/** + * @brief Add new search to the search chain + */ +static void _mdns_search_add(mdns_search_once_t *search) +{ + search->next = _mdns_server->search_once; + _mdns_server->search_once = search; +} + +/** + * @brief Called from parser to finish any searches that have reached maximum results + */ +static void _mdns_search_finish_done(void) +{ + mdns_search_once_t *search = _mdns_server->search_once; + mdns_search_once_t *s = NULL; + while (search) { + s = search; + search = search->next; + if (s->max_results && s->num_results >= s->max_results) { + _mdns_search_finish(s); + } + } +} + +/** + * @brief Create linked IP (copy) from parsed one + */ +static mdns_ip_addr_t *_mdns_result_addr_create_ip(esp_ip_addr_t *ip) +{ + mdns_ip_addr_t *a = (mdns_ip_addr_t *)malloc(sizeof(mdns_ip_addr_t)); + if (!a) { + HOOK_MALLOC_FAILED; + return NULL; + } + memset(a, 0, sizeof(mdns_ip_addr_t)); + a->addr.type = ip->type; + if (ip->type == ESP_IPADDR_TYPE_V6) { + memcpy(a->addr.u_addr.ip6.addr, ip->u_addr.ip6.addr, 16); + } else { + a->addr.u_addr.ip4.addr = ip->u_addr.ip4.addr; + } + return a; +} + +static inline void _mdns_result_update_ttl(mdns_result_t *r, uint32_t ttl) +{ + r->ttl = r->ttl < ttl ? r->ttl : ttl; +} + +/** + * @brief Chain new IP to search result + */ +static void _mdns_result_add_ip(mdns_result_t *r, esp_ip_addr_t *ip) +{ + mdns_ip_addr_t *a = r->addr; + while (a) { + if (a->addr.type == ip->type) { +#ifdef CONFIG_LWIP_IPV4 + if (a->addr.type == ESP_IPADDR_TYPE_V4 && a->addr.u_addr.ip4.addr == ip->u_addr.ip4.addr) { + return; + } +#endif +#ifdef CONFIG_LWIP_IPV6 + if (a->addr.type == ESP_IPADDR_TYPE_V6 && !memcmp(a->addr.u_addr.ip6.addr, ip->u_addr.ip6.addr, 16)) { + return; + } +#endif + } + a = a->next; + } + a = _mdns_result_addr_create_ip(ip); + if (!a) { + return; + } + a->next = r->addr; + r->addr = a; +} + +/** + * @brief Called from parser to add A/AAAA data to search result + */ +static void _mdns_search_result_add_ip(mdns_search_once_t *search, const char *hostname, esp_ip_addr_t *ip, + mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl) +{ + mdns_result_t *r = NULL; + mdns_ip_addr_t *a = NULL; + + if ((search->type == MDNS_TYPE_A && ip->type == ESP_IPADDR_TYPE_V4) + || (search->type == MDNS_TYPE_AAAA && ip->type == ESP_IPADDR_TYPE_V6) + || search->type == MDNS_TYPE_ANY) { + r = search->result; + while (r) { + if (r->esp_netif == _mdns_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol) { + _mdns_result_add_ip(r, ip); + _mdns_result_update_ttl(r, ttl); + return; + } + r = r->next; + } + if (!search->max_results || search->num_results < search->max_results) { + r = (mdns_result_t *)malloc(sizeof(mdns_result_t)); + if (!r) { + HOOK_MALLOC_FAILED; + return; + } + + memset(r, 0, sizeof(mdns_result_t)); + + a = _mdns_result_addr_create_ip(ip); + if (!a) { + free(r); + return; + } + a->next = r->addr; + r->hostname = strdup(hostname); + r->addr = a; + r->esp_netif = _mdns_get_esp_netif(tcpip_if); + r->ip_protocol = ip_protocol; + r->next = search->result; + r->ttl = ttl; + search->result = r; + search->num_results++; + } + } else if (search->type == MDNS_TYPE_PTR || search->type == MDNS_TYPE_SRV) { + r = search->result; + while (r) { + if (r->esp_netif == _mdns_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !_str_null_or_empty(r->hostname) && !strcasecmp(hostname, r->hostname)) { + _mdns_result_add_ip(r, ip); + _mdns_result_update_ttl(r, ttl); + break; + } + r = r->next; + } + } +} + +/** + * @brief Called from parser to add PTR data to search result + */ +static mdns_result_t *_mdns_search_result_add_ptr(mdns_search_once_t *search, const char *instance, + const char *service_type, const char *proto, mdns_if_t tcpip_if, + mdns_ip_protocol_t ip_protocol, uint32_t ttl) +{ + mdns_result_t *r = search->result; + while (r) { + if (r->esp_netif == _mdns_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !_str_null_or_empty(r->instance_name) && !strcasecmp(instance, r->instance_name)) { + _mdns_result_update_ttl(r, ttl); + return r; + } + r = r->next; + } + if (!search->max_results || search->num_results < search->max_results) { + r = (mdns_result_t *)malloc(sizeof(mdns_result_t)); + if (!r) { + HOOK_MALLOC_FAILED; + return NULL; + } + + memset(r, 0, sizeof(mdns_result_t)); + r->instance_name = strdup(instance); + r->service_type = strdup(service_type); + r->proto = strdup(proto); + if (!r->instance_name) { + free(r); + return NULL; + } + + r->esp_netif = _mdns_get_esp_netif(tcpip_if); + r->ip_protocol = ip_protocol; + r->ttl = ttl; + r->next = search->result; + search->result = r; + search->num_results++; + return r; + } + return NULL; +} + +/** + * @brief Called from parser to add SRV data to search result + */ +static void _mdns_search_result_add_srv(mdns_search_once_t *search, const char *hostname, uint16_t port, + mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl) +{ + mdns_result_t *r = search->result; + while (r) { + if (r->esp_netif == _mdns_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !_str_null_or_empty(r->hostname) && !strcasecmp(hostname, r->hostname)) { + _mdns_result_update_ttl(r, ttl); + return; + } + r = r->next; + } + if (!search->max_results || search->num_results < search->max_results) { + r = (mdns_result_t *)malloc(sizeof(mdns_result_t)); + if (!r) { + HOOK_MALLOC_FAILED; + return; + } + + memset(r, 0, sizeof(mdns_result_t)); + r->hostname = strdup(hostname); + if (!r->hostname) { + free(r); + return; + } + if (search->instance) { + r->instance_name = strdup(search->instance); + } + r->service_type = strdup(search->service); + r->proto = strdup(search->proto); + r->port = port; + r->esp_netif = _mdns_get_esp_netif(tcpip_if); + r->ip_protocol = ip_protocol; + r->ttl = ttl; + r->next = search->result; + search->result = r; + search->num_results++; + } +} + +/** + * @brief Called from parser to add TXT data to search result + */ +static void _mdns_search_result_add_txt(mdns_search_once_t *search, mdns_txt_item_t *txt, uint8_t *txt_value_len, + size_t txt_count, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, + uint32_t ttl) +{ + mdns_result_t *r = search->result; + while (r) { + if (r->esp_netif == _mdns_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol) { + if (r->txt) { + goto free_txt; + } + r->txt = txt; + r->txt_value_len = txt_value_len; + r->txt_count = txt_count; + _mdns_result_update_ttl(r, ttl); + return; + } + r = r->next; + } + if (!search->max_results || search->num_results < search->max_results) { + r = (mdns_result_t *)malloc(sizeof(mdns_result_t)); + if (!r) { + HOOK_MALLOC_FAILED; + goto free_txt; + } + + memset(r, 0, sizeof(mdns_result_t)); + r->txt = txt; + r->txt_value_len = txt_value_len; + r->txt_count = txt_count; + r->esp_netif = _mdns_get_esp_netif(tcpip_if); + r->ip_protocol = ip_protocol; + r->ttl = ttl; + r->next = search->result; + search->result = r; + search->num_results++; + } + return; + +free_txt: + for (size_t i = 0; i < txt_count; i++) { + free((char *)(txt[i].key)); + free((char *)(txt[i].value)); + } + free(txt); + free(txt_value_len); +} + +/** + * @brief Called from packet parser to find matching running search + */ +static mdns_search_once_t *_mdns_search_find_from(mdns_search_once_t *s, mdns_name_t *name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + mdns_result_t *r = NULL; + while (s) { + if (s->state == SEARCH_OFF) { + s = s->next; + continue; + } + + if (type == MDNS_TYPE_A || type == MDNS_TYPE_AAAA) { + if ((s->type == MDNS_TYPE_ANY && s->service != NULL) + || (s->type != MDNS_TYPE_ANY && s->type != type && s->type != MDNS_TYPE_PTR && s->type != MDNS_TYPE_SRV)) { + s = s->next; + continue; + } + if (s->type != MDNS_TYPE_PTR && s->type != MDNS_TYPE_SRV) { + if (!strcasecmp(name->host, s->instance)) { + return s; + } + s = s->next; + continue; + } + r = s->result; + while (r) { + if (r->esp_netif == _mdns_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !_str_null_or_empty(r->hostname) && !strcasecmp(name->host, r->hostname)) { + return s; + } + r = r->next; + } + s = s->next; + continue; + } + + if (type == MDNS_TYPE_SRV || type == MDNS_TYPE_TXT) { + if ((s->type == MDNS_TYPE_ANY && s->service == NULL) + || (s->type != MDNS_TYPE_ANY && s->type != type && s->type != MDNS_TYPE_PTR)) { + s = s->next; + continue; + } + if (strcasecmp(name->service, s->service) + || strcasecmp(name->proto, s->proto)) { + s = s->next; + continue; + } + if (s->type != MDNS_TYPE_PTR) { + if (s->instance && strcasecmp(name->host, s->instance) == 0) { + return s; + } + s = s->next; + continue; + } + return s; + } + + if (type == MDNS_TYPE_PTR && type == s->type && !strcasecmp(name->service, s->service) && !strcasecmp(name->proto, s->proto)) { + return s; + } + + s = s->next; + } + + return NULL; +} + +/** + * @brief Create search packet for particular interface + */ +static mdns_tx_packet_t *_mdns_create_search_packet(mdns_search_once_t *search, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + mdns_result_t *r = NULL; + mdns_tx_packet_t *packet = _mdns_alloc_packet_default(tcpip_if, ip_protocol); + if (!packet) { + return NULL; + } + + mdns_out_question_t *q = (mdns_out_question_t *)malloc(sizeof(mdns_out_question_t)); + if (!q) { + HOOK_MALLOC_FAILED; + _mdns_free_tx_packet(packet); + return NULL; + } + q->next = NULL; + q->unicast = search->unicast; + q->type = search->type; + q->host = search->instance; + q->service = search->service; + q->proto = search->proto; + q->domain = MDNS_DEFAULT_DOMAIN; + q->own_dynamic_memory = false; + queueToEnd(mdns_out_question_t, packet->questions, q); + + if (search->type == MDNS_TYPE_PTR) { + r = search->result; + while (r) { + //full record on the same interface is available + if (r->esp_netif != _mdns_get_esp_netif(tcpip_if) || r->ip_protocol != ip_protocol || r->instance_name == NULL || r->hostname == NULL || r->addr == NULL) { + r = r->next; + continue; + } + mdns_out_answer_t *a = (mdns_out_answer_t *)malloc(sizeof(mdns_out_answer_t)); + if (!a) { + HOOK_MALLOC_FAILED; + _mdns_free_tx_packet(packet); + return NULL; + } + a->type = MDNS_TYPE_PTR; + a->service = NULL; + a->custom_instance = r->instance_name; + a->custom_service = search->service; + a->custom_proto = search->proto; + a->bye = false; + a->flush = false; + a->next = NULL; + queueToEnd(mdns_out_answer_t, packet->answers, a); + r = r->next; + } + } + + return packet; +} + +/** + * @brief Send search packet to particular interface + */ +static void _mdns_search_send_pcb(mdns_search_once_t *search, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + mdns_tx_packet_t *packet = NULL; + if (mdns_is_netif_ready(tcpip_if, ip_protocol) && _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].state > PCB_INIT) { + packet = _mdns_create_search_packet(search, tcpip_if, ip_protocol); + if (!packet) { + return; + } + _mdns_dispatch_tx_packet(packet); + _mdns_free_tx_packet(packet); + } +} + +/** + * @brief Send search packet to all available interfaces + */ +static void _mdns_search_send(mdns_search_once_t *search) +{ + mdns_search_once_t *queue = _mdns_server->search_once; + bool found = false; + // looking for this search in active searches + while (queue) { + if (queue == search) { + found = true; + break; + } + queue = queue->next; + } + + if (!found) { + // no longer active -> skip sending this search + return; + } + + uint8_t i, j; + for (i = 0; i < MDNS_MAX_INTERFACES; i++) { + for (j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) { + _mdns_search_send_pcb(search, (mdns_if_t)i, (mdns_ip_protocol_t)j); + } + } +} + +static void _mdns_tx_handle_packet(mdns_tx_packet_t *p) +{ + mdns_tx_packet_t *a = NULL; + mdns_out_question_t *q = NULL; + mdns_pcb_t *pcb = &_mdns_server->interfaces[p->tcpip_if].pcbs[p->ip_protocol]; + uint32_t send_after = 1000; + + if (pcb->state == PCB_OFF) { + _mdns_free_tx_packet(p); + return; + } + _mdns_dispatch_tx_packet(p); + + switch (pcb->state) { + case PCB_PROBE_1: + q = p->questions; + while (q) { + q->unicast = false; + q = q->next; + } + //fallthrough + case PCB_PROBE_2: + _mdns_schedule_tx_packet(p, 250); + pcb->state = (mdns_pcb_state_t)((uint8_t)(pcb->state) + 1); + break; + case PCB_PROBE_3: + a = _mdns_create_announce_from_probe(p); + if (!a) { + _mdns_schedule_tx_packet(p, 250); + break; + } + pcb->probe_running = false; + pcb->probe_ip = false; + pcb->probe_services_len = 0; + pcb->failed_probes = 0; + free(pcb->probe_services); + pcb->probe_services = NULL; + _mdns_free_tx_packet(p); + p = a; + send_after = 250; + //fallthrough + case PCB_ANNOUNCE_1: + //fallthrough + case PCB_ANNOUNCE_2: + _mdns_schedule_tx_packet(p, send_after); + pcb->state = (mdns_pcb_state_t)((uint8_t)(pcb->state) + 1); + break; + case PCB_ANNOUNCE_3: + pcb->state = PCB_RUNNING; + _mdns_free_tx_packet(p); + break; + default: + _mdns_free_tx_packet(p); + break; + } +} + +static void _mdns_remap_self_service_hostname(const char *old_hostname, const char *new_hostname) +{ + mdns_srv_item_t *service = _mdns_server->services; + + while (service) { + if (service->service->hostname && + strcmp(service->service->hostname, old_hostname) == 0) { + free((char *)service->service->hostname); + service->service->hostname = strdup(new_hostname); + } + service = service->next; + } +} + +static void _mdns_sync_browse_result_link_free(mdns_browse_sync_t *browse_sync) +{ + mdns_browse_result_sync_t *current = browse_sync->sync_result; + mdns_browse_result_sync_t *need_free; + while (current) { + need_free = current; + current = current->next; + free(need_free); + } + free(browse_sync); +} + +/** + * @brief Free action data + */ +static void _mdns_free_action(mdns_action_t *action) +{ + switch (action->type) { + case ACTION_HOSTNAME_SET: + free(action->data.hostname_set.hostname); + break; + case ACTION_INSTANCE_SET: + free(action->data.instance); + break; + case ACTION_SEARCH_ADD: + //fallthrough + case ACTION_SEARCH_SEND: + //fallthrough + case ACTION_SEARCH_END: + _mdns_search_free(action->data.search_add.search); + break; + case ACTION_BROWSE_ADD: + //fallthrough + case ACTION_BROWSE_END: + _mdns_browse_item_free(action->data.browse_add.browse); + break; + case ACTION_BROWSE_SYNC: + _mdns_sync_browse_result_link_free(action->data.browse_sync.browse_sync); + break; + case ACTION_TX_HANDLE: + _mdns_free_tx_packet(action->data.tx_handle.packet); + break; + case ACTION_RX_HANDLE: + _mdns_packet_free(action->data.rx_handle.packet); + break; + case ACTION_DELEGATE_HOSTNAME_SET_ADDR: + case ACTION_DELEGATE_HOSTNAME_ADD: + free((char *)action->data.delegate_hostname.hostname); + free_address_list(action->data.delegate_hostname.address_list); + break; + case ACTION_DELEGATE_HOSTNAME_REMOVE: + free((char *)action->data.delegate_hostname.hostname); + break; + default: + break; + } + free(action); +} + +/** + * @brief Called from service thread to execute given action + */ +static void _mdns_execute_action(mdns_action_t *action) +{ + switch (action->type) { + case ACTION_SYSTEM_EVENT: + perform_event_action(action->data.sys_event.interface, action->data.sys_event.event_action); + break; + case ACTION_HOSTNAME_SET: + _mdns_send_bye_all_pcbs_no_instance(true); + _mdns_remap_self_service_hostname(_mdns_server->hostname, action->data.hostname_set.hostname); + free((char *)_mdns_server->hostname); + _mdns_server->hostname = action->data.hostname_set.hostname; + _mdns_self_host.hostname = action->data.hostname_set.hostname; + _mdns_restart_all_pcbs(); + xSemaphoreGive(_mdns_server->action_sema); + break; + case ACTION_INSTANCE_SET: + _mdns_send_bye_all_pcbs_no_instance(false); + free((char *)_mdns_server->instance); + _mdns_server->instance = action->data.instance; + _mdns_restart_all_pcbs_no_instance(); + + break; + case ACTION_SEARCH_ADD: + _mdns_search_add(action->data.search_add.search); + break; + case ACTION_SEARCH_SEND: + _mdns_search_send(action->data.search_add.search); + break; + case ACTION_SEARCH_END: + _mdns_search_finish(action->data.search_add.search); + break; + + case ACTION_BROWSE_ADD: + _mdns_browse_add(action->data.browse_add.browse); + break; + case ACTION_BROWSE_SYNC: + _mdns_browse_sync(action->data.browse_sync.browse_sync); + _mdns_sync_browse_result_link_free(action->data.browse_sync.browse_sync); + break; + case ACTION_BROWSE_END: + _mdns_browse_finish(action->data.browse_add.browse); + break; + + case ACTION_TX_HANDLE: { + mdns_tx_packet_t *p = _mdns_server->tx_queue_head; + // packet to be handled should be at tx head, but must be consistent with the one pushed to action queue + if (p && p == action->data.tx_handle.packet && p->queued) { + p->queued = false; // clearing, as the packet might be reused (pushed and transmitted again) + _mdns_server->tx_queue_head = p->next; + _mdns_tx_handle_packet(p); + } else { + ESP_LOGD(TAG, "Skipping transmit of an unexpected packet!"); + } + } + break; + case ACTION_RX_HANDLE: + mdns_parse_packet(action->data.rx_handle.packet); + _mdns_packet_free(action->data.rx_handle.packet); + break; + case ACTION_DELEGATE_HOSTNAME_ADD: + if (!_mdns_delegate_hostname_add(action->data.delegate_hostname.hostname, + action->data.delegate_hostname.address_list)) { + free((char *)action->data.delegate_hostname.hostname); + free_address_list(action->data.delegate_hostname.address_list); + } + xSemaphoreGive(_mdns_server->action_sema); + break; + case ACTION_DELEGATE_HOSTNAME_SET_ADDR: + if (!_mdns_delegate_hostname_set_address(action->data.delegate_hostname.hostname, + action->data.delegate_hostname.address_list)) { + free_address_list(action->data.delegate_hostname.address_list); + } + free((char *)action->data.delegate_hostname.hostname); + break; + case ACTION_DELEGATE_HOSTNAME_REMOVE: + _mdns_delegate_hostname_remove(action->data.delegate_hostname.hostname); + free((char *)action->data.delegate_hostname.hostname); + break; + default: + break; + } + free(action); +} + +/** + * @brief Queue search action + */ +static esp_err_t _mdns_send_search_action(mdns_action_type_t type, mdns_search_once_t *search) +{ + mdns_action_t *action = NULL; + + action = (mdns_action_t *)malloc(sizeof(mdns_action_t)); + if (!action) { + HOOK_MALLOC_FAILED; + return ESP_ERR_NO_MEM; + } + + action->type = type; + action->data.search_add.search = search; + if (xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0) != pdPASS) { + free(action); + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +/** + * @brief Called from timer task to run mDNS responder + * + * periodically checks first unqueued packet (from tx head). + * if it is scheduled to be transmitted, then pushes the packet to action queue to be handled. + * + */ +static void _mdns_scheduler_run(void) +{ + MDNS_SERVICE_LOCK(); + mdns_tx_packet_t *p = _mdns_server->tx_queue_head; + mdns_action_t *action = NULL; + + // find first unqueued packet + while (p && p->queued) { + p = p->next; + } + if (!p) { + MDNS_SERVICE_UNLOCK(); + return; + } + while (p && (int32_t)(p->send_at - (xTaskGetTickCount() * portTICK_PERIOD_MS)) < 0) { + action = (mdns_action_t *)malloc(sizeof(mdns_action_t)); + if (action) { + action->type = ACTION_TX_HANDLE; + action->data.tx_handle.packet = p; + p->queued = true; + if (xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0) != pdPASS) { + free(action); + p->queued = false; + } + } else { + HOOK_MALLOC_FAILED; + break; + } + //Find the next unqued packet + p = p->next; + } + MDNS_SERVICE_UNLOCK(); +} + +/** + * @brief Called from timer task to run active searches + */ +static void _mdns_search_run(void) +{ + MDNS_SERVICE_LOCK(); + mdns_search_once_t *s = _mdns_server->search_once; + uint32_t now = xTaskGetTickCount() * portTICK_PERIOD_MS; + if (!s) { + MDNS_SERVICE_UNLOCK(); + return; + } + while (s) { + if (s->state != SEARCH_OFF) { + if (now > (s->started_at + s->timeout)) { + s->state = SEARCH_OFF; + if (_mdns_send_search_action(ACTION_SEARCH_END, s) != ESP_OK) { + s->state = SEARCH_RUNNING; + } + } else if (s->state == SEARCH_INIT || (now - s->sent_at) > 1000) { + s->state = SEARCH_RUNNING; + s->sent_at = now; + if (_mdns_send_search_action(ACTION_SEARCH_SEND, s) != ESP_OK) { + s->sent_at -= 1000; + } + } + } + s = s->next; + } + MDNS_SERVICE_UNLOCK(); +} + +/** + * @brief the main MDNS service task. Packets are received and parsed here + */ +static void _mdns_service_task(void *pvParameters) +{ + mdns_action_t *a = NULL; + for (;;) { + if (_mdns_server && _mdns_server->action_queue) { + if (xQueueReceive(_mdns_server->action_queue, &a, portMAX_DELAY) == pdTRUE) { + if (a && a->type == ACTION_TASK_STOP) { + break; + } + MDNS_SERVICE_LOCK(); + _mdns_execute_action(a); + MDNS_SERVICE_UNLOCK(); + } + } else { + vTaskDelay(500 * portTICK_PERIOD_MS); + } + } + _mdns_service_task_handle = NULL; + vTaskDelete(NULL); +} + +static void _mdns_timer_cb(void *arg) +{ + _mdns_scheduler_run(); + _mdns_search_run(); +} + +static esp_err_t _mdns_start_timer(void) +{ + esp_timer_create_args_t timer_conf = { + .callback = _mdns_timer_cb, + .arg = NULL, + .dispatch_method = ESP_TIMER_TASK, + .name = "mdns_timer" + }; + esp_err_t err = esp_timer_create(&timer_conf, &(_mdns_server->timer_handle)); + if (err) { + return err; + } + return esp_timer_start_periodic(_mdns_server->timer_handle, MDNS_TIMER_PERIOD_US); +} + +static esp_err_t _mdns_stop_timer(void) +{ + esp_err_t err = ESP_OK; + if (_mdns_server->timer_handle) { + err = esp_timer_stop(_mdns_server->timer_handle); + if (err) { + return err; + } + err = esp_timer_delete(_mdns_server->timer_handle); + } + return err; +} + +/** + * @brief Start the service thread if not running + * + * @return + * - ESP_OK on success + * - ESP_FAIL on error + */ +static esp_err_t _mdns_service_task_start(void) +{ + if (!_mdns_service_semaphore) { + _mdns_service_semaphore = xSemaphoreCreateMutex(); + if (!_mdns_service_semaphore) { + return ESP_FAIL; + } + } + MDNS_SERVICE_LOCK(); + if (_mdns_start_timer()) { + MDNS_SERVICE_UNLOCK(); + return ESP_FAIL; + } + if (!_mdns_service_task_handle) { + xTaskCreatePinnedToCore(_mdns_service_task, "mdns", MDNS_SERVICE_STACK_DEPTH, NULL, MDNS_TASK_PRIORITY, + (TaskHandle_t *const)(&_mdns_service_task_handle), MDNS_TASK_AFFINITY); + if (!_mdns_service_task_handle) { + _mdns_stop_timer(); + MDNS_SERVICE_UNLOCK(); + vSemaphoreDelete(_mdns_service_semaphore); + _mdns_service_semaphore = NULL; + return ESP_FAIL; + } + } + MDNS_SERVICE_UNLOCK(); + return ESP_OK; +} + +/** + * @brief Stop the service thread + * + * @return + * - ESP_OK + */ +static esp_err_t _mdns_service_task_stop(void) +{ + _mdns_stop_timer(); + if (_mdns_service_task_handle) { + mdns_action_t action; + mdns_action_t *a = &action; + action.type = ACTION_TASK_STOP; + if (xQueueSend(_mdns_server->action_queue, &a, (TickType_t)0) != pdPASS) { + vTaskDelete(_mdns_service_task_handle); + _mdns_service_task_handle = NULL; + } + while (_mdns_service_task_handle) { + vTaskDelay(10 / portTICK_PERIOD_MS); + } + } + vSemaphoreDelete(_mdns_service_semaphore); + _mdns_service_semaphore = NULL; + return ESP_OK; +} + +static esp_err_t mdns_post_custom_action_tcpip_if(mdns_if_t mdns_if, mdns_event_actions_t event_action) +{ + if (!_mdns_server || mdns_if >= MDNS_MAX_INTERFACES) { + return ESP_ERR_INVALID_STATE; + } + + mdns_action_t *action = (mdns_action_t *)calloc(1, sizeof(mdns_action_t)); + if (!action) { + HOOK_MALLOC_FAILED; + return ESP_ERR_NO_MEM; + } + action->type = ACTION_SYSTEM_EVENT; + action->data.sys_event.event_action = event_action; + action->data.sys_event.interface = mdns_if; + + if (xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0) != pdPASS) { + free(action); + } + return ESP_OK; +} + +static inline void set_default_duplicated_interfaces(void) +{ + mdns_if_t wifi_sta_if = MDNS_MAX_INTERFACES; + mdns_if_t eth_if = MDNS_MAX_INTERFACES; + for (mdns_if_t i = 0; i < MDNS_MAX_INTERFACES; i++) { + if (s_esp_netifs[i].predefined && s_esp_netifs[i].predef_if == MDNS_IF_STA) { + wifi_sta_if = i; + } + if (s_esp_netifs[i].predefined && s_esp_netifs[i].predef_if == MDNS_IF_ETH) { + eth_if = i; + } + } + if (wifi_sta_if != MDNS_MAX_INTERFACES && eth_if != MDNS_MAX_INTERFACES) { + s_esp_netifs[wifi_sta_if].duplicate = eth_if; + s_esp_netifs[eth_if].duplicate = wifi_sta_if; + } +} + +static inline void unregister_predefined_handlers(void) +{ +#if MDNS_ESP_WIFI_ENABLED && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP) + esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, mdns_preset_if_handle_system_event); +#endif +#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH + esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, mdns_preset_if_handle_system_event); +#endif +#if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH + esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, mdns_preset_if_handle_system_event); +#endif +} + +/* + * Public Methods + * */ + +esp_err_t mdns_netif_action(esp_netif_t *esp_netif, mdns_event_actions_t event_action) +{ + return mdns_post_custom_action_tcpip_if(_mdns_get_if_from_esp_netif(esp_netif), event_action); +} + +esp_err_t mdns_register_netif(esp_netif_t *esp_netif) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + + esp_err_t err = ESP_ERR_NO_MEM; + MDNS_SERVICE_LOCK(); + for (mdns_if_t i = 0; i < MDNS_MAX_INTERFACES; ++i) { + if (s_esp_netifs[i].netif == esp_netif) { + MDNS_SERVICE_UNLOCK(); + return ESP_ERR_INVALID_STATE; + } + } + + for (mdns_if_t i = 0; i < MDNS_MAX_INTERFACES; ++i) { + if (!s_esp_netifs[i].predefined && s_esp_netifs[i].netif == NULL) { + s_esp_netifs[i].netif = esp_netif; + err = ESP_OK; + break; + } + } + MDNS_SERVICE_UNLOCK(); + return err; +} + +esp_err_t mdns_unregister_netif(esp_netif_t *esp_netif) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + + esp_err_t err = ESP_ERR_NOT_FOUND; + MDNS_SERVICE_LOCK(); + for (mdns_if_t i = 0; i < MDNS_MAX_INTERFACES; ++i) { + if (!s_esp_netifs[i].predefined && s_esp_netifs[i].netif == esp_netif) { + s_esp_netifs[i].netif = NULL; + err = ESP_OK; + break; + } + } + MDNS_SERVICE_UNLOCK(); + return err; +} + + +esp_err_t mdns_init(void) +{ + esp_err_t err = ESP_OK; + + if (_mdns_server) { + return err; + } + + _mdns_server = (mdns_server_t *)malloc(sizeof(mdns_server_t)); + if (!_mdns_server) { + HOOK_MALLOC_FAILED; + return ESP_ERR_NO_MEM; + } + memset((uint8_t *)_mdns_server, 0, sizeof(mdns_server_t)); + // zero-out local copy of netifs to initiate a fresh search by interface key whenever a netif ptr is needed + for (mdns_if_t i = 0; i < MDNS_MAX_INTERFACES; ++i) { + s_esp_netifs[i].netif = NULL; + } + + _mdns_server->action_queue = xQueueCreate(MDNS_ACTION_QUEUE_LEN, sizeof(mdns_action_t *)); + if (!_mdns_server->action_queue) { + err = ESP_ERR_NO_MEM; + goto free_server; + } + + _mdns_server->action_sema = xSemaphoreCreateBinary(); + if (!_mdns_server->action_sema) { + err = ESP_ERR_NO_MEM; + goto free_queue; + } + +#if MDNS_ESP_WIFI_ENABLED && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP) + if ((err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, mdns_preset_if_handle_system_event, NULL)) != ESP_OK) { + goto free_event_handlers; + } +#endif +#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH + if ((err = esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, mdns_preset_if_handle_system_event, NULL)) != ESP_OK) { + goto free_event_handlers; + } +#endif +#if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH + if ((err = esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, mdns_preset_if_handle_system_event, NULL)) != ESP_OK) { + goto free_event_handlers; + } +#endif + +#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH + set_default_duplicated_interfaces(); +#endif + + uint8_t i; +#ifdef CONFIG_LWIP_IPV6 + esp_ip6_addr_t tmp_addr6; +#endif +#ifdef CONFIG_LWIP_IPV4 + esp_netif_ip_info_t if_ip_info; +#endif + + for (i = 0; i < MDNS_MAX_INTERFACES; i++) { +#ifdef CONFIG_LWIP_IPV6 + if (!esp_netif_get_ip6_linklocal(_mdns_get_esp_netif(i), &tmp_addr6) && !_ipv6_address_is_zero(tmp_addr6)) { + _mdns_enable_pcb(i, MDNS_IP_PROTOCOL_V6); + } +#endif +#ifdef CONFIG_LWIP_IPV4 + if (!esp_netif_get_ip_info(_mdns_get_esp_netif(i), &if_ip_info) && if_ip_info.ip.addr) { + _mdns_enable_pcb(i, MDNS_IP_PROTOCOL_V4); + } +#endif + } + if (_mdns_service_task_start()) { + //service start failed! + err = ESP_FAIL; + goto free_all_and_disable_pcbs; + } + + return ESP_OK; + +free_all_and_disable_pcbs: + for (i = 0; i < MDNS_MAX_INTERFACES; i++) { + _mdns_disable_pcb(i, MDNS_IP_PROTOCOL_V6); + _mdns_disable_pcb(i, MDNS_IP_PROTOCOL_V4); + s_esp_netifs[i].duplicate = MDNS_MAX_INTERFACES; + } +#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH +free_event_handlers: + unregister_predefined_handlers(); +#endif + vSemaphoreDelete(_mdns_server->action_sema); +free_queue: + vQueueDelete(_mdns_server->action_queue); +free_server: + free(_mdns_server); + _mdns_server = NULL; + return err; +} + +void mdns_free(void) +{ + uint8_t i, j; + if (!_mdns_server) { + return; + } + + // Unregister handlers before destroying the mdns internals to avoid receiving async events while deinit + unregister_predefined_handlers(); + + mdns_service_remove_all(); + free_delegated_hostnames(); + _mdns_service_task_stop(); + for (i = 0; i < MDNS_MAX_INTERFACES; i++) { + for (j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) { + mdns_pcb_deinit_local(i, j); + } + } + free((char *)_mdns_server->hostname); + free((char *)_mdns_server->instance); + if (_mdns_server->action_queue) { + mdns_action_t *c; + while (xQueueReceive(_mdns_server->action_queue, &c, 0) == pdTRUE) { + _mdns_free_action(c); + } + vQueueDelete(_mdns_server->action_queue); + } + _mdns_clear_tx_queue_head(); + while (_mdns_server->search_once) { + mdns_search_once_t *h = _mdns_server->search_once; + _mdns_server->search_once = h->next; + free(h->instance); + free(h->service); + free(h->proto); + vSemaphoreDelete(h->done_semaphore); + if (h->result) { + _mdns_query_results_free(h->result); + } + free(h); + } + while (_mdns_server->browse) { + mdns_browse_t *b = _mdns_server->browse; + _mdns_server->browse = _mdns_server->browse->next; + _mdns_browse_item_free(b); + + } + vSemaphoreDelete(_mdns_server->action_sema); + free(_mdns_server); + _mdns_server = NULL; +} + +esp_err_t mdns_hostname_set(const char *hostname) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_ARG; + } + if (_str_null_or_empty(hostname) || strlen(hostname) > (MDNS_NAME_BUF_LEN - 1)) { + return ESP_ERR_INVALID_ARG; + } + char *new_hostname = strndup(hostname, MDNS_NAME_BUF_LEN - 1); + if (!new_hostname) { + return ESP_ERR_NO_MEM; + } + + mdns_action_t *action = (mdns_action_t *)malloc(sizeof(mdns_action_t)); + if (!action) { + HOOK_MALLOC_FAILED; + free(new_hostname); + return ESP_ERR_NO_MEM; + } + action->type = ACTION_HOSTNAME_SET; + action->data.hostname_set.hostname = new_hostname; + if (xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0) != pdPASS) { + free(new_hostname); + free(action); + return ESP_ERR_NO_MEM; + } + xSemaphoreTake(_mdns_server->action_sema, portMAX_DELAY); + return ESP_OK; +} + +esp_err_t mdns_hostname_get(char *hostname) +{ + if (!hostname) { + return ESP_ERR_INVALID_ARG; + } + + if (!_mdns_server || !_mdns_server->hostname) { + return ESP_ERR_INVALID_STATE; + } + + MDNS_SERVICE_LOCK(); + size_t len = strnlen(_mdns_server->hostname, MDNS_NAME_BUF_LEN - 1); + strncpy(hostname, _mdns_server->hostname, len); + hostname[len] = 0; + MDNS_SERVICE_UNLOCK(); + return ESP_OK; +} + +esp_err_t mdns_delegate_hostname_add(const char *hostname, const mdns_ip_addr_t *address_list) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + if (_str_null_or_empty(hostname) || strlen(hostname) > (MDNS_NAME_BUF_LEN - 1)) { + return ESP_ERR_INVALID_ARG; + } + char *new_hostname = strndup(hostname, MDNS_NAME_BUF_LEN - 1); + if (!new_hostname) { + return ESP_ERR_NO_MEM; + } + + mdns_action_t *action = (mdns_action_t *)malloc(sizeof(mdns_action_t)); + if (!action) { + HOOK_MALLOC_FAILED; + free(new_hostname); + return ESP_ERR_NO_MEM; + } + action->type = ACTION_DELEGATE_HOSTNAME_ADD; + action->data.delegate_hostname.hostname = new_hostname; + action->data.delegate_hostname.address_list = copy_address_list(address_list); + if (xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0) != pdPASS) { + free(new_hostname); + free(action); + return ESP_ERR_NO_MEM; + } + xSemaphoreTake(_mdns_server->action_sema, portMAX_DELAY); + return ESP_OK; +} + +esp_err_t mdns_delegate_hostname_remove(const char *hostname) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + if (_str_null_or_empty(hostname) || strlen(hostname) > (MDNS_NAME_BUF_LEN - 1)) { + return ESP_ERR_INVALID_ARG; + } + char *new_hostname = strndup(hostname, MDNS_NAME_BUF_LEN - 1); + if (!new_hostname) { + return ESP_ERR_NO_MEM; + } + + mdns_action_t *action = (mdns_action_t *)malloc(sizeof(mdns_action_t)); + if (!action) { + HOOK_MALLOC_FAILED; + free(new_hostname); + return ESP_ERR_NO_MEM; + } + action->type = ACTION_DELEGATE_HOSTNAME_REMOVE; + action->data.delegate_hostname.hostname = new_hostname; + if (xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0) != pdPASS) { + free(new_hostname); + free(action); + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +esp_err_t mdns_delegate_hostname_set_address(const char *hostname, const mdns_ip_addr_t *address_list) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + if (_str_null_or_empty(hostname) || strlen(hostname) > (MDNS_NAME_BUF_LEN - 1)) { + return ESP_ERR_INVALID_ARG; + } + char *new_hostname = strndup(hostname, MDNS_NAME_BUF_LEN - 1); + if (!new_hostname) { + return ESP_ERR_NO_MEM; + } + + mdns_action_t *action = (mdns_action_t *)malloc(sizeof(mdns_action_t)); + if (!action) { + HOOK_MALLOC_FAILED; + free(new_hostname); + return ESP_ERR_NO_MEM; + } + action->type = ACTION_DELEGATE_HOSTNAME_SET_ADDR; + action->data.delegate_hostname.hostname = new_hostname; + action->data.delegate_hostname.address_list = copy_address_list(address_list); + if (xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0) != pdPASS) { + free(new_hostname); + free(action); + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +bool mdns_hostname_exists(const char *hostname) +{ + bool ret = false; + MDNS_SERVICE_LOCK(); + ret = _hostname_is_ours(hostname); + MDNS_SERVICE_UNLOCK(); + return ret; +} + +esp_err_t mdns_instance_name_set(const char *instance) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + if (_str_null_or_empty(instance) || _mdns_server->hostname == NULL || strlen(instance) > (MDNS_NAME_BUF_LEN - 1)) { + return ESP_ERR_INVALID_ARG; + } + char *new_instance = strndup(instance, MDNS_NAME_BUF_LEN - 1); + if (!new_instance) { + return ESP_ERR_NO_MEM; + } + + mdns_action_t *action = (mdns_action_t *)malloc(sizeof(mdns_action_t)); + if (!action) { + HOOK_MALLOC_FAILED; + free(new_instance); + return ESP_ERR_NO_MEM; + } + action->type = ACTION_INSTANCE_SET; + action->data.instance = new_instance; + if (xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0) != pdPASS) { + free(new_instance); + free(action); + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +/* + * MDNS SERVICES + * */ + +esp_err_t mdns_service_add_for_host(const char *instance, const char *service, const char *proto, const char *host, + uint16_t port, mdns_txt_item_t txt[], size_t num_items) +{ + if (!_mdns_server || _str_null_or_empty(service) || _str_null_or_empty(proto) || !_mdns_server->hostname) { + return ESP_ERR_INVALID_ARG; + } + + MDNS_SERVICE_LOCK(); + esp_err_t ret = ESP_OK; + const char *hostname = host ? host : _mdns_server->hostname; + mdns_service_t *s = NULL; + + ESP_GOTO_ON_FALSE(_mdns_can_add_more_services(), ESP_ERR_NO_MEM, err, TAG, + "Cannot add more services, please increase CONFIG_MDNS_MAX_SERVICES (%d)", CONFIG_MDNS_MAX_SERVICES); + + mdns_srv_item_t *item = _mdns_get_service_item_instance(instance, service, proto, hostname); + ESP_GOTO_ON_FALSE(!item, ESP_ERR_INVALID_ARG, err, TAG, "Service already exists"); + + s = _mdns_create_service(service, proto, hostname, port, instance, num_items, txt); + ESP_GOTO_ON_FALSE(s, ESP_ERR_NO_MEM, err, TAG, "Cannot create service: Out of memory"); + + item = (mdns_srv_item_t *)malloc(sizeof(mdns_srv_item_t)); + ESP_GOTO_ON_FALSE(item, ESP_ERR_NO_MEM, err, TAG, "Cannot create service: Out of memory"); + + item->service = s; + item->next = NULL; + + item->next = _mdns_server->services; + _mdns_server->services = item; + _mdns_probe_all_pcbs(&item, 1, false, false); + MDNS_SERVICE_UNLOCK(); + return ESP_OK; + +err: + MDNS_SERVICE_UNLOCK(); + _mdns_free_service(s); + if (ret == ESP_ERR_NO_MEM) { + HOOK_MALLOC_FAILED; + } + return ret; +} + +esp_err_t mdns_service_add(const char *instance, const char *service, const char *proto, uint16_t port, + mdns_txt_item_t txt[], size_t num_items) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_add_for_host(instance, service, proto, NULL, port, txt, num_items); +} + +bool mdns_service_exists(const char *service_type, const char *proto, const char *hostname) +{ + bool ret = false; + MDNS_SERVICE_LOCK(); + ret = _mdns_get_service_item(service_type, proto, hostname) != NULL; + MDNS_SERVICE_UNLOCK(); + return ret; +} + +bool mdns_service_exists_with_instance(const char *instance, const char *service_type, const char *proto, + const char *hostname) +{ + bool ret = false; + MDNS_SERVICE_LOCK(); + ret = _mdns_get_service_item_instance(instance, service_type, proto, hostname) != NULL; + MDNS_SERVICE_UNLOCK(); + return ret; +} + +static mdns_txt_item_t *_copy_mdns_txt_items(mdns_txt_linked_item_t *items, uint8_t **txt_value_len, size_t *txt_count) +{ + mdns_txt_item_t *ret = NULL; + size_t ret_index = 0; + for (mdns_txt_linked_item_t *tmp = items; tmp != NULL; tmp = tmp->next) { + ret_index++; + } + *txt_count = ret_index; + if (ret_index == 0) { // handle empty TXT + *txt_value_len = NULL; + return NULL; + } + ret = (mdns_txt_item_t *)calloc(ret_index, sizeof(mdns_txt_item_t)); + *txt_value_len = (uint8_t *)calloc(ret_index, sizeof(uint8_t)); + if (!ret || !(*txt_value_len)) { + HOOK_MALLOC_FAILED; + goto handle_error; + } + ret_index = 0; + for (mdns_txt_linked_item_t *tmp = items; tmp != NULL; tmp = tmp->next) { + size_t key_len = strlen(tmp->key); + char *key = (char *)malloc(key_len + 1); + if (!key) { + HOOK_MALLOC_FAILED; + goto handle_error; + } + memcpy(key, tmp->key, key_len); + key[key_len] = 0; + ret[ret_index].key = key; + char *value = (char *)malloc(tmp->value_len + 1); + if (!value) { + HOOK_MALLOC_FAILED; + goto handle_error; + } + memcpy(value, tmp->value, tmp->value_len); + value[tmp->value_len] = 0; + ret[ret_index].value = value; + (*txt_value_len)[ret_index] = tmp->value_len; + ret_index++; + } + return ret; + +handle_error: + for (size_t y = 0; y < ret_index + 1 && ret != NULL; y++) { + mdns_txt_item_t *t = &ret[y]; + free((char *)t->key); + free((char *)t->value); + } + free(*txt_value_len); + free(ret); + return NULL; +} + +static mdns_ip_addr_t *_copy_delegated_host_address_list(char *hostname) +{ + mdns_host_item_t *host = _mdns_host_list; + while (host) { + if (strcasecmp(host->hostname, hostname) == 0) { + return copy_address_list(host->address_list); + } + host = host->next; + } + return NULL; +} + +static mdns_result_t *_mdns_lookup_service(const char *instance, const char *service, const char *proto, size_t max_results, bool selfhost) +{ + if (_str_null_or_empty(service) || _str_null_or_empty(proto)) { + return NULL; + } + mdns_result_t *results = NULL; + size_t num_results = 0; + mdns_srv_item_t *s = _mdns_server->services; + while (s) { + mdns_service_t *srv = s->service; + if (!srv || !srv->hostname) { + s = s->next; + continue; + } + bool is_service_selfhosted = !_str_null_or_empty(_mdns_server->hostname) && !strcasecmp(_mdns_server->hostname, srv->hostname); + bool is_service_delegated = _str_null_or_empty(_mdns_server->hostname) || strcasecmp(_mdns_server->hostname, srv->hostname); + if ((selfhost && is_service_selfhosted) || (!selfhost && is_service_delegated)) { + if (!strcasecmp(srv->service, service) && !strcasecmp(srv->proto, proto) && + (_str_null_or_empty(instance) || _mdns_instance_name_match(srv->instance, instance))) { + mdns_result_t *item = (mdns_result_t *)malloc(sizeof(mdns_result_t)); + if (!item) { + HOOK_MALLOC_FAILED; + goto handle_error; + } + item->next = results; + results = item; + item->esp_netif = NULL; + item->ttl = _str_null_or_empty(instance) ? MDNS_ANSWER_PTR_TTL : MDNS_ANSWER_SRV_TTL; + item->ip_protocol = MDNS_IP_PROTOCOL_MAX; + if (srv->instance) { + item->instance_name = strndup(srv->instance, MDNS_NAME_BUF_LEN - 1); + if (!item->instance_name) { + HOOK_MALLOC_FAILED; + goto handle_error; + } + } else { + item->instance_name = NULL; + } + item->service_type = strndup(srv->service, MDNS_NAME_BUF_LEN - 1); + if (!item->service_type) { + HOOK_MALLOC_FAILED; + goto handle_error; + } + item->proto = strndup(srv->proto, MDNS_NAME_BUF_LEN - 1); + if (!item->proto) { + HOOK_MALLOC_FAILED; + goto handle_error; + } + item->hostname = strndup(srv->hostname, MDNS_NAME_BUF_LEN - 1); + if (!item->hostname) { + HOOK_MALLOC_FAILED; + goto handle_error; + } + item->port = srv->port; + item->txt = _copy_mdns_txt_items(srv->txt, &(item->txt_value_len), &(item->txt_count)); + // We should not append addresses for selfhost lookup result as we don't know which interface's address to append. + if (selfhost) { + item->addr = NULL; + } else { + item->addr = _copy_delegated_host_address_list(item->hostname); + if (!item->addr) { + goto handle_error; + } + } + if (num_results < max_results) { + num_results++; + } + if (num_results >= max_results) { + break; + } + } + } + s = s->next; + } + return results; +handle_error: + _mdns_query_results_free(results); + return NULL; +} + +esp_err_t mdns_service_port_set_for_host(const char *instance, const char *service, const char *proto, const char *host, uint16_t port) +{ + MDNS_SERVICE_LOCK(); + esp_err_t ret = ESP_OK; + const char *hostname = host ? host : _mdns_server->hostname; + ESP_GOTO_ON_FALSE(_mdns_server && _mdns_server->services && !_str_null_or_empty(service) && !_str_null_or_empty(proto) && port, + ESP_ERR_INVALID_ARG, err, TAG, "Invalid state or arguments"); + mdns_srv_item_t *s = _mdns_get_service_item_instance(instance, service, proto, hostname); + ESP_GOTO_ON_FALSE(s, ESP_ERR_NOT_FOUND, err, TAG, "Service doesn't exist"); + + s->service->port = port; + _mdns_announce_all_pcbs(&s, 1, true); + +err: + MDNS_SERVICE_UNLOCK(); + return ret; +} + +esp_err_t mdns_service_port_set(const char *service, const char *proto, uint16_t port) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_port_set_for_host(NULL, service, proto, NULL, port); +} + +esp_err_t mdns_service_txt_set_for_host(const char *instance, const char *service, const char *proto, const char *host, + mdns_txt_item_t txt_items[], uint8_t num_items) +{ + MDNS_SERVICE_LOCK(); + esp_err_t ret = ESP_OK; + const char *hostname = host ? host : _mdns_server->hostname; + ESP_GOTO_ON_FALSE(_mdns_server && _mdns_server->services && !_str_null_or_empty(service) && !_str_null_or_empty(proto) && !(num_items && txt_items == NULL), + ESP_ERR_INVALID_ARG, err, TAG, "Invalid state or arguments"); + mdns_srv_item_t *s = _mdns_get_service_item_instance(instance, service, proto, hostname); + ESP_GOTO_ON_FALSE(s, ESP_ERR_NOT_FOUND, err, TAG, "Service doesn't exist"); + + mdns_txt_linked_item_t *new_txt = NULL; + if (num_items) { + new_txt = _mdns_allocate_txt(num_items, txt_items); + if (!new_txt) { + return ESP_ERR_NO_MEM; + } + } + mdns_service_t *srv = s->service; + mdns_txt_linked_item_t *txt = srv->txt; + srv->txt = NULL; + _mdns_free_linked_txt(txt); + srv->txt = new_txt; + _mdns_announce_all_pcbs(&s, 1, false); + +err: + MDNS_SERVICE_UNLOCK(); + return ret; +} + +esp_err_t mdns_service_txt_set(const char *service, const char *proto, mdns_txt_item_t txt[], uint8_t num_items) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_txt_set_for_host(NULL, service, proto, NULL, txt, num_items); +} + +esp_err_t mdns_service_txt_item_set_for_host_with_explicit_value_len(const char *instance, const char *service, const char *proto, + const char *host, const char *key, const char *value_arg, uint8_t value_len) +{ + MDNS_SERVICE_LOCK(); + esp_err_t ret = ESP_OK; + char *value = NULL; + mdns_txt_linked_item_t *new_txt = NULL; + const char *hostname = host ? host : _mdns_server->hostname; + ESP_GOTO_ON_FALSE(_mdns_server && _mdns_server->services && !_str_null_or_empty(service) && !_str_null_or_empty(proto) && !_str_null_or_empty(key) && + !((!value_arg && value_len)), ESP_ERR_INVALID_ARG, err, TAG, "Invalid state or arguments"); + + mdns_srv_item_t *s = _mdns_get_service_item_instance(instance, service, proto, hostname); + ESP_GOTO_ON_FALSE(s, ESP_ERR_NOT_FOUND, err, TAG, "Service doesn't exist"); + + mdns_service_t *srv = s->service; + if (value_len > 0) { + value = (char *) malloc(value_len); + ESP_GOTO_ON_FALSE(value, ESP_ERR_NO_MEM, out_of_mem, TAG, "Out of memory"); + memcpy(value, value_arg, value_len); + } else { + value_len = 0; + } + mdns_txt_linked_item_t *txt = srv->txt; + while (txt) { + if (strcmp(txt->key, key) == 0) { + free((char *)txt->value); + txt->value = value; + txt->value_len = value_len; + break; + } + txt = txt->next; + } + if (!txt) { + new_txt = (mdns_txt_linked_item_t *)malloc(sizeof(mdns_txt_linked_item_t)); + ESP_GOTO_ON_FALSE(new_txt, ESP_ERR_NO_MEM, out_of_mem, TAG, "Out of memory"); + new_txt->key = strdup(key); + ESP_GOTO_ON_FALSE(new_txt->key, ESP_ERR_NO_MEM, out_of_mem, TAG, "Out of memory"); + new_txt->value = value; + new_txt->value_len = value_len; + new_txt->next = srv->txt; + srv->txt = new_txt; + } + + _mdns_announce_all_pcbs(&s, 1, false); + +err: + MDNS_SERVICE_UNLOCK(); + return ret; +out_of_mem: + MDNS_SERVICE_UNLOCK(); + HOOK_MALLOC_FAILED; + free(value); + free(new_txt); + return ret; +} + +esp_err_t mdns_service_txt_item_set_for_host(const char *instance, const char *service, const char *proto, const char *hostname, + const char *key, const char *value) +{ + return mdns_service_txt_item_set_for_host_with_explicit_value_len(instance, service, proto, hostname, key, value, + strlen(value)); +} + + +esp_err_t mdns_service_txt_item_set(const char *service, const char *proto, const char *key, const char *value) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_txt_item_set_for_host_with_explicit_value_len(NULL, service, proto, NULL, key, + value, strlen(value)); +} + +esp_err_t mdns_service_txt_item_set_with_explicit_value_len(const char *service, const char *proto, const char *key, + const char *value, uint8_t value_len) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_txt_item_set_for_host_with_explicit_value_len(NULL, service, proto, NULL, key, value, value_len); +} + +esp_err_t mdns_service_txt_item_remove_for_host(const char *instance, const char *service, const char *proto, const char *host, + const char *key) +{ + MDNS_SERVICE_LOCK(); + esp_err_t ret = ESP_OK; + const char *hostname = host ? host : _mdns_server->hostname; + ESP_GOTO_ON_FALSE(_mdns_server && _mdns_server->services && !_str_null_or_empty(service) && !_str_null_or_empty(proto) && !_str_null_or_empty(key), + ESP_ERR_INVALID_ARG, err, TAG, "Invalid state or arguments"); + + mdns_srv_item_t *s = _mdns_get_service_item_instance(instance, service, proto, hostname); + ESP_GOTO_ON_FALSE(s, ESP_ERR_NOT_FOUND, err, TAG, "Service doesn't exist"); + + mdns_service_t *srv = s->service; + mdns_txt_linked_item_t *txt = srv->txt; + if (!txt) { + goto err; + } + if (strcmp(txt->key, key) == 0) { + srv->txt = txt->next; + free((char *)txt->key); + free((char *)txt->value); + free(txt); + } else { + while (txt->next) { + if (strcmp(txt->next->key, key) == 0) { + mdns_txt_linked_item_t *t = txt->next; + txt->next = t->next; + free((char *)t->key); + free((char *)t->value); + free(t); + break; + } else { + txt = txt->next; + } + } + } + + _mdns_announce_all_pcbs(&s, 1, false); + +err: + MDNS_SERVICE_UNLOCK(); + if (ret == ESP_ERR_NO_MEM) { + HOOK_MALLOC_FAILED; + } + return ret; +} + +esp_err_t mdns_service_txt_item_remove(const char *service, const char *proto, const char *key) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_txt_item_remove_for_host(NULL, service, proto, NULL, key); +} + +static esp_err_t _mdns_service_subtype_remove_for_host(mdns_srv_item_t *service, const char *subtype) +{ + esp_err_t ret = ESP_ERR_NOT_FOUND; + mdns_subtype_t *srv_subtype = service->service->subtype; + mdns_subtype_t *pre = service->service->subtype; + while (srv_subtype) { + if (strcmp(srv_subtype->subtype, subtype) == 0) { + // Target subtype is found. + if (srv_subtype == service->service->subtype) { + // The first node needs to be removed + service->service->subtype = service->service->subtype->next; + } else { + pre->next = srv_subtype->next; + } + free((char *)srv_subtype->subtype); + free(srv_subtype); + ret = ESP_OK; + break; + } + pre = srv_subtype; + srv_subtype = srv_subtype->next; + } + if (ret == ESP_ERR_NOT_FOUND) { + ESP_LOGE(TAG, "Subtype : %s doesn't exist", subtype); + } + + return ret; +} + +esp_err_t mdns_service_subtype_remove_for_host(const char *instance_name, const char *service, const char *proto, + const char *hostname, const char *subtype) +{ + MDNS_SERVICE_LOCK(); + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_FALSE(_mdns_server && _mdns_server->services && !_str_null_or_empty(service) && !_str_null_or_empty(proto) && + !_str_null_or_empty(subtype), ESP_ERR_INVALID_ARG, err, TAG, "Invalid state or arguments"); + + mdns_srv_item_t *s = _mdns_get_service_item_instance(instance_name, service, proto, hostname); + ESP_GOTO_ON_FALSE(s, ESP_ERR_NOT_FOUND, err, TAG, "Service doesn't exist"); + + ret = _mdns_service_subtype_remove_for_host(s, subtype); + ESP_GOTO_ON_ERROR(ret, err, TAG, "Failed to remove the subtype: %s", subtype); + + // TODO: Need to transmit a sendbye message for the removed subtype. + // TODO: Need to remove this subtype answer from the scheduled answer list. +err: + MDNS_SERVICE_UNLOCK(); + return ret; +} + +static esp_err_t _mdns_service_subtype_add_for_host(mdns_srv_item_t *service, const char *subtype) +{ + esp_err_t ret = ESP_OK; + mdns_subtype_t *srv_subtype = service->service->subtype; + while (srv_subtype) { + ESP_GOTO_ON_FALSE(strcmp(srv_subtype->subtype, subtype) != 0, ESP_ERR_INVALID_ARG, err, TAG, "Subtype: %s has already been added", subtype); + srv_subtype = srv_subtype->next; + } + + mdns_subtype_t *subtype_item = (mdns_subtype_t *)malloc(sizeof(mdns_subtype_t)); + ESP_GOTO_ON_FALSE(subtype_item, ESP_ERR_NO_MEM, out_of_mem, TAG, "Out of memory"); + subtype_item->subtype = strdup(subtype); + ESP_GOTO_ON_FALSE(subtype_item->subtype, ESP_ERR_NO_MEM, out_of_mem, TAG, "Out of memory"); + subtype_item->next = service->service->subtype; + service->service->subtype = subtype_item; + +err: + return ret; +out_of_mem: + HOOK_MALLOC_FAILED; + free(subtype_item); + return ret; +} + +esp_err_t mdns_service_subtype_add_multiple_items_for_host(const char *instance_name, const char *service, const char *proto, + const char *hostname, mdns_subtype_item_t subtype[], uint8_t num_items) +{ + MDNS_SERVICE_LOCK(); + esp_err_t ret = ESP_OK; + int cur_index = 0; + ESP_GOTO_ON_FALSE(_mdns_server && _mdns_server->services && !_str_null_or_empty(service) && !_str_null_or_empty(proto) && + (num_items > 0), ESP_ERR_INVALID_ARG, err, TAG, "Invalid state or arguments"); + + mdns_srv_item_t *s = _mdns_get_service_item_instance(instance_name, service, proto, hostname); + ESP_GOTO_ON_FALSE(s, ESP_ERR_NOT_FOUND, err, TAG, "Service doesn't exist"); + + for (; cur_index < num_items; cur_index++) { + ret = _mdns_service_subtype_add_for_host(s, subtype[cur_index].subtype); + if (ret == ESP_OK) { + continue; + } else if (ret == ESP_ERR_NO_MEM) { + ESP_LOGE(TAG, "Out of memory"); + goto err; + } else { + ESP_LOGE(TAG, "Failed to add subtype: %s", subtype[cur_index].subtype); + goto exit; + } + } + + _mdns_announce_all_pcbs(&s, 1, false); +err: + if (ret == ESP_ERR_NO_MEM) { + for (int idx = 0; idx < cur_index; idx++) { + _mdns_service_subtype_remove_for_host(s, subtype[idx].subtype); + } + } +exit: + MDNS_SERVICE_UNLOCK(); + return ret; +} + +esp_err_t mdns_service_subtype_add_for_host(const char *instance_name, const char *service_type, const char *proto, + const char *hostname, const char *subtype) +{ + mdns_subtype_item_t _subtype[1]; + _subtype[0].subtype = subtype; + return mdns_service_subtype_add_multiple_items_for_host(instance_name, service_type, proto, hostname, _subtype, 1); +} + +esp_err_t mdns_service_subtype_update_multiple_items_for_host(const char *instance_name, const char *service_type, const char *proto, + const char *hostname, mdns_subtype_item_t subtype[], uint8_t num_items) +{ + MDNS_SERVICE_LOCK(); + esp_err_t ret = ESP_OK; + int cur_index = 0; + ESP_GOTO_ON_FALSE(_mdns_server && _mdns_server->services && !_str_null_or_empty(service_type) && !_str_null_or_empty(proto), + ESP_ERR_INVALID_ARG, err, TAG, "Invalid state or arguments"); + + mdns_srv_item_t *s = _mdns_get_service_item_instance(instance_name, service_type, proto, hostname); + ESP_GOTO_ON_FALSE(s, ESP_ERR_NOT_FOUND, err, TAG, "Service doesn't exist"); + + // TODO: find subtype needs to say sendbye + _mdns_free_service_subtype(s->service); + + for (; cur_index < num_items; cur_index++) { + ret = _mdns_service_subtype_add_for_host(s, subtype[cur_index].subtype); + if (ret == ESP_OK) { + continue; + } else if (ret == ESP_ERR_NO_MEM) { + ESP_LOGE(TAG, "Out of memory"); + goto err; + } else { + ESP_LOGE(TAG, "Failed to add subtype: %s", subtype[cur_index].subtype); + goto exit; + } + } + if (num_items) { + _mdns_announce_all_pcbs(&s, 1, false); + } +err: + if (ret == ESP_ERR_NO_MEM) { + for (int idx = 0; idx < cur_index; idx++) { + _mdns_service_subtype_remove_for_host(s, subtype[idx].subtype); + } + } +exit: + MDNS_SERVICE_UNLOCK(); + return ret; +} + +esp_err_t mdns_service_instance_name_set_for_host(const char *instance_old, const char *service, const char *proto, const char *host, + const char *instance) +{ + MDNS_SERVICE_LOCK(); + esp_err_t ret = ESP_OK; + const char *hostname = host ? host : _mdns_server->hostname; + + ESP_GOTO_ON_FALSE(_mdns_server && _mdns_server->services && !_str_null_or_empty(service) && !_str_null_or_empty(proto) && + !_str_null_or_empty(instance) && strlen(instance) <= (MDNS_NAME_BUF_LEN - 1), ESP_ERR_INVALID_ARG, err, TAG, "Invalid state or arguments"); + + mdns_srv_item_t *s = _mdns_get_service_item_instance(instance_old, service, proto, hostname); + ESP_GOTO_ON_FALSE(s, ESP_ERR_NOT_FOUND, err, TAG, "Service doesn't exist"); + + if (s->service->instance) { + _mdns_send_bye(&s, 1, false); + free((char *)s->service->instance); + } + s->service->instance = strndup(instance, MDNS_NAME_BUF_LEN - 1); + ESP_GOTO_ON_FALSE(s->service->instance, ESP_ERR_NO_MEM, err, TAG, "Out of memory"); + _mdns_probe_all_pcbs(&s, 1, false, false); + +err: + MDNS_SERVICE_UNLOCK(); + return ret; +} + +esp_err_t mdns_service_instance_name_set(const char *service, const char *proto, const char *instance) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_instance_name_set_for_host(NULL, service, proto, NULL, instance); +} + +esp_err_t mdns_service_remove_for_host(const char *instance, const char *service, const char *proto, const char *host) +{ + MDNS_SERVICE_LOCK(); + esp_err_t ret = ESP_OK; + const char *hostname = host ? host : _mdns_server->hostname; + ESP_GOTO_ON_FALSE(_mdns_server && _mdns_server->services && !_str_null_or_empty(service) && !_str_null_or_empty(proto), + ESP_ERR_INVALID_ARG, err, TAG, "Invalid state or arguments"); + mdns_srv_item_t *s = _mdns_get_service_item_instance(instance, service, proto, hostname); + ESP_GOTO_ON_FALSE(s, ESP_ERR_NOT_FOUND, err, TAG, "Service doesn't exist"); + + mdns_srv_item_t *a = _mdns_server->services; + mdns_srv_item_t *b = a; + if (instance) { + while (a) { + if (_mdns_service_match_instance(a->service, instance, service, proto, hostname)) { + if (_mdns_server->services != a) { + b->next = a->next; + } else { + _mdns_server->services = a->next; + } + _mdns_send_bye(&a, 1, false); + _mdns_remove_scheduled_service_packets(a->service); + _mdns_free_service(a->service); + free(a); + break; + } + b = a; + a = a->next; + } + } else { + while (a) { + if (_mdns_service_match(a->service, service, proto, hostname)) { + if (_mdns_server->services != a) { + b->next = a->next; + } else { + _mdns_server->services = a->next; + } + _mdns_send_bye(&a, 1, false); + _mdns_remove_scheduled_service_packets(a->service); + _mdns_free_service(a->service); + free(a); + break; + } + b = a; + a = a->next; + } + } + +err: + MDNS_SERVICE_UNLOCK(); + return ret; +} + +esp_err_t mdns_service_remove(const char *service_type, const char *proto) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + return mdns_service_remove_for_host(NULL, service_type, proto, NULL); +} + +esp_err_t mdns_service_remove_all(void) +{ + MDNS_SERVICE_LOCK(); + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_FALSE(_mdns_server, ESP_ERR_INVALID_ARG, done, TAG, "Invalid state"); + if (!_mdns_server->services) { + goto done; + } + + _mdns_send_final_bye(false); + mdns_srv_item_t *services = _mdns_server->services; + _mdns_server->services = NULL; + while (services) { + mdns_srv_item_t *s = services; + services = services->next; + _mdns_remove_scheduled_service_packets(s->service); + _mdns_free_service(s->service); + free(s); + } + +done: + MDNS_SERVICE_UNLOCK(); + return ret; +} + +/* + * MDNS QUERY + * */ +void mdns_query_results_free(mdns_result_t *results) +{ + MDNS_SERVICE_LOCK(); + _mdns_query_results_free(results); + MDNS_SERVICE_UNLOCK(); +} + +static void _mdns_query_results_free(mdns_result_t *results) +{ + mdns_result_t *r; + mdns_ip_addr_t *a; + + while (results) { + r = results; + + free((char *)(r->hostname)); + free((char *)(r->instance_name)); + free((char *)(r->service_type)); + free((char *)(r->proto)); + + for (size_t i = 0; i < r->txt_count; i++) { + free((char *)(r->txt[i].key)); + free((char *)(r->txt[i].value)); + } + free(r->txt); + free(r->txt_value_len); + + while (r->addr) { + a = r->addr; + r->addr = r->addr->next; + free(a); + } + + results = results->next; + free(r); + } +} + +esp_err_t mdns_query_async_delete(mdns_search_once_t *search) +{ + if (!search) { + return ESP_ERR_INVALID_ARG; + } + if (search->state != SEARCH_OFF) { + return ESP_ERR_INVALID_STATE; + } + + MDNS_SERVICE_LOCK(); + _mdns_search_free(search); + MDNS_SERVICE_UNLOCK(); + + return ESP_OK; +} + +bool mdns_query_async_get_results(mdns_search_once_t *search, uint32_t timeout, mdns_result_t **results, uint8_t *num_results) +{ + if (xSemaphoreTake(search->done_semaphore, pdMS_TO_TICKS(timeout)) == pdTRUE) { + if (results) { + *results = search->result; + } + if (num_results) { + *num_results = search->num_results; + } + return true; + } + return false; +} + +mdns_search_once_t *mdns_query_async_new(const char *name, const char *service, const char *proto, uint16_t type, + uint32_t timeout, size_t max_results, mdns_query_notify_t notifier) +{ + mdns_search_once_t *search = NULL; + + if (!_mdns_server || !timeout || _str_null_or_empty(service) != _str_null_or_empty(proto)) { + return NULL; + } + + search = _mdns_search_init(name, service, proto, type, type != MDNS_TYPE_PTR, timeout, max_results, notifier); + if (!search) { + return NULL; + } + + if (_mdns_send_search_action(ACTION_SEARCH_ADD, search)) { + _mdns_search_free(search); + return NULL; + } + + return search; +} + +esp_err_t mdns_query_generic(const char *name, const char *service, const char *proto, uint16_t type, mdns_query_transmission_type_t transmission_type, uint32_t timeout, size_t max_results, mdns_result_t **results) +{ + mdns_search_once_t *search = NULL; + + *results = NULL; + + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + + if (!timeout || _str_null_or_empty(service) != _str_null_or_empty(proto)) { + return ESP_ERR_INVALID_ARG; + } + + search = _mdns_search_init(name, service, proto, type, transmission_type == MDNS_QUERY_UNICAST, timeout, max_results, NULL); + if (!search) { + return ESP_ERR_NO_MEM; + } + + if (_mdns_send_search_action(ACTION_SEARCH_ADD, search)) { + _mdns_search_free(search); + return ESP_ERR_NO_MEM; + } + xSemaphoreTake(search->done_semaphore, portMAX_DELAY); + + *results = search->result; + _mdns_search_free(search); + + return ESP_OK; +} + +esp_err_t mdns_query(const char *name, const char *service_type, const char *proto, uint16_t type, uint32_t timeout, size_t max_results, mdns_result_t **results) +{ + return mdns_query_generic(name, service_type, proto, type, type != MDNS_TYPE_PTR, timeout, max_results, results); +} + +esp_err_t mdns_query_ptr(const char *service, const char *proto, uint32_t timeout, size_t max_results, mdns_result_t **results) +{ + if (_str_null_or_empty(service) || _str_null_or_empty(proto)) { + return ESP_ERR_INVALID_ARG; + } + + return mdns_query(NULL, service, proto, MDNS_TYPE_PTR, timeout, max_results, results); +} + +esp_err_t mdns_query_srv(const char *instance, const char *service, const char *proto, uint32_t timeout, mdns_result_t **result) +{ + if (_str_null_or_empty(instance) || _str_null_or_empty(service) || _str_null_or_empty(proto)) { + return ESP_ERR_INVALID_ARG; + } + + return mdns_query(instance, service, proto, MDNS_TYPE_SRV, timeout, 1, result); +} + +esp_err_t mdns_query_txt(const char *instance, const char *service, const char *proto, uint32_t timeout, mdns_result_t **result) +{ + if (_str_null_or_empty(instance) || _str_null_or_empty(service) || _str_null_or_empty(proto)) { + return ESP_ERR_INVALID_ARG; + } + + return mdns_query(instance, service, proto, MDNS_TYPE_TXT, timeout, 1, result); +} + +esp_err_t mdns_lookup_delegated_service(const char *instance, const char *service, const char *proto, size_t max_results, + mdns_result_t **result) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + if (!result || _str_null_or_empty(service) || _str_null_or_empty(proto)) { + return ESP_ERR_INVALID_ARG; + } + MDNS_SERVICE_LOCK(); + *result = _mdns_lookup_service(instance, service, proto, max_results, false); + MDNS_SERVICE_UNLOCK(); + return ESP_OK; +} + +esp_err_t mdns_lookup_selfhosted_service(const char *instance, const char *service, const char *proto, size_t max_results, + mdns_result_t **result) +{ + if (!_mdns_server) { + return ESP_ERR_INVALID_STATE; + } + if (!result || _str_null_or_empty(service) || _str_null_or_empty(proto)) { + return ESP_ERR_INVALID_ARG; + } + MDNS_SERVICE_LOCK(); + *result = _mdns_lookup_service(instance, service, proto, max_results, true); + MDNS_SERVICE_UNLOCK(); + return ESP_OK; +} + +#ifdef CONFIG_LWIP_IPV4 +esp_err_t mdns_query_a(const char *name, uint32_t timeout, esp_ip4_addr_t *addr) +{ + mdns_result_t *result = NULL; + esp_err_t err; + + if (_str_null_or_empty(name)) { + return ESP_ERR_INVALID_ARG; + } + + if (strstr(name, ".local")) { + ESP_LOGW(TAG, "Please note that hostname must not contain domain name, as mDNS uses '.local' domain"); + } + + err = mdns_query(name, NULL, NULL, MDNS_TYPE_A, timeout, 1, &result); + + if (err) { + return err; + } + + if (!result) { + return ESP_ERR_NOT_FOUND; + } + + mdns_ip_addr_t *a = result->addr; + while (a) { + if (a->addr.type == ESP_IPADDR_TYPE_V4) { + addr->addr = a->addr.u_addr.ip4.addr; + mdns_query_results_free(result); + return ESP_OK; + } + a = a->next; + } + + mdns_query_results_free(result); + return ESP_ERR_NOT_FOUND; +} +#endif /* CONFIG_LWIP_IPV4 */ + +#ifdef CONFIG_LWIP_IPV6 +esp_err_t mdns_query_aaaa(const char *name, uint32_t timeout, esp_ip6_addr_t *addr) +{ + mdns_result_t *result = NULL; + esp_err_t err; + + if (_str_null_or_empty(name)) { + return ESP_ERR_INVALID_ARG; + } + + if (strstr(name, ".local")) { + ESP_LOGW(TAG, "Please note that hostname must not contain domain name, as mDNS uses '.local' domain"); + } + + err = mdns_query(name, NULL, NULL, MDNS_TYPE_AAAA, timeout, 1, &result); + + if (err) { + return err; + } + + if (!result) { + return ESP_ERR_NOT_FOUND; + } + + mdns_ip_addr_t *a = result->addr; + while (a) { + if (a->addr.type == ESP_IPADDR_TYPE_V6) { + memcpy(addr->addr, a->addr.u_addr.ip6.addr, 16); + mdns_query_results_free(result); + return ESP_OK; + } + a = a->next; + } + + mdns_query_results_free(result); + return ESP_ERR_NOT_FOUND; +} +#endif /* CONFIG_LWIP_IPV6 */ + +#ifdef MDNS_ENABLE_DEBUG + +void mdns_debug_packet(const uint8_t *data, size_t len) +{ + static mdns_name_t n; + mdns_header_t header; + const uint8_t *content = data + MDNS_HEAD_LEN; + uint32_t t = xTaskGetTickCount() * portTICK_PERIOD_MS; + mdns_name_t *name = &n; + memset(name, 0, sizeof(mdns_name_t)); + + _mdns_dbg_printf("Packet[%" PRIu32 "]: ", t); + + header.id = _mdns_read_u16(data, MDNS_HEAD_ID_OFFSET); + header.flags = _mdns_read_u16(data, MDNS_HEAD_FLAGS_OFFSET); + header.questions = _mdns_read_u16(data, MDNS_HEAD_QUESTIONS_OFFSET); + header.answers = _mdns_read_u16(data, MDNS_HEAD_ANSWERS_OFFSET); + header.servers = _mdns_read_u16(data, MDNS_HEAD_SERVERS_OFFSET); + header.additional = _mdns_read_u16(data, MDNS_HEAD_ADDITIONAL_OFFSET); + + _mdns_dbg_printf("%s", + (header.flags == MDNS_FLAGS_QR_AUTHORITATIVE) ? "AUTHORITATIVE\n" : + (header.flags == MDNS_FLAGS_DISTRIBUTED) ? "DISTRIBUTED\n" : + (header.flags == 0) ? "\n" : " " + ); + if (header.flags && header.flags != MDNS_FLAGS_QR_AUTHORITATIVE) { + _mdns_dbg_printf("0x%04X\n", header.flags); + } + + if (header.questions) { + uint8_t qs = header.questions; + + while (qs--) { + content = _mdns_parse_fqdn(data, content, name, len); + if (!content || content + MDNS_CLASS_OFFSET + 1 >= data + len) { + header.answers = 0; + header.additional = 0; + header.servers = 0; + _mdns_dbg_printf("ERROR: parse header questions\n"); + break; + } + + uint16_t type = _mdns_read_u16(content, MDNS_TYPE_OFFSET); + uint16_t mdns_class = _mdns_read_u16(content, MDNS_CLASS_OFFSET); + bool unicast = !!(mdns_class & 0x8000); + mdns_class &= 0x7FFF; + content = content + 4; + + _mdns_dbg_printf(" Q: "); + if (unicast) { + _mdns_dbg_printf("*U* "); + } + if (type == MDNS_TYPE_PTR) { + _mdns_dbg_printf("%s.%s%s.%s.%s. PTR ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain); + } else if (type == MDNS_TYPE_SRV) { + _mdns_dbg_printf("%s.%s%s.%s.%s. SRV ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain); + } else if (type == MDNS_TYPE_TXT) { + _mdns_dbg_printf("%s.%s%s.%s.%s. TXT ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain); + } else if (type == MDNS_TYPE_A) { + _mdns_dbg_printf("%s.%s. A ", name->host, name->domain); + } else if (type == MDNS_TYPE_AAAA) { + _mdns_dbg_printf("%s.%s. AAAA ", name->host, name->domain); + } else if (type == MDNS_TYPE_NSEC) { + _mdns_dbg_printf("%s.%s%s.%s.%s. NSEC ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain); + } else if (type == MDNS_TYPE_ANY) { + _mdns_dbg_printf("%s.%s%s.%s.%s. ANY ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain); + } else { + _mdns_dbg_printf("%s.%s%s.%s.%s. %04X ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain, type); + } + + if (mdns_class == 0x0001) { + _mdns_dbg_printf("IN"); + } else { + _mdns_dbg_printf("%04X", mdns_class); + } + _mdns_dbg_printf("\n"); + } + } + + if (header.answers || header.servers || header.additional) { + uint16_t recordIndex = 0; + + while (content < (data + len)) { + + content = _mdns_parse_fqdn(data, content, name, len); + if (!content) { + _mdns_dbg_printf("ERROR: parse mdns records\n"); + break; + } + + uint16_t type = _mdns_read_u16(content, MDNS_TYPE_OFFSET); + uint16_t mdns_class = _mdns_read_u16(content, MDNS_CLASS_OFFSET); + uint32_t ttl = _mdns_read_u32(content, MDNS_TTL_OFFSET); + uint16_t data_len = _mdns_read_u16(content, MDNS_LEN_OFFSET); + const uint8_t *data_ptr = content + MDNS_DATA_OFFSET; + bool flush = !!(mdns_class & 0x8000); + mdns_class &= 0x7FFF; + + content = data_ptr + data_len; + if (content > (data + len)) { + _mdns_dbg_printf("ERROR: content length overflow\n"); + break; + } + + mdns_parsed_record_type_t record_type = MDNS_ANSWER; + + if (recordIndex >= (header.answers + header.servers)) { + record_type = MDNS_EXTRA; + } else if (recordIndex >= (header.answers)) { + record_type = MDNS_NS; + } + recordIndex++; + + if (record_type == MDNS_EXTRA) { + _mdns_dbg_printf(" X"); + } else if (record_type == MDNS_NS) { + _mdns_dbg_printf(" S"); + } else { + _mdns_dbg_printf(" A"); + } + + if (type == MDNS_TYPE_PTR) { + _mdns_dbg_printf(": %s%s%s.%s.%s. PTR ", name->host, name->host[0] ? "." : "", name->service, name->proto, name->domain); + } else if (type == MDNS_TYPE_SRV) { + _mdns_dbg_printf(": %s.%s.%s.%s. SRV ", name->host, name->service, name->proto, name->domain); + } else if (type == MDNS_TYPE_TXT) { + _mdns_dbg_printf(": %s.%s.%s.%s. TXT ", name->host, name->service, name->proto, name->domain); + } else if (type == MDNS_TYPE_A) { + _mdns_dbg_printf(": %s.%s. A ", name->host, name->domain); + } else if (type == MDNS_TYPE_AAAA) { + _mdns_dbg_printf(": %s.%s. AAAA ", name->host, name->domain); + } else if (type == MDNS_TYPE_NSEC) { + _mdns_dbg_printf(": %s.%s.%s.%s. NSEC ", name->host, name->service, name->proto, name->domain); + } else if (type == MDNS_TYPE_ANY) { + _mdns_dbg_printf(": %s.%s.%s.%s. ANY ", name->host, name->service, name->proto, name->domain); + } else if (type == MDNS_TYPE_OPT) { + _mdns_dbg_printf(": . OPT "); + } else { + _mdns_dbg_printf(": %s.%s.%s.%s. %04X ", name->host, name->service, name->proto, name->domain, type); + } + + if (mdns_class == 0x0001) { + _mdns_dbg_printf("IN "); + } else { + _mdns_dbg_printf("%04X ", mdns_class); + } + if (flush) { + _mdns_dbg_printf("FLUSH "); + } + _mdns_dbg_printf("%" PRIu32, ttl); + _mdns_dbg_printf("[%u] ", data_len); + if (type == MDNS_TYPE_PTR) { + if (!_mdns_parse_fqdn(data, data_ptr, name, len)) { + _mdns_dbg_printf("ERROR: parse PTR\n"); + continue; + } + _mdns_dbg_printf("%s.%s.%s.%s.\n", name->host, name->service, name->proto, name->domain); + } else if (type == MDNS_TYPE_SRV) { + if (!_mdns_parse_fqdn(data, data_ptr + MDNS_SRV_FQDN_OFFSET, name, len)) { + _mdns_dbg_printf("ERROR: parse SRV\n"); + continue; + } + uint16_t priority = _mdns_read_u16(data_ptr, MDNS_SRV_PRIORITY_OFFSET); + uint16_t weight = _mdns_read_u16(data_ptr, MDNS_SRV_WEIGHT_OFFSET); + uint16_t port = _mdns_read_u16(data_ptr, MDNS_SRV_PORT_OFFSET); + _mdns_dbg_printf("%u %u %u %s.%s.\n", priority, weight, port, name->host, name->domain); + } else if (type == MDNS_TYPE_TXT) { + uint16_t i = 0, y; + while (i < data_len) { + uint8_t partLen = data_ptr[i++]; + if ((i + partLen) > data_len) { + _mdns_dbg_printf("ERROR: parse TXT\n"); + break; + } + char txt[partLen + 1]; + for (y = 0; y < partLen; y++) { + char d = data_ptr[i++]; + txt[y] = d; + } + txt[partLen] = 0; + _mdns_dbg_printf("%s", txt); + if (i < data_len) { + _mdns_dbg_printf("; "); + } + } + _mdns_dbg_printf("\n"); + } else if (type == MDNS_TYPE_AAAA) { + esp_ip6_addr_t ip6; + memcpy(&ip6, data_ptr, sizeof(esp_ip6_addr_t)); + _mdns_dbg_printf(IPV6STR "\n", IPV62STR(ip6)); + } else if (type == MDNS_TYPE_A) { + esp_ip4_addr_t ip; + memcpy(&ip, data_ptr, sizeof(esp_ip4_addr_t)); + _mdns_dbg_printf(IPSTR "\n", IP2STR(&ip)); + } else if (type == MDNS_TYPE_NSEC) { + const uint8_t *old_ptr = data_ptr; + const uint8_t *new_ptr = _mdns_parse_fqdn(data, data_ptr, name, len); + if (new_ptr) { + _mdns_dbg_printf("%s.%s.%s.%s. ", name->host, name->service, name->proto, name->domain); + size_t diff = new_ptr - old_ptr; + data_len -= diff; + data_ptr = new_ptr; + } + size_t i; + for (i = 0; i < data_len; i++) { + _mdns_dbg_printf(" %02x", data_ptr[i]); + } + _mdns_dbg_printf("\n"); + } else if (type == MDNS_TYPE_OPT) { + uint16_t opCode = _mdns_read_u16(data_ptr, 0); + uint16_t opLen = _mdns_read_u16(data_ptr, 2); + _mdns_dbg_printf(" Code: %04x Data[%u]:", opCode, opLen); + size_t i; + for (i = 4; i < data_len; i++) { + _mdns_dbg_printf(" %02x", data_ptr[i]); + } + _mdns_dbg_printf("\n"); + } else { + size_t i; + for (i = 0; i < data_len; i++) { + _mdns_dbg_printf(" %02x", data_ptr[i]); + } + _mdns_dbg_printf("\n"); + } + } + } +} +#endif /* MDNS_ENABLE_DEBUG */ + +/** + * @brief Browse sync result action + */ +static esp_err_t _mdns_sync_browse_action(mdns_action_type_t type, mdns_browse_sync_t *browse_sync) +{ + mdns_action_t *action = NULL; + + action = (mdns_action_t *)malloc(sizeof(mdns_action_t)); + if (!action) { + HOOK_MALLOC_FAILED; + return ESP_ERR_NO_MEM; + } + + action->type = type; + action->data.browse_sync.browse_sync = browse_sync; + if (xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0) != pdPASS) { + free(action); + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +/** + * @brief Browse action + */ +static esp_err_t _mdns_send_browse_action(mdns_action_type_t type, mdns_browse_t *browse) +{ + mdns_action_t *action = NULL; + + action = (mdns_action_t *)malloc(sizeof(mdns_action_t)); + + if (!action) { + HOOK_MALLOC_FAILED; + return ESP_ERR_NO_MEM; + } + + action->type = type; + action->data.browse_add.browse = browse; + if (xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0) != pdPASS) { + free(action); + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +/** + * @brief Free a browse item (Not free the list). + */ +static void _mdns_browse_item_free(mdns_browse_t *browse) +{ + free(browse->service); + free(browse->proto); + if (browse->result) { + _mdns_query_results_free(browse->result); + } + free(browse); +} + +/** + * @brief Allocate new browse structure + */ +static mdns_browse_t *_mdns_browse_init(const char *service, const char *proto, mdns_browse_notify_t notifier) +{ + mdns_browse_t *browse = (mdns_browse_t *)malloc(sizeof(mdns_browse_t)); + + if (!browse) { + HOOK_MALLOC_FAILED; + return NULL; + } + memset(browse, 0, sizeof(mdns_browse_t)); + + browse->state = BROWSE_INIT; + if (!_str_null_or_empty(service)) { + browse->service = strndup(service, MDNS_NAME_BUF_LEN - 1); + if (!browse->service) { + _mdns_browse_item_free(browse); + return NULL; + } + } + + if (!_str_null_or_empty(proto)) { + browse->proto = strndup(proto, MDNS_NAME_BUF_LEN - 1); + if (!browse->proto) { + _mdns_browse_item_free(browse); + return NULL; + } + } + + browse->notifier = notifier; + return browse; +} + +mdns_browse_t *mdns_browse_new(const char *service, const char *proto, mdns_browse_notify_t notifier) +{ + mdns_browse_t *browse = NULL; + + if (!_mdns_server || _str_null_or_empty(service) || _str_null_or_empty(proto)) { + return NULL; + } + + browse = _mdns_browse_init(service, proto, notifier); + if (!browse) { + return NULL; + } + + if (_mdns_send_browse_action(ACTION_BROWSE_ADD, browse)) { + _mdns_browse_item_free(browse); + return NULL; + } + + return browse; +} + +esp_err_t mdns_browse_delete(const char *service, const char *proto) +{ + mdns_browse_t *browse = NULL; + + if (!_mdns_server || _str_null_or_empty(service) || _str_null_or_empty(proto)) { + return ESP_FAIL; + } + + browse = _mdns_browse_init(service, proto, NULL); + if (!browse) { + return ESP_ERR_NO_MEM; + } + + if (_mdns_send_browse_action(ACTION_BROWSE_END, browse)) { + _mdns_browse_item_free(browse); + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +/** + * @brief Mark browse as finished, remove and free it from browse chain + */ +static void _mdns_browse_finish(mdns_browse_t *browse) +{ + browse->state = BROWSE_OFF; + mdns_browse_t *b = _mdns_server->browse; + mdns_browse_t *target_free = NULL; + while (b) { + if (strlen(b->service) == strlen(browse->service) && memcmp(b->service, browse->service, strlen(b->service)) == 0 && + strlen(b->proto) == strlen(browse->proto) && memcmp(b->proto, browse->proto, strlen(b->proto)) == 0) { + target_free = b; + b = b->next; + queueDetach(mdns_browse_t, _mdns_server->browse, target_free); + _mdns_browse_item_free(target_free); + } else { + b = b->next; + } + } + _mdns_browse_item_free(browse); +} + +/** + * @brief Add new browse to the browse chain + */ +static void _mdns_browse_add(mdns_browse_t *browse) +{ + browse->state = BROWSE_RUNNING; + mdns_browse_t *queue = _mdns_server->browse; + bool found = false; + // looking for this browse in active browses + while (queue) { + if (strlen(queue->service) == strlen(browse->service) && memcmp(queue->service, browse->service, strlen(queue->service)) == 0 && + strlen(queue->proto) == strlen(browse->proto) && memcmp(queue->proto, browse->proto, strlen(queue->proto)) == 0) { + found = true; + break; + } + queue = queue->next; + } + if (!found) { + browse->next = _mdns_server->browse; + _mdns_server->browse = browse; + } + for (uint8_t interface_idx = 0; interface_idx < MDNS_MAX_INTERFACES; interface_idx++) { + _mdns_browse_send(browse, (mdns_if_t)interface_idx); + } + if (found) { + _mdns_browse_item_free(browse); + } +} + +/** + * @brief Send PTR query packet to all available interfaces for browsing. + */ +static void _mdns_browse_send(mdns_browse_t *browse, mdns_if_t interface) +{ + // Using search once for sending the PTR query + mdns_search_once_t search = {0}; + + search.instance = NULL; + search.service = browse->service; + search.proto = browse->proto; + search.type = MDNS_TYPE_PTR; + search.unicast = false; + search.result = NULL; + search.next = NULL; + + for (uint8_t protocol_idx = 0; protocol_idx < MDNS_IP_PROTOCOL_MAX; protocol_idx++) { + _mdns_search_send_pcb(&search, interface, (mdns_ip_protocol_t)protocol_idx); + } +} + +/** + * @brief Add result to browse, only add when the result is a new one. + */ +static esp_err_t _mdns_add_browse_result(mdns_browse_sync_t *sync_browse, mdns_result_t *r) +{ + mdns_browse_result_sync_t *sync_r = sync_browse->sync_result; + while (sync_r) { + if (sync_r->result == r) { + break; + } + sync_r = sync_r->next; + } + if (!sync_r) { + // Do not find, need to add the result to the list + mdns_browse_result_sync_t *new = (mdns_browse_result_sync_t *)malloc(sizeof(mdns_browse_result_sync_t)); + + if (!new) { + HOOK_MALLOC_FAILED; + return ESP_ERR_NO_MEM; + } + new->result = r; + new->next = sync_browse->sync_result; + sync_browse->sync_result = new; + } + return ESP_OK; +} + +/** + * @brief Called from parser to add A/AAAA data to search result + */ +static void _mdns_browse_result_add_ip(mdns_browse_t *browse, const char *hostname, esp_ip_addr_t *ip, + mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl, mdns_browse_sync_t *out_sync_browse) +{ + if (out_sync_browse->browse == NULL) { + return; + } else { + if (out_sync_browse->browse != browse) { + return; + } + } + mdns_result_t *r = NULL; + mdns_ip_addr_t *r_a = NULL; + if (browse) { + r = browse->result; + while (r) { + if (r->ip_protocol == ip_protocol) { + // Find the target result in browse result. + if (r->esp_netif == _mdns_get_esp_netif(tcpip_if) && !_str_null_or_empty(r->hostname) && !strcasecmp(hostname, r->hostname)) { + r_a = r->addr; + // Check if the address has already added in result. + while (r_a) { +#ifdef CONFIG_LWIP_IPV4 + if (r_a->addr.type == ip->type && r_a->addr.type == ESP_IPADDR_TYPE_V4 && r_a->addr.u_addr.ip4.addr == ip->u_addr.ip4.addr) { + break; + } +#endif +#ifdef CONFIG_LWIP_IPV6 + if (r_a->addr.type == ip->type && r_a->addr.type == ESP_IPADDR_TYPE_V6 && !memcmp(r_a->addr.u_addr.ip6.addr, ip->u_addr.ip6.addr, 16)) { + break; + } +#endif + r_a = r_a->next; + } + if (!r_a) { + // The current IP is a new one, add it to the link list. + mdns_ip_addr_t *a = NULL; + a = _mdns_result_addr_create_ip(ip); + if (!a) { + return; + } + a->next = r->addr; + r->addr = a; + if (r->ttl != ttl) { + if (r->ttl == 0) { + r->ttl = ttl; + } else { + _mdns_result_update_ttl(r, ttl); + } + } + if (_mdns_add_browse_result(out_sync_browse, r) != ESP_OK) { + return; + } + break; + } + } + } + r = r->next; + } + } + return; +} + +/** + * @brief Called from packet parser to find matching running search + */ +static mdns_browse_t *_mdns_browse_find_from(mdns_browse_t *b, mdns_name_t *name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + // For browse, we only care about the SRV, TXT, A and AAAA + if (type != MDNS_TYPE_SRV && type != MDNS_TYPE_A && type != MDNS_TYPE_AAAA && type != MDNS_TYPE_TXT) { + return NULL; + } + mdns_result_t *r = NULL; + while (b) { + if (type == MDNS_TYPE_SRV || type == MDNS_TYPE_TXT) { + if (strcasecmp(name->service, b->service) + || strcasecmp(name->proto, b->proto)) { + b = b->next; + continue; + } + return b; + } else if (type == MDNS_TYPE_A || type == MDNS_TYPE_AAAA) { + r = b->result; + while (r) { + if (r->esp_netif == _mdns_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !_str_null_or_empty(r->hostname) && !strcasecmp(name->host, r->hostname)) { + return b; + } + r = r->next; + } + b = b->next; + continue; + } + } + return b; +} + +static bool is_txt_item_in_list(mdns_txt_item_t txt, uint8_t txt_value_len, mdns_txt_item_t *txt_list, uint8_t *txt_value_len_list, size_t txt_count) +{ + for (size_t i = 0; i < txt_count; i++) { + if (strcmp(txt.key, txt_list[i].key) == 0) { + if (txt_value_len == txt_value_len_list[i] && memcmp(txt.value, txt_list[i].value, txt_value_len) == 0) { + return true; + } else { + // The key value is unique, so there is no need to continue searching. + return false; + } + } + } + return false; +} + +/** + * @brief Called from parser to add TXT data to search result + */ +static void _mdns_browse_result_add_txt(mdns_browse_t *browse, const char *instance, const char *service, const char *proto, + mdns_txt_item_t *txt, uint8_t *txt_value_len, size_t txt_count, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, + uint32_t ttl, mdns_browse_sync_t *out_sync_browse) +{ + if (out_sync_browse->browse == NULL) { + return; + } else { + if (out_sync_browse->browse != browse) { + return; + } + } + mdns_result_t *r = browse->result; + while (r) { + if (r->esp_netif == _mdns_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && + !_str_null_or_empty(r->instance_name) && !strcasecmp(instance, r->instance_name) && + !_str_null_or_empty(r->service_type) && !strcasecmp(service, r->service_type) && + !_str_null_or_empty(r->proto) && !strcasecmp(proto, r->proto)) { + bool should_update = false; + if (r->txt) { + // Check if txt changed + if (txt_count != r->txt_count) { + should_update = true; + } else { + for (size_t txt_index = 0; txt_index < txt_count; txt_index++) { + if (!is_txt_item_in_list(txt[txt_index], txt_value_len[txt_index], r->txt, r->txt_value_len, r->txt_count)) { + should_update = true; + break; + } + } + } + // If the result has a previous txt entry, we delete it and re-add. + for (size_t i = 0; i < r->txt_count; i++) { + free((char *)(r->txt[i].key)); + free((char *)(r->txt[i].value)); + } + free(r->txt); + free(r->txt_value_len); + } + r->txt = txt; + r->txt_value_len = txt_value_len; + r->txt_count = txt_count; + if (r->ttl != ttl) { + uint32_t previous_ttl = r->ttl; + if (r->ttl == 0) { + r->ttl = ttl; + } else { + _mdns_result_update_ttl(r, ttl); + } + if (previous_ttl != r->ttl) { + should_update = true; + } + } + if (should_update) { + if (_mdns_add_browse_result(out_sync_browse, r) != ESP_OK) { + return; + } + } + return; + } + r = r->next; + } + r = (mdns_result_t *)malloc(sizeof(mdns_result_t)); + if (!r) { + HOOK_MALLOC_FAILED; + goto free_txt; + } + memset(r, 0, sizeof(mdns_result_t)); + r->instance_name = strdup(instance); + r->service_type = strdup(service); + r->proto = strdup(proto); + if (!r->instance_name || !r->service_type || !r->proto) { + free(r->instance_name); + free(r->service_type); + free(r->proto); + free(r); + return; + } + r->txt = txt; + r->txt_value_len = txt_value_len; + r->txt_count = txt_count; + r->esp_netif = _mdns_get_esp_netif(tcpip_if); + r->ip_protocol = ip_protocol; + r->ttl = ttl; + r->next = browse->result; + browse->result = r; + _mdns_add_browse_result(out_sync_browse, r); + return; + +free_txt: + for (size_t i = 0; i < txt_count; i++) { + free((char *)(txt[i].key)); + free((char *)(txt[i].value)); + } + free(txt); + free(txt_value_len); + return; +} + +static esp_err_t _mdns_copy_address_in_previous_result(mdns_result_t *result_list, mdns_result_t *r) +{ + while (result_list) { + if (!_str_null_or_empty(result_list->hostname) && !_str_null_or_empty(r->hostname) && !strcasecmp(result_list->hostname, r->hostname) && + result_list->ip_protocol == r->ip_protocol && result_list->addr && !r->addr) { + // If there is a same hostname in previous result, we need to copy the address here. + r->addr = copy_address_list(result_list->addr); + if (!r->addr) { + return ESP_ERR_NO_MEM; + } + break; + } else { + result_list = result_list->next; + } + } + return ESP_OK; +} + +/** + * @brief Called from parser to add SRV data to search result + */ +static void _mdns_browse_result_add_srv(mdns_browse_t *browse, const char *hostname, const char *instance, const char *service, const char *proto, + uint16_t port, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl, mdns_browse_sync_t *out_sync_browse) +{ + if (out_sync_browse->browse == NULL) { + return; + } else { + if (out_sync_browse->browse != browse) { + return; + } + } + mdns_result_t *r = browse->result; + while (r) { + if (r->esp_netif == _mdns_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && + !_str_null_or_empty(r->instance_name) && !strcasecmp(instance, r->instance_name) && + !_str_null_or_empty(r->service_type) && !strcasecmp(service, r->service_type) && + !_str_null_or_empty(r->proto) && !strcasecmp(proto, r->proto)) { + if (_str_null_or_empty(r->hostname) || strcasecmp(hostname, r->hostname)) { + r->hostname = strdup(hostname); + r->port = port; + if (!r->hostname) { + HOOK_MALLOC_FAILED; + return; + } + if (!r->addr) { + esp_err_t err = _mdns_copy_address_in_previous_result(browse->result, r); + if (err == ESP_ERR_NO_MEM) { + return; + } + } + if (_mdns_add_browse_result(out_sync_browse, r) != ESP_OK) { + return; + } + } + if (r->ttl != ttl) { + uint32_t previous_ttl = r->ttl; + if (r->ttl == 0) { + r->ttl = ttl; + } else { + _mdns_result_update_ttl(r, ttl); + } + if (previous_ttl != r->ttl) { + if (_mdns_add_browse_result(out_sync_browse, r) != ESP_OK) { + return; + } + } + } + return; + } + r = r->next; + } + r = (mdns_result_t *)malloc(sizeof(mdns_result_t)); + if (!r) { + HOOK_MALLOC_FAILED; + return; + } + + memset(r, 0, sizeof(mdns_result_t)); + r->hostname = strdup(hostname); + r->instance_name = strdup(instance); + r->service_type = strdup(service); + r->proto = strdup(proto); + if (!r->hostname || !r->instance_name || !r->service_type || !r->proto) { + HOOK_MALLOC_FAILED; + free(r->hostname); + free(r->instance_name); + free(r->service_type); + free(r->proto); + free(r); + return; + } + r->port = port; + r->esp_netif = _mdns_get_esp_netif(tcpip_if); + r->ip_protocol = ip_protocol; + r->ttl = ttl; + r->next = browse->result; + browse->result = r; + _mdns_add_browse_result(out_sync_browse, r); + return; +} + +static void _mdns_browse_sync(mdns_browse_sync_t *browse_sync) +{ + mdns_browse_t *browse = browse_sync->browse; + mdns_browse_result_sync_t *sync_result = browse_sync->sync_result; + while (sync_result) { + mdns_result_t *result = sync_result->result; +#ifdef MDNS_ENABLE_DEBUG + debug_printf_browse_result(result, browse_sync->browse); +#endif + browse->notifier(result); + if (result->ttl == 0) { + queueDetach(mdns_result_t, browse->result, result); + // Just free current result + result->next = NULL; + mdns_query_results_free(result); + } + sync_result = sync_result->next; + } +} + +#ifdef MDNS_ENABLE_DEBUG +void _debug_printf_result(mdns_result_t *r_t) +{ + mdns_ip_addr_t *r_a = NULL; + int addr_count = 0; + _mdns_dbg_printf("result esp_netif: %p\n", r_t->esp_netif); + _mdns_dbg_printf("result ip_protocol: %d\n", r_t->ip_protocol); + _mdns_dbg_printf("result hostname: %s\n", _str_null_or_empty(r_t->hostname) ? "NULL" : r_t->hostname); + _mdns_dbg_printf("result instance_name: %s\n", _str_null_or_empty(r_t->instance_name) ? "NULL" : r_t->instance_name); + _mdns_dbg_printf("result service_type: %s\n", _str_null_or_empty(r_t->service_type) ? "NULL" : r_t->service_type); + _mdns_dbg_printf("result proto: %s\n", _str_null_or_empty(r_t->proto) ? "NULL" : r_t->proto); + _mdns_dbg_printf("result port: %d\n", r_t->port); + _mdns_dbg_printf("result ttl: %" PRIu32 "\n", r_t->ttl); + for (int i = 0; i < r_t->txt_count; i++) { + _mdns_dbg_printf("result txt item%d, key: %s, value: %s\n", i, r_t->txt[i].key, r_t->txt[i].value); + } + r_a = r_t->addr; + while (r_a) { +#ifdef CONFIG_LWIP_IPV4 + if (r_a->addr.type == ESP_IPADDR_TYPE_V4) { + _mdns_dbg_printf("Addr%d: " IPSTR "\n", addr_count++, IP2STR(&r_a->addr.u_addr.ip4)); + } +#endif +#ifdef CONFIG_LWIP_IPV6 + if (r_a->addr.type == ESP_IPADDR_TYPE_V6) { + _mdns_dbg_printf("Addr%d: " IPV6STR "\n", addr_count++, IPV62STR(r_a->addr.u_addr.ip6)); + } +#endif + r_a = r_a->next; + } +} + +static void debug_printf_browse_result(mdns_result_t *r_t, mdns_browse_t *b_t) +{ + _mdns_dbg_printf("----------------sync browse %s.%s result---------------\n", b_t->service, b_t->proto); + _mdns_dbg_printf("browse pointer: %p\n", b_t); + _debug_printf_result(r_t); +} + +static void debug_printf_browse_result_all(mdns_result_t *r_t) +{ + int count = 0; + while (r_t) { + _mdns_dbg_printf("----------------result %d---------------\n", count++); + _debug_printf_result(r_t); + r_t = r_t->next; + } +} +#endif // MDNS_ENABLE_DEBUG diff --git a/code/components/mdns/mdns_console.c b/code/components/mdns/mdns_console.c new file mode 100644 index 00000000..80f009a1 --- /dev/null +++ b/code/components/mdns/mdns_console.c @@ -0,0 +1,1451 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "esp_console.h" +#include "argtable3/argtable3.h" +#include "mdns.h" +#include "mdns_private.h" +#include "inttypes.h" + +static const char *ip_protocol_str[] = {"V4", "V6", "MAX"}; + +static void mdns_print_results(mdns_result_t *results) +{ + mdns_result_t *r = results; + mdns_ip_addr_t *a = NULL; + int i = 1; + while (r) { + if (r->esp_netif) { + printf("%d: Interface: %s, Type: %s, TTL: %" PRIu32 "\n", i++, esp_netif_get_ifkey(r->esp_netif), + ip_protocol_str[r->ip_protocol], r->ttl); + } + if (r->instance_name) { + printf(" PTR : %s\n", r->instance_name); + } + if (r->hostname) { + printf(" SRV : %s.local:%u\n", r->hostname, r->port); + } + if (r->txt_count) { + printf(" TXT : [%u] ", (int)r->txt_count); + for (size_t t = 0; t < r->txt_count; t++) { + printf("%s=%s; ", r->txt[t].key, r->txt[t].value); + } + printf("\n"); + } + a = r->addr; + while (a) { + if (a->addr.type == ESP_IPADDR_TYPE_V6) { + printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6)); + } else { + printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4))); + } + a = a->next; + } + r = r->next; + } +} + +static struct { + struct arg_str *hostname; + struct arg_int *timeout; + struct arg_end *end; +} mdns_query_a_args; + +#ifdef CONFIG_LWIP_IPV4 +static int cmd_mdns_query_a(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_query_a_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_query_a_args.end, argv[0]); + return 1; + } + + const char *hostname = mdns_query_a_args.hostname->sval[0]; + int timeout = mdns_query_a_args.timeout->ival[0]; + + if (!hostname || !hostname[0]) { + printf("ERROR: Hostname not supplied\n"); + return 1; + } + + if (timeout <= 0) { + timeout = 1000; + } + + printf("Query A: %s.local, Timeout: %d\n", hostname, timeout); + + struct esp_ip4_addr addr; + addr.addr = 0; + + esp_err_t err = mdns_query_a(hostname, timeout, &addr); + if (err) { + if (err == ESP_ERR_NOT_FOUND) { + printf("ERROR: Host was not found!\n"); + return 0; + } + printf("ERROR: Query Failed\n"); + return 1; + } + + printf(IPSTR "\n", IP2STR(&addr)); + + return 0; +} + +static void register_mdns_query_a(void) +{ + mdns_query_a_args.hostname = arg_str1(NULL, NULL, "", "Hostname that is searched for"); + mdns_query_a_args.timeout = arg_int0("t", "timeout", "", "Timeout for this query"); + mdns_query_a_args.end = arg_end(2); + + const esp_console_cmd_t cmd_init = { + .command = "mdns_query_a", + .help = "Query MDNS for IPv4", + .hint = NULL, + .func = &cmd_mdns_query_a, + .argtable = &mdns_query_a_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) ); +} +#endif /* CONFIG_LWIP_IPV4 */ + +#ifdef CONFIG_LWIP_IPV6 +static int cmd_mdns_query_aaaa(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_query_a_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_query_a_args.end, argv[0]); + return 1; + } + + const char *hostname = mdns_query_a_args.hostname->sval[0]; + int timeout = mdns_query_a_args.timeout->ival[0]; + + if (!hostname || !hostname[0]) { + printf("ERROR: Hostname not supplied\n"); + return 1; + } + + if (timeout <= 0) { + timeout = 1000; + } + + printf("Query AAAA: %s.local, Timeout: %d\n", hostname, timeout); + + struct esp_ip6_addr addr; + memset(addr.addr, 0, 16); + + esp_err_t err = mdns_query_aaaa(hostname, timeout, &addr); + if (err) { + if (err == ESP_ERR_NOT_FOUND) { + printf("Host was not found!\n"); + return 0; + } + printf("ERROR: Query Failed\n"); + return 1; + } + + printf(IPV6STR "\n", IPV62STR(addr)); + + return 0; +} + +static void register_mdns_query_aaaa(void) +{ + mdns_query_a_args.hostname = arg_str1(NULL, NULL, "", "Hostname that is searched for"); + mdns_query_a_args.timeout = arg_int0("t", "timeout", "", "Timeout for this query"); + mdns_query_a_args.end = arg_end(2); + + const esp_console_cmd_t cmd_init = { + .command = "mdns_query_aaaa", + .help = "Query MDNS for IPv6", + .hint = NULL, + .func = &cmd_mdns_query_aaaa, + .argtable = &mdns_query_a_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) ); +} +#endif /* CONFIG_LWIP_IPV6 */ + +static struct { + struct arg_str *instance; + struct arg_str *service; + struct arg_str *proto; + struct arg_int *timeout; + struct arg_end *end; +} mdns_query_srv_args; + +static int cmd_mdns_query_srv(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_query_srv_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_query_srv_args.end, argv[0]); + return 1; + } + + const char *instance = mdns_query_srv_args.instance->sval[0]; + const char *service = mdns_query_srv_args.service->sval[0]; + const char *proto = mdns_query_srv_args.proto->sval[0]; + int timeout = mdns_query_srv_args.timeout->ival[0]; + + if (timeout <= 0) { + timeout = 1000; + } + + printf("Query SRV: %s.%s.%s.local, Timeout: %d\n", instance, service, proto, timeout); + + mdns_result_t *results = NULL; + esp_err_t err = mdns_query_srv(instance, service, proto, timeout, &results); + if (err) { + printf("ERROR: Query Failed\n"); + return 1; + } + if (!results) { + printf("No results found!\n"); + return 0; + } + mdns_print_results(results); + mdns_query_results_free(results); + return 0; +} + +static void register_mdns_query_srv(void) +{ + mdns_query_srv_args.instance = arg_str1(NULL, NULL, "", "Instance to search for"); + mdns_query_srv_args.service = arg_str1(NULL, NULL, "", "Service to search for (ex. _http, _smb, etc.)"); + mdns_query_srv_args.proto = arg_str1(NULL, NULL, "", "Protocol to search for (_tcp, _udp, etc.)"); + mdns_query_srv_args.timeout = arg_int0("t", "timeout", "", "Timeout for this query"); + mdns_query_srv_args.end = arg_end(2); + + const esp_console_cmd_t cmd_init = { + .command = "mdns_query_srv", + .help = "Query MDNS for Service SRV", + .hint = NULL, + .func = &cmd_mdns_query_srv, + .argtable = &mdns_query_srv_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) ); +} + +static struct { + struct arg_str *instance; + struct arg_str *service; + struct arg_str *proto; + struct arg_int *timeout; + struct arg_end *end; +} mdns_query_txt_args; + +static int cmd_mdns_query_txt(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_query_txt_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_query_txt_args.end, argv[0]); + return 1; + } + + const char *instance = mdns_query_txt_args.instance->sval[0]; + const char *service = mdns_query_txt_args.service->sval[0]; + const char *proto = mdns_query_txt_args.proto->sval[0]; + int timeout = mdns_query_txt_args.timeout->ival[0]; + + printf("Query TXT: %s.%s.%s.local, Timeout: %d\n", instance, service, proto, timeout); + + if (timeout <= 0) { + timeout = 5000; + } + + mdns_result_t *results = NULL; + esp_err_t err = mdns_query_txt(instance, service, proto, timeout, &results); + if (err) { + printf("ERROR: Query Failed\n"); + return 1; + } + if (!results) { + printf("No results found!\n"); + return 0; + } + + mdns_print_results(results); + mdns_query_results_free(results); + return 0; +} + +static void register_mdns_query_txt(void) +{ + mdns_query_txt_args.instance = arg_str1(NULL, NULL, "", "Instance to search for"); + mdns_query_txt_args.service = arg_str1(NULL, NULL, "", "Service to search for (ex. _http, _smb, etc.)"); + mdns_query_txt_args.proto = arg_str1(NULL, NULL, "", "Protocol to search for (_tcp, _udp, etc.)"); + mdns_query_txt_args.timeout = arg_int0("t", "timeout", "", "Timeout for this query"); + mdns_query_txt_args.end = arg_end(2); + + const esp_console_cmd_t cmd_init = { + .command = "mdns_query_txt", + .help = "Query MDNS for Service TXT", + .hint = NULL, + .func = &cmd_mdns_query_txt, + .argtable = &mdns_query_txt_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) ); +} + +static struct { + struct arg_str *service; + struct arg_str *proto; + struct arg_int *timeout; + struct arg_int *max_results; + struct arg_end *end; +} mdns_query_ptr_args; + +static int cmd_mdns_query_ptr(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_query_ptr_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_query_ptr_args.end, argv[0]); + return 1; + } + + const char *service = mdns_query_ptr_args.service->sval[0]; + const char *proto = mdns_query_ptr_args.proto->sval[0]; + int timeout = mdns_query_ptr_args.timeout->ival[0]; + int max_results = mdns_query_ptr_args.max_results->ival[0]; + + if (timeout <= 0) { + timeout = 5000; + } + + if (max_results <= 0 || max_results > 255) { + max_results = 255; + } + + printf("Query PTR: %s.%s.local, Timeout: %d, Max Results: %d\n", service, proto, timeout, max_results); + + mdns_result_t *results = NULL; + esp_err_t err = mdns_query_ptr(service, proto, timeout, max_results, &results); + if (err) { + printf("ERROR: Query Failed\n"); + return 1; + } + if (!results) { + printf("No results found!\n"); + return 0; + } + + mdns_print_results(results); + mdns_query_results_free(results); + return 0; +} + +static void register_mdns_query_ptr(void) +{ + mdns_query_ptr_args.service = arg_str1(NULL, NULL, "", "Service to search for (ex. _http, _smb, etc.)"); + mdns_query_ptr_args.proto = arg_str1(NULL, NULL, "", "Protocol to search for (_tcp, _udp, etc.)"); + mdns_query_ptr_args.timeout = arg_int0("t", "timeout", "", "Timeout for this query"); + mdns_query_ptr_args.max_results = arg_int0("m", "max_results", "", "Maximum results returned"); + mdns_query_ptr_args.end = arg_end(2); + + const esp_console_cmd_t cmd_init = { + .command = "mdns_query_ptr", + .help = "Query MDNS for Service", + .hint = NULL, + .func = &cmd_mdns_query_ptr, + .argtable = &mdns_query_ptr_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) ); +} + +static struct { + struct arg_str *hostname; + struct arg_int *timeout; + struct arg_int *max_results; + struct arg_end *end; +} mdns_query_ip_args; + +static int cmd_mdns_query_ip(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_query_ip_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_query_ip_args.end, argv[0]); + return 1; + } + + const char *hostname = mdns_query_ip_args.hostname->sval[0]; + int timeout = mdns_query_ip_args.timeout->ival[0]; + int max_results = mdns_query_ip_args.max_results->ival[0]; + + if (!hostname || !hostname[0]) { + printf("ERROR: Hostname not supplied\n"); + return 1; + } + + if (timeout <= 0) { + timeout = 1000; + } + + if (max_results < 0 || max_results > 255) { + max_results = 255; + } + + printf("Query IP: %s.local, Timeout: %d, Max Results: %d\n", hostname, timeout, max_results); + + mdns_result_t *results = NULL; + esp_err_t err = mdns_query(hostname, NULL, NULL, MDNS_TYPE_ANY, timeout, max_results, &results); + if (err) { + printf("ERROR: Query Failed\n"); + return 1; + } + if (!results) { + printf("No results found!\n"); + return 0; + } + mdns_print_results(results); + mdns_query_results_free(results); + + return 0; +} + +static void register_mdns_query_ip(void) +{ + mdns_query_ip_args.hostname = arg_str1(NULL, NULL, "", "Hostname that is searched for"); + mdns_query_ip_args.timeout = arg_int0("t", "timeout", "", "Timeout for this query"); + mdns_query_ip_args.max_results = arg_int0("m", "max_results", "", "Maximum results returned"); + mdns_query_ip_args.end = arg_end(2); + + const esp_console_cmd_t cmd_init = { + .command = "mdns_query_ip", + .help = "Query MDNS for IP", + .hint = NULL, + .func = &cmd_mdns_query_ip, + .argtable = &mdns_query_ip_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) ); +} + +static struct { + struct arg_str *instance; + struct arg_str *service; + struct arg_str *proto; + struct arg_int *timeout; + struct arg_int *max_results; + struct arg_end *end; +} mdns_query_svc_args; + +static int cmd_mdns_query_svc(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_query_svc_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_query_svc_args.end, argv[0]); + return 1; + } + + const char *instance = mdns_query_svc_args.instance->sval[0]; + const char *service = mdns_query_svc_args.service->sval[0]; + const char *proto = mdns_query_svc_args.proto->sval[0]; + int timeout = mdns_query_svc_args.timeout->ival[0]; + int max_results = mdns_query_svc_args.max_results->ival[0]; + + if (timeout <= 0) { + timeout = 5000; + } + + if (max_results < 0 || max_results > 255) { + max_results = 255; + } + + printf("Query SVC: %s.%s.%s.local, Timeout: %d, Max Results: %d\n", instance, service, proto, timeout, max_results); + + mdns_result_t *results = NULL; + esp_err_t err = mdns_query(instance, service, proto, MDNS_TYPE_ANY, timeout, max_results, &results); + if (err) { + printf("ERROR: Query Failed\n"); + return 1; + } + if (!results) { + printf("No results found!\n"); + return 0; + } + + mdns_print_results(results); + mdns_query_results_free(results); + return 0; +} + +static void register_mdns_query_svc(void) +{ + mdns_query_svc_args.instance = arg_str1(NULL, NULL, "", "Instance to search for"); + mdns_query_svc_args.service = arg_str1(NULL, NULL, "", "Service to search for (ex. _http, _smb, etc.)"); + mdns_query_svc_args.proto = arg_str1(NULL, NULL, "", "Protocol to search for (_tcp, _udp, etc.)"); + mdns_query_svc_args.timeout = arg_int0("t", "timeout", "", "Timeout for this query"); + mdns_query_svc_args.max_results = arg_int0("m", "max_results", "", "Maximum results returned"); + mdns_query_svc_args.end = arg_end(2); + + const esp_console_cmd_t cmd_init = { + .command = "mdns_query_svc", + .help = "Query MDNS for Service TXT & SRV", + .hint = NULL, + .func = &cmd_mdns_query_svc, + .argtable = &mdns_query_svc_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) ); +} + +static struct { + struct arg_str *hostname; + struct arg_str *instance; + struct arg_end *end; +} mdns_init_args; + +static int cmd_mdns_init(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_init_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_init_args.end, argv[0]); + return 1; + } + + ESP_ERROR_CHECK( mdns_init() ); + + if (mdns_init_args.hostname->sval[0]) { + ESP_ERROR_CHECK( mdns_hostname_set(mdns_init_args.hostname->sval[0]) ); + printf("MDNS: Hostname: %s\n", mdns_init_args.hostname->sval[0]); + } + + if (mdns_init_args.instance->count) { + ESP_ERROR_CHECK( mdns_instance_name_set(mdns_init_args.instance->sval[0]) ); + printf("MDNS: Instance: %s\n", mdns_init_args.instance->sval[0]); + } + + return 0; +} + +static void register_mdns_init(void) +{ + mdns_init_args.hostname = arg_str0("h", "hostname", "", "Hostname that the server will advertise"); + mdns_init_args.instance = arg_str0("i", "instance", "", "Default instance name for services"); + mdns_init_args.end = arg_end(2); + + const esp_console_cmd_t cmd_init = { + .command = "mdns_init", + .help = "Start MDNS Server", + .hint = NULL, + .func = &cmd_mdns_init, + .argtable = &mdns_init_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) ); +} + +static int cmd_mdns_free(int argc, char **argv) +{ + mdns_free(); + return 0; +} + +static void register_mdns_free(void) +{ + const esp_console_cmd_t cmd_free = { + .command = "mdns_free", + .help = "Stop MDNS Server", + .hint = NULL, + .func = &cmd_mdns_free, + .argtable = NULL + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_free) ); +} + +static struct { + struct arg_str *hostname; + struct arg_end *end; +} mdns_set_hostname_args; + +static int cmd_mdns_set_hostname(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_set_hostname_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_set_hostname_args.end, argv[0]); + return 1; + } + + if (mdns_set_hostname_args.hostname->sval[0] == NULL) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + + ESP_ERROR_CHECK( mdns_hostname_set(mdns_set_hostname_args.hostname->sval[0]) ); + return 0; +} + +static void register_mdns_set_hostname(void) +{ + mdns_set_hostname_args.hostname = arg_str1(NULL, NULL, "", "Hostname that the server will advertise"); + mdns_set_hostname_args.end = arg_end(2); + + const esp_console_cmd_t cmd_set_hostname = { + .command = "mdns_set_hostname", + .help = "Set MDNS Server hostname", + .hint = NULL, + .func = &cmd_mdns_set_hostname, + .argtable = &mdns_set_hostname_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_set_hostname) ); +} + +static struct { + struct arg_str *instance; + struct arg_end *end; +} mdns_set_instance_args; + +static int cmd_mdns_set_instance(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_set_instance_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_set_instance_args.end, argv[0]); + return 1; + } + + if (mdns_set_instance_args.instance->sval[0] == NULL) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + + ESP_ERROR_CHECK( mdns_instance_name_set(mdns_set_instance_args.instance->sval[0]) ); + return 0; +} + +static void register_mdns_set_instance(void) +{ + mdns_set_instance_args.instance = arg_str1(NULL, NULL, "", "Default instance name for services"); + mdns_set_instance_args.end = arg_end(2); + + const esp_console_cmd_t cmd_set_instance = { + .command = "mdns_set_instance", + .help = "Set MDNS Server Istance Name", + .hint = NULL, + .func = &cmd_mdns_set_instance, + .argtable = &mdns_set_instance_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_set_instance) ); +} + +static mdns_txt_item_t *_convert_items(const char **values, int count) +{ + int i = 0, e; + const char *value = NULL; + mdns_txt_item_t *items = (mdns_txt_item_t *) malloc(sizeof(mdns_txt_item_t) * count); + if (!items) { + printf("ERROR: No Memory!\n"); + goto fail; + + } + memset(items, 0, sizeof(mdns_txt_item_t) * count); + + for (i = 0; i < count; i++) { + value = values[i]; + char *esign = strchr(value, '='); + if (!esign) { + printf("ERROR: Equal sign not found in '%s'!\n", value); + goto fail; + } + int var_len = esign - value; + int val_len = strlen(value) - var_len - 1; + char *var = (char *)malloc(var_len + 1); + if (var == NULL) { + printf("ERROR: No Memory!\n"); + goto fail; + } + char *val = (char *)malloc(val_len + 1); + if (val == NULL) { + printf("ERROR: No Memory!\n"); + free(var); + goto fail; + } + memcpy(var, value, var_len); + var[var_len] = 0; + memcpy(val, esign + 1, val_len); + val[val_len] = 0; + + items[i].key = var; + items[i].value = val; + } + + return items; + +fail: + for (e = 0; e < i; e++) { + free((char *)items[e].key); + free((char *)items[e].value); + } + free(items); + return NULL; +} + +static struct { + struct arg_str *service; + struct arg_str *proto; + struct arg_int *port; + struct arg_str *instance; + struct arg_str *host; + struct arg_str *txt; + struct arg_end *end; +} mdns_add_args; + +static int cmd_mdns_service_add(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_add_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_add_args.end, argv[0]); + return 1; + } + + if (!mdns_add_args.service->sval[0] || !mdns_add_args.proto->sval[0] || !mdns_add_args.port->ival[0]) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + const char *instance = NULL; + if (mdns_add_args.instance->sval[0] && mdns_add_args.instance->sval[0][0]) { + instance = mdns_add_args.instance->sval[0]; + printf("MDNS: Service Instance: %s\n", instance); + } + const char *host = NULL; + if (mdns_add_args.host->count && mdns_add_args.host->sval[0]) { + host = mdns_add_args.host->sval[0]; + printf("MDNS: Service for delegated host: %s\n", host); + } + mdns_txt_item_t *items = NULL; + if (mdns_add_args.txt->count) { + items = _convert_items(mdns_add_args.txt->sval, mdns_add_args.txt->count); + if (!items) { + printf("ERROR: No Memory!\n"); + return 1; + + } + } + + ESP_ERROR_CHECK( mdns_service_add_for_host(instance, mdns_add_args.service->sval[0], mdns_add_args.proto->sval[0], + host, mdns_add_args.port->ival[0], items, mdns_add_args.txt->count) ); + free(items); + return 0; +} + +static void register_mdns_service_add(void) +{ + mdns_add_args.service = arg_str1(NULL, NULL, "", "MDNS Service"); + mdns_add_args.proto = arg_str1(NULL, NULL, "", "IP Protocol"); + mdns_add_args.port = arg_int1(NULL, NULL, "", "Service Port"); + mdns_add_args.instance = arg_str0("i", "instance", "", "Instance name"); + mdns_add_args.host = arg_str0("h", "host", "", "Service for this (delegated) host"); + mdns_add_args.txt = arg_strn(NULL, NULL, "item", 0, 30, "TXT Items (name=value)"); + mdns_add_args.end = arg_end(2); + + const esp_console_cmd_t cmd_add = { + .command = "mdns_service_add", + .help = "Add service to MDNS", + .hint = NULL, + .func = &cmd_mdns_service_add, + .argtable = &mdns_add_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_add) ); +} + +static struct { + struct arg_str *instance; + struct arg_str *service; + struct arg_str *proto; + struct arg_str *host; + struct arg_end *end; +} mdns_remove_args; + +static int cmd_mdns_service_remove(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_remove_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_remove_args.end, argv[0]); + return 1; + } + + if (!mdns_remove_args.service->sval[0] || !mdns_remove_args.proto->sval[0]) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + + const char *instance = NULL; + if (mdns_remove_args.instance->count && mdns_remove_args.instance->sval[0]) { + instance = mdns_remove_args.instance->sval[0]; + } + const char *host = NULL; + if (mdns_remove_args.host->count && mdns_remove_args.host->sval[0]) { + host = mdns_remove_args.host->sval[0]; + } + + ESP_ERROR_CHECK( mdns_service_remove_for_host(instance, mdns_remove_args.service->sval[0], mdns_remove_args.proto->sval[0], host) ); + return 0; +} + +static void register_mdns_service_remove(void) +{ + mdns_remove_args.service = arg_str1(NULL, NULL, "", "MDNS Service"); + mdns_remove_args.proto = arg_str1(NULL, NULL, "", "IP Protocol"); + mdns_remove_args.host = arg_str0("h", "host", "", "Service for this (delegated) host"); + mdns_remove_args.instance = arg_str0("i", "instance", "", "Instance name"); + mdns_remove_args.end = arg_end(4); + + const esp_console_cmd_t cmd_remove = { + .command = "mdns_service_remove", + .help = "Remove service from MDNS", + .hint = NULL, + .func = &cmd_mdns_service_remove, + .argtable = &mdns_remove_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_remove) ); +} + +static struct { + struct arg_str *service; + struct arg_str *proto; + struct arg_str *instance; + struct arg_str *host; + struct arg_str *old_instance; + struct arg_end *end; +} mdns_service_instance_set_args; + +static int cmd_mdns_service_instance_set(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_service_instance_set_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_service_instance_set_args.end, argv[0]); + return 1; + } + + if (!mdns_service_instance_set_args.service->sval[0] || !mdns_service_instance_set_args.proto->sval[0] || !mdns_service_instance_set_args.instance->sval[0]) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + const char *host = NULL; + if (mdns_service_instance_set_args.host->count && mdns_service_instance_set_args.host->sval[0]) { + host = mdns_service_instance_set_args.host->sval[0]; + } + const char *old_instance = NULL; + if (mdns_service_instance_set_args.old_instance->count && mdns_service_instance_set_args.old_instance->sval[0]) { + old_instance = mdns_service_instance_set_args.old_instance->sval[0]; + } + esp_err_t err = mdns_service_instance_name_set_for_host(old_instance, mdns_service_instance_set_args.service->sval[0], mdns_service_instance_set_args.proto->sval[0], host, mdns_service_instance_set_args.instance->sval[0]); + if (err != ESP_OK) { + printf("mdns_service_instance_name_set_for_host() failed with %s\n", esp_err_to_name(err)); + return 1; + } + + return 0; +} + +static void register_mdns_service_instance_set(void) +{ + mdns_service_instance_set_args.service = arg_str1(NULL, NULL, "", "MDNS Service"); + mdns_service_instance_set_args.proto = arg_str1(NULL, NULL, "", "IP Protocol"); + mdns_service_instance_set_args.instance = arg_str1(NULL, NULL, "", "Instance name"); + mdns_service_instance_set_args.host = arg_str0("h", "host", "", "Service for this (delegated) host"); + mdns_service_instance_set_args.old_instance = arg_str0("i", "old_instance", "", "Instance name before update"); + mdns_service_instance_set_args.end = arg_end(4); + + const esp_console_cmd_t cmd_add = { + .command = "mdns_service_instance_set", + .help = "Set MDNS Service Instance Name", + .hint = NULL, + .func = &cmd_mdns_service_instance_set, + .argtable = &mdns_service_instance_set_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_add) ); +} + +static struct { + struct arg_str *service; + struct arg_str *proto; + struct arg_int *port; + struct arg_str *host; + struct arg_str *instance; + struct arg_end *end; +} mdns_service_port_set_args; + +static int cmd_mdns_service_port_set(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_service_port_set_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_service_port_set_args.end, argv[0]); + return 1; + } + + if (!mdns_service_port_set_args.service->sval[0] || !mdns_service_port_set_args.proto->sval[0] || !mdns_service_port_set_args.port->ival[0]) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + + const char *host = NULL; + if (mdns_service_port_set_args.host->count && mdns_service_port_set_args.host->sval[0]) { + host = mdns_service_port_set_args.host->sval[0]; + } + const char *instance = NULL; + if (mdns_service_port_set_args.instance->count && mdns_service_port_set_args.instance->sval[0]) { + instance = mdns_service_port_set_args.instance->sval[0]; + } + esp_err_t err = mdns_service_port_set_for_host(instance, mdns_service_port_set_args.service->sval[0], mdns_service_port_set_args.proto->sval[0], host, mdns_service_port_set_args.port->ival[0]); + if (err != ESP_OK) { + printf("mdns_service_port_set_for_host() failed with %s\n", esp_err_to_name(err)); + return 1; + } + return 0; +} + +static void register_mdns_service_port_set(void) +{ + mdns_service_port_set_args.service = arg_str1(NULL, NULL, "", "MDNS Service"); + mdns_service_port_set_args.proto = arg_str1(NULL, NULL, "", "IP Protocol"); + mdns_service_port_set_args.port = arg_int1(NULL, NULL, "", "Service Port"); + mdns_service_port_set_args.host = arg_str0("h", "host", "", "Service for this (delegated) host"); + mdns_service_port_set_args.instance = arg_str0("i", "instance", "", "Instance name"); + mdns_service_port_set_args.end = arg_end(2); + + const esp_console_cmd_t cmd_add = { + .command = "mdns_service_port_set", + .help = "Set MDNS Service port", + .hint = NULL, + .func = &cmd_mdns_service_port_set, + .argtable = &mdns_service_port_set_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_add) ); +} + +static struct { + struct arg_str *service; + struct arg_str *proto; + struct arg_str *instance; + struct arg_str *host; + struct arg_str *txt; + struct arg_end *end; +} mdns_txt_replace_args; + +static int cmd_mdns_service_txt_replace(int argc, char **argv) +{ + mdns_txt_item_t *items = NULL; + int nerrors = arg_parse(argc, argv, (void **) &mdns_txt_replace_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_txt_replace_args.end, argv[0]); + return 1; + } + + if (!mdns_txt_replace_args.service->sval[0] || !mdns_txt_replace_args.proto->sval[0]) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + const char *instance = NULL; + if (mdns_txt_replace_args.instance->count && mdns_txt_replace_args.instance->sval[0]) { + instance = mdns_txt_replace_args.instance->sval[0]; + printf("MDNS: Service Instance: %s\n", instance); + } + const char *host = NULL; + if (mdns_txt_replace_args.host->count && mdns_txt_replace_args.host->sval[0]) { + host = mdns_txt_replace_args.host->sval[0]; + printf("MDNS: Service for delegated host: %s\n", host); + } + if (mdns_txt_replace_args.txt->count) { + items = _convert_items(mdns_txt_replace_args.txt->sval, mdns_txt_replace_args.txt->count); + if (!items) { + printf("ERROR: No Memory!\n"); + return 1; + + } + } + ESP_ERROR_CHECK( mdns_service_txt_set_for_host(instance, mdns_txt_replace_args.service->sval[0], mdns_txt_replace_args.proto->sval[0], host, items, mdns_txt_replace_args.txt->count) ); + free(items); + return 0; +} + +static void register_mdns_service_txt_replace(void) +{ + mdns_txt_replace_args.service = arg_str1(NULL, NULL, "", "MDNS Service"); + mdns_txt_replace_args.proto = arg_str1(NULL, NULL, "", "IP Protocol"); + mdns_txt_replace_args.instance = arg_str0("i", "instance", "", "Instance name"); + mdns_txt_replace_args.host = arg_str0("h", "host", "", "Service for this (delegated) host"); + mdns_txt_replace_args.txt = arg_strn(NULL, NULL, "item", 0, 30, "TXT Items (name=value)"); + mdns_txt_replace_args.end = arg_end(5); + + const esp_console_cmd_t cmd_txt_set = { + .command = "mdns_service_txt_replace", + .help = "Replace MDNS service TXT items", + .hint = NULL, + .func = &cmd_mdns_service_txt_replace, + .argtable = &mdns_txt_replace_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_txt_set) ); +} + +static struct { + struct arg_str *service; + struct arg_str *proto; + struct arg_str *instance; + struct arg_str *host; + struct arg_str *var; + struct arg_str *value; + struct arg_end *end; +} mdns_txt_set_args; + +static int cmd_mdns_service_txt_set(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_txt_set_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_txt_set_args.end, argv[0]); + return 1; + } + + if (!mdns_txt_set_args.service->sval[0] || !mdns_txt_set_args.proto->sval[0] || !mdns_txt_set_args.var->sval[0]) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + const char *instance = NULL; + if (mdns_txt_set_args.instance->count && mdns_txt_set_args.instance->sval[0]) { + instance = mdns_txt_set_args.instance->sval[0]; + printf("MDNS: Service Instance: %s\n", instance); + } + const char *host = NULL; + if (mdns_txt_set_args.host->count && mdns_txt_set_args.host->sval[0]) { + host = mdns_txt_set_args.host->sval[0]; + printf("MDNS: Service for delegated host: %s\n", host); + } + + ESP_ERROR_CHECK( mdns_service_txt_item_set_for_host(instance, mdns_txt_set_args.service->sval[0], mdns_txt_set_args.proto->sval[0], host, mdns_txt_set_args.var->sval[0], mdns_txt_set_args.value->sval[0]) ); + return 0; +} + +static void register_mdns_service_txt_set(void) +{ + mdns_txt_set_args.service = arg_str1(NULL, NULL, "", "MDNS Service"); + mdns_txt_set_args.proto = arg_str1(NULL, NULL, "", "IP Protocol"); + mdns_txt_set_args.var = arg_str1(NULL, NULL, "", "Item Name"); + mdns_txt_set_args.value = arg_str1(NULL, NULL, "", "Item Value"); + mdns_txt_set_args.instance = arg_str0("i", "instance", "", "Instance name"); + mdns_txt_set_args.host = arg_str0("h", "host", "", "Service for this (delegated) host"); + mdns_txt_set_args.end = arg_end(6); + + const esp_console_cmd_t cmd_txt_set = { + .command = "mdns_service_txt_set", + .help = "Add/Set MDNS service TXT item", + .hint = NULL, + .func = &cmd_mdns_service_txt_set, + .argtable = &mdns_txt_set_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_txt_set) ); +} + +static struct { + struct arg_str *service; + struct arg_str *proto; + struct arg_str *var; + struct arg_str *instance; + struct arg_str *host; + struct arg_end *end; +} mdns_txt_remove_args; + +static int cmd_mdns_service_txt_remove(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_txt_remove_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_txt_remove_args.end, argv[0]); + return 1; + } + + if (!mdns_txt_remove_args.service->sval[0] || !mdns_txt_remove_args.proto->sval[0] || !mdns_txt_remove_args.var->sval[0]) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + const char *instance = NULL; + if (mdns_txt_remove_args.instance->count && mdns_txt_remove_args.instance->sval[0]) { + instance = mdns_txt_remove_args.instance->sval[0]; + } + const char *host = NULL; + if (mdns_txt_remove_args.host->count && mdns_txt_remove_args.host->sval[0]) { + host = mdns_txt_remove_args.host->sval[0]; + } + ESP_ERROR_CHECK( mdns_service_txt_item_remove_for_host(instance, mdns_txt_remove_args.service->sval[0], mdns_txt_remove_args.proto->sval[0], host, mdns_txt_remove_args.var->sval[0]) ); + return 0; +} + +static void register_mdns_service_txt_remove(void) +{ + mdns_txt_remove_args.service = arg_str1(NULL, NULL, "", "MDNS Service"); + mdns_txt_remove_args.proto = arg_str1(NULL, NULL, "", "IP Protocol"); + mdns_txt_remove_args.var = arg_str1(NULL, NULL, "", "Item Name"); + mdns_txt_remove_args.instance = arg_str0("i", "instance", "", "Instance name"); + mdns_txt_remove_args.host = arg_str0("h", "host", "", "Service for this (delegated) host"); + mdns_txt_remove_args.end = arg_end(2); + + const esp_console_cmd_t cmd_txt_remove = { + .command = "mdns_service_txt_remove", + .help = "Remove MDNS service TXT item", + .hint = NULL, + .func = &cmd_mdns_service_txt_remove, + .argtable = &mdns_txt_remove_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_txt_remove) ); +} + +static int cmd_mdns_service_remove_all(int argc, char **argv) +{ + mdns_service_remove_all(); + return 0; +} + +static void register_mdns_service_remove_all(void) +{ + const esp_console_cmd_t cmd_free = { + .command = "mdns_service_remove_all", + .help = "Remove all MDNS services", + .hint = NULL, + .func = &cmd_mdns_service_remove_all, + .argtable = NULL + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_free) ); +} + +#define MDNS_MAX_LOOKUP_RESULTS CONFIG_MDNS_MAX_SERVICES + +static struct { + struct arg_str *instance; + struct arg_str *service; + struct arg_str *proto; + struct arg_lit *delegated; + struct arg_end *end; +} mdns_lookup_service_args; + +static esp_err_t lookup_service(const char *instance, const char *service, const char *proto, size_t max_results, + mdns_result_t **result, bool delegated) +{ + if (delegated) { + return mdns_lookup_delegated_service(instance, service, proto, max_results, result); + } + return mdns_lookup_selfhosted_service(instance, service, proto, max_results, result); +} + +static int cmd_mdns_lookup_service(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_lookup_service_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_lookup_service_args.end, argv[0]); + return 1; + } + + if (!mdns_lookup_service_args.instance->sval[0] || !mdns_lookup_service_args.service->sval[0] || !mdns_lookup_service_args.proto->sval[0]) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + mdns_result_t *results = NULL; + esp_err_t err = lookup_service(mdns_lookup_service_args.instance->count ? mdns_lookup_service_args.instance->sval[0] : NULL, + mdns_lookup_service_args.service->sval[0], mdns_lookup_service_args.proto->sval[0], + MDNS_MAX_LOOKUP_RESULTS, &results, mdns_lookup_service_args.delegated->count); + if (err) { + printf("Service lookup failed\n"); + return 1; + } + if (!results) { + printf("No results found!\n"); + return 0; + } + mdns_print_results(results); + mdns_query_results_free(results); + return 0; +} + +static void register_mdns_lookup_service(void) +{ + mdns_lookup_service_args.service = arg_str1(NULL, NULL, "", "MDNS Service"); + mdns_lookup_service_args.proto = arg_str1(NULL, NULL, "", "IP Protocol"); + mdns_lookup_service_args.instance = arg_str0("i", "instance", "", "Instance name"); + mdns_lookup_service_args.delegated = arg_lit0("d", "delegated", "Lookup delegated services"); + mdns_lookup_service_args.end = arg_end(4); + + const esp_console_cmd_t cmd_lookup_service = { + .command = "mdns_service_lookup", + .help = "Lookup registered service", + .hint = NULL, + .func = &cmd_mdns_lookup_service, + .argtable = &mdns_lookup_service_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_lookup_service) ); +} + +static struct { + struct arg_str *hostname; + struct arg_str *address; + struct arg_end *end; +} mdns_delegate_host_args; + +static int cmd_mdns_delegate_host(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_delegate_host_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_delegate_host_args.end, argv[0]); + return 1; + } + + if (!mdns_delegate_host_args.hostname->sval[0] || !mdns_delegate_host_args.address->sval[0]) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + + mdns_ip_addr_t addr = { .next = NULL}; + esp_netif_str_to_ip4(mdns_delegate_host_args.address->sval[0], &addr.addr.u_addr.ip4); + addr.addr.type = ESP_IPADDR_TYPE_V4; + + esp_err_t err = mdns_delegate_hostname_add(mdns_delegate_host_args.hostname->sval[0], &addr); + if (err) { + printf("mdns_delegate_hostname_add() failed\n"); + return 1; + } + return 0; +} + +static void register_mdns_delegate_host(void) +{ + mdns_delegate_host_args.hostname = arg_str1(NULL, NULL, "", "Delegated hostname"); + mdns_delegate_host_args.address = arg_str1(NULL, NULL, "
", "Delegated hosts address"); + mdns_delegate_host_args.end = arg_end(2); + + const esp_console_cmd_t cmd_delegate_host = { + .command = "mdns_delegate_host", + .help = "Add delegated hostname", + .hint = NULL, + .func = &cmd_mdns_delegate_host, + .argtable = &mdns_delegate_host_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_delegate_host) ); +} + +static struct { + struct arg_str *hostname; + struct arg_end *end; +} mdns_undelegate_host_args; + +static int cmd_mdns_undelegate_host(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_undelegate_host_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_undelegate_host_args.end, argv[0]); + return 1; + } + + if (!mdns_undelegate_host_args.hostname->sval[0]) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + + if (mdns_delegate_hostname_remove(mdns_undelegate_host_args.hostname->sval[0]) != ESP_OK) { + printf("mdns_delegate_hostname_remove() failed\n"); + return 1; + } + return 0; +} + +static void register_mdns_undelegate_host(void) +{ + mdns_undelegate_host_args.hostname = arg_str1(NULL, NULL, "", "Delegated hostname"); + mdns_undelegate_host_args.end = arg_end(2); + + const esp_console_cmd_t cmd_undelegate_host = { + .command = "mdns_undelegate_host", + .help = "Remove delegated hostname", + .hint = NULL, + .func = &cmd_mdns_undelegate_host, + .argtable = &mdns_undelegate_host_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_undelegate_host) ); +} + +static struct { + struct arg_str *service; + struct arg_str *proto; + struct arg_str *sub; + struct arg_str *instance; + struct arg_str *host; + struct arg_end *end; +} mdns_service_subtype_args; + +static int cmd_mdns_service_subtype(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_service_subtype_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_service_subtype_args.end, argv[0]); + return 1; + } + + if (!mdns_service_subtype_args.service->sval[0] || !mdns_service_subtype_args.proto->sval[0] || !mdns_service_subtype_args.sub->sval[0]) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + const char *instance = NULL; + if (mdns_service_subtype_args.instance->count && mdns_service_subtype_args.instance->sval[0]) { + instance = mdns_service_subtype_args.instance->sval[0]; + } + const char *host = NULL; + if (mdns_service_subtype_args.host->count && mdns_service_subtype_args.host->sval[0]) { + host = mdns_service_subtype_args.host->sval[0]; + } + ESP_ERROR_CHECK( mdns_service_subtype_add_for_host(instance, mdns_service_subtype_args.service->sval[0], mdns_service_subtype_args.proto->sval[0], host, mdns_service_subtype_args.sub->sval[0]) ); + return 0; +} + +static void register_mdns_service_subtype_set(void) +{ + mdns_service_subtype_args.service = arg_str1(NULL, NULL, "", "MDNS Service"); + mdns_service_subtype_args.proto = arg_str1(NULL, NULL, "", "IP Protocol"); + mdns_service_subtype_args.sub = arg_str1(NULL, NULL, "", "Subtype"); + mdns_service_subtype_args.instance = arg_str0("i", "instance", "", "Instance name"); + mdns_service_subtype_args.host = arg_str0("h", "host", "", "Service for this (delegated) host"); + mdns_service_subtype_args.end = arg_end(5); + + const esp_console_cmd_t cmd_service_sub = { + .command = "mdns_service_subtype", + .help = "Adds subtype for service", + .hint = NULL, + .func = &cmd_mdns_service_subtype, + .argtable = &mdns_service_subtype_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_service_sub) ); +} + +static struct { + struct arg_str *service; + struct arg_str *proto; + struct arg_end *end; +} mdns_browse_args; + +static void mdns_browse_notifier(mdns_result_t *result) +{ + if (result) { + mdns_print_results(result); + } +} + +static int cmd_mdns_browse(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_browse_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_browse_args.end, argv[0]); + return 1; + } + + if (!mdns_browse_args.service->sval[0] || !mdns_browse_args.proto->sval[0]) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + mdns_browse_t *handle = mdns_browse_new(mdns_browse_args.service->sval[0], mdns_browse_args.proto->sval[0], mdns_browse_notifier); + return handle ? 0 : 1; +} + +static void register_mdns_browse(void) +{ + mdns_browse_args.service = arg_str1(NULL, NULL, "", "MDNS Service"); + mdns_browse_args.proto = arg_str1(NULL, NULL, "", "IP Protocol"); + mdns_browse_args.end = arg_end(2); + + const esp_console_cmd_t cmd_browse = { + .command = "mdns_browse", + .help = "Start browsing", + .hint = NULL, + .func = &cmd_mdns_browse, + .argtable = &mdns_browse_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_browse) ); +} + +static int cmd_mdns_browse_del(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &mdns_browse_args); + if (nerrors != 0) { + arg_print_errors(stderr, mdns_browse_args.end, argv[0]); + return 1; + } + + if (!mdns_browse_args.service->sval[0] || !mdns_browse_args.proto->sval[0]) { + printf("ERROR: Bad arguments!\n"); + return 1; + } + esp_err_t err = mdns_browse_delete(mdns_browse_args.service->sval[0], mdns_browse_args.proto->sval[0]); + return err == ESP_OK ? 0 : 1; +} + +static void register_mdns_browse_del(void) +{ + mdns_browse_args.service = arg_str1(NULL, NULL, "", "MDNS Service"); + mdns_browse_args.proto = arg_str1(NULL, NULL, "", "IP Protocol"); + mdns_browse_args.end = arg_end(2); + + const esp_console_cmd_t cmd_browse_del = { + .command = "mdns_browse_del", + .help = "Stop browsing", + .hint = NULL, + .func = &cmd_mdns_browse_del, + .argtable = &mdns_browse_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_browse_del) ); +} + +void mdns_console_register(void) +{ + register_mdns_init(); + register_mdns_free(); + register_mdns_set_hostname(); + register_mdns_set_instance(); + register_mdns_service_add(); + register_mdns_service_remove(); + register_mdns_service_instance_set(); + register_mdns_service_port_set(); + register_mdns_service_txt_replace(); + register_mdns_service_txt_set(); + register_mdns_service_txt_remove(); + register_mdns_service_remove_all(); + + register_mdns_lookup_service(); + register_mdns_delegate_host(); + register_mdns_undelegate_host(); + register_mdns_service_subtype_set(); + + register_mdns_browse(); + register_mdns_browse_del(); + +#ifdef CONFIG_LWIP_IPV4 + register_mdns_query_a(); +#endif +#ifdef CONFIG_LWIP_IPV6 + register_mdns_query_aaaa(); +#endif + register_mdns_query_txt(); + register_mdns_query_srv(); + register_mdns_query_ptr(); + + register_mdns_query_ip(); + register_mdns_query_svc(); +} diff --git a/code/components/mdns/mdns_networking_lwip.c b/code/components/mdns/mdns_networking_lwip.c new file mode 100644 index 00000000..16604cdf --- /dev/null +++ b/code/components/mdns/mdns_networking_lwip.c @@ -0,0 +1,397 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * MDNS Server Networking + * + */ +#include +#include "esp_log.h" +#include "lwip/ip_addr.h" +#include "lwip/pbuf.h" +#include "lwip/igmp.h" +#include "lwip/udp.h" +#include "lwip/mld6.h" +#include "lwip/priv/tcpip_priv.h" +#include "esp_system.h" +#include "esp_event.h" +#include "mdns_networking.h" +#include "esp_netif_net_stack.h" + +/* + * MDNS Server Networking + * + */ +enum interface_protocol { + PROTO_IPV4 = 1 << MDNS_IP_PROTOCOL_V4, + PROTO_IPV6 = 1 << MDNS_IP_PROTOCOL_V6 +}; + +typedef struct interfaces { + bool ready; + int proto; +} interfaces_t; + +static interfaces_t s_interfaces[MDNS_MAX_INTERFACES]; + +static struct udp_pcb *_pcb_main = NULL; + +static const char *TAG = "mdns_networking"; + +static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *raddr, uint16_t rport); + +/** + * @brief Low level UDP PCB Initialize + */ +static esp_err_t _udp_pcb_main_init(void) +{ + if (_pcb_main) { + return ESP_OK; + } + _pcb_main = udp_new(); + if (!_pcb_main) { + return ESP_ERR_NO_MEM; + } + if (udp_bind(_pcb_main, IP_ANY_TYPE, MDNS_SERVICE_PORT) != 0) { + udp_remove(_pcb_main); + _pcb_main = NULL; + return ESP_ERR_INVALID_STATE; + } + _pcb_main->mcast_ttl = 255; + _pcb_main->remote_port = MDNS_SERVICE_PORT; + ip_addr_copy(_pcb_main->remote_ip, *(IP_ANY_TYPE)); + udp_recv(_pcb_main, &_udp_recv, NULL); + return ESP_OK; +} + +/** + * @brief Low level UDP PCB Free + */ +static void _udp_pcb_main_deinit(void) +{ + if (_pcb_main) { + udp_recv(_pcb_main, NULL, NULL); + udp_disconnect(_pcb_main); + udp_remove(_pcb_main); + _pcb_main = NULL; + } +} + +/** + * @brief Low level UDP Multicast membership control + */ +static esp_err_t _udp_join_group(mdns_if_t if_inx, mdns_ip_protocol_t ip_protocol, bool join) +{ + struct netif *netif = NULL; + esp_netif_t *tcpip_if = _mdns_get_esp_netif(if_inx); + + if (!esp_netif_is_netif_up(tcpip_if)) { + // Network interface went down before event propagated, skipping IGMP config + return ESP_ERR_INVALID_STATE; + } + + netif = esp_netif_get_netif_impl(tcpip_if); + assert(netif); + +#if LWIP_IPV4 + if (ip_protocol == MDNS_IP_PROTOCOL_V4) { + ip4_addr_t multicast_addr; + IP4_ADDR(&multicast_addr, 224, 0, 0, 251); + + if (join) { + if (igmp_joingroup_netif(netif, &multicast_addr)) { + return ESP_ERR_INVALID_STATE; + } + } else { + if (igmp_leavegroup_netif(netif, &multicast_addr)) { + return ESP_ERR_INVALID_STATE; + } + } + } +#endif // LWIP_IPV4 +#if LWIP_IPV6 + if (ip_protocol == MDNS_IP_PROTOCOL_V6) { + ip_addr_t multicast_addr = IPADDR6_INIT(0x000002ff, 0, 0, 0xfb000000); + + if (join) { + if (mld6_joingroup_netif(netif, ip_2_ip6(&multicast_addr))) { + return ESP_ERR_INVALID_STATE; + } + } else { + if (mld6_leavegroup_netif(netif, ip_2_ip6(&multicast_addr))) { + return ESP_ERR_INVALID_STATE; + } + } + } +#endif // LWIP_IPV6 + return ESP_OK; +} + +/** + * @brief the receive callback of the raw udp api. Packets are received here + * + */ +static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *raddr, uint16_t rport) +{ + + uint8_t i; + while (pb != NULL) { + struct pbuf *this_pb = pb; + pb = pb->next; + this_pb->next = NULL; + + mdns_rx_packet_t *packet = (mdns_rx_packet_t *)malloc(sizeof(mdns_rx_packet_t)); + if (!packet) { + HOOK_MALLOC_FAILED; + //missed packet - no memory + pbuf_free(this_pb); + continue; + } + + packet->tcpip_if = MDNS_MAX_INTERFACES; + packet->pb = this_pb; + packet->src_port = rport; +#if LWIP_IPV4 && LWIP_IPV6 + packet->src.type = raddr->type; + memcpy(&packet->src.u_addr, &raddr->u_addr, sizeof(raddr->u_addr)); +#elif LWIP_IPV4 + packet->src.type = IPADDR_TYPE_V4; + packet->src.u_addr.ip4.addr = raddr->addr; +#elif LWIP_IPV6 + packet->src.type = IPADDR_TYPE_V6; + memcpy(&packet->src.u_addr.ip6, raddr, sizeof(ip_addr_t)); +#endif + packet->dest.type = packet->src.type; + +#if LWIP_IPV4 + if (packet->src.type == IPADDR_TYPE_V4) { + packet->ip_protocol = MDNS_IP_PROTOCOL_V4; + struct ip_hdr *iphdr = (struct ip_hdr *)(((uint8_t *)(packet->pb->payload)) - UDP_HLEN - IP_HLEN); + packet->dest.u_addr.ip4.addr = iphdr->dest.addr; + packet->multicast = ip4_addr_ismulticast(&(packet->dest.u_addr.ip4)); + } +#endif // LWIP_IPV4 +#if LWIP_IPV6 + if (packet->src.type == IPADDR_TYPE_V6) { + packet->ip_protocol = MDNS_IP_PROTOCOL_V6; + struct ip6_hdr *ip6hdr = (struct ip6_hdr *)(((uint8_t *)(packet->pb->payload)) - UDP_HLEN - IP6_HLEN); + memcpy(&packet->dest.u_addr.ip6.addr, (uint8_t *)ip6hdr->dest.addr, 16); + packet->multicast = ip6_addr_ismulticast(&(packet->dest.u_addr.ip6)); + } +#endif // LWIP_IPV6 + + //lwip does not return the proper pcb if you have more than one for the same multicast address (but different interfaces) + struct netif *netif = NULL; + bool found = false; + for (i = 0; i < MDNS_MAX_INTERFACES; i++) { + netif = esp_netif_get_netif_impl(_mdns_get_esp_netif(i)); + if (s_interfaces[i].proto && netif && netif == ip_current_input_netif ()) { +#if LWIP_IPV4 + if (packet->src.type == IPADDR_TYPE_V4) { + if ((packet->src.u_addr.ip4.addr & ip_2_ip4(&netif->netmask)->addr) != (ip_2_ip4(&netif->ip_addr)->addr & ip_2_ip4(&netif->netmask)->addr)) { + //packet source is not in the same subnet + break; + } + } +#endif // LWIP_IPV4 + packet->tcpip_if = i; + found = true; + break; + } + } + + if (!found || _mdns_send_rx_action(packet) != ESP_OK) { + pbuf_free(this_pb); + free(packet); + } + } + +} + +bool mdns_is_netif_ready(mdns_if_t netif, mdns_ip_protocol_t ip_proto) +{ + return s_interfaces[netif].ready && + s_interfaces[netif].proto & (ip_proto == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6); +} + +/** + * @brief Check if any of the interfaces is up + */ +static bool _udp_pcb_is_in_use(void) +{ + int i, p; + for (i = 0; i < MDNS_MAX_INTERFACES; i++) { + for (p = 0; p < MDNS_IP_PROTOCOL_MAX; p++) { + if (mdns_is_netif_ready(i, p)) { + return true; + } + } + } + return false; +} + +/** + * @brief Stop PCB Main code + */ +static void _udp_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + s_interfaces[tcpip_if].proto &= ~(ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6); + if (s_interfaces[tcpip_if].proto == 0) { + s_interfaces[tcpip_if].ready = false; + _udp_join_group(tcpip_if, ip_protocol, false); + if (!_udp_pcb_is_in_use()) { + _udp_pcb_main_deinit(); + } + } +} + +/** + * @brief Start PCB Main code + */ +static esp_err_t _udp_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + if (mdns_is_netif_ready(tcpip_if, ip_protocol)) { + return ESP_ERR_INVALID_STATE; + } + + esp_err_t err = _udp_join_group(tcpip_if, ip_protocol, true); + if (err) { + return err; + } + + err = _udp_pcb_main_init(); + if (err) { + return err; + } + s_interfaces[tcpip_if].proto |= (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6); + s_interfaces[tcpip_if].ready = true; + + return ESP_OK; +} + +typedef struct { + struct tcpip_api_call_data call; + mdns_if_t tcpip_if; + mdns_ip_protocol_t ip_protocol; + struct pbuf *pbt; + const ip_addr_t *ip; + uint16_t port; + esp_err_t err; +} mdns_api_call_t; + +/** + * @brief Start PCB from LwIP thread + */ +static err_t _mdns_pcb_init_api(struct tcpip_api_call_data *api_call_msg) +{ + mdns_api_call_t *msg = (mdns_api_call_t *)api_call_msg; + msg->err = _udp_pcb_init(msg->tcpip_if, msg->ip_protocol) == ESP_OK ? ERR_OK : ERR_IF; + return msg->err; +} + +/** + * @brief Stop PCB from LwIP thread + */ +static err_t _mdns_pcb_deinit_api(struct tcpip_api_call_data *api_call_msg) +{ + mdns_api_call_t *msg = (mdns_api_call_t *)api_call_msg; + _udp_pcb_deinit(msg->tcpip_if, msg->ip_protocol); + msg->err = ESP_OK; + return ESP_OK; +} + +/* + * Non-static functions below are + * - _mdns prefixed + * - commented in mdns_networking.h header + */ +esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + mdns_api_call_t msg = { + .tcpip_if = tcpip_if, + .ip_protocol = ip_protocol + }; + tcpip_api_call(_mdns_pcb_init_api, &msg.call); + return msg.err; +} + +esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + mdns_api_call_t msg = { + .tcpip_if = tcpip_if, + .ip_protocol = ip_protocol + }; + tcpip_api_call(_mdns_pcb_deinit_api, &msg.call); + return msg.err; +} + +static err_t _mdns_udp_pcb_write_api(struct tcpip_api_call_data *api_call_msg) +{ + void *nif = NULL; + mdns_api_call_t *msg = (mdns_api_call_t *)api_call_msg; + nif = esp_netif_get_netif_impl(_mdns_get_esp_netif(msg->tcpip_if)); + if (!nif || !mdns_is_netif_ready(msg->tcpip_if, msg->ip_protocol) || _pcb_main == NULL) { + pbuf_free(msg->pbt); + msg->err = ERR_IF; + return ERR_IF; + } + esp_err_t err = udp_sendto_if (_pcb_main, msg->pbt, msg->ip, msg->port, (struct netif *)nif); + pbuf_free(msg->pbt); + msg->err = err; + return err; +} + +size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t *data, size_t len) +{ + struct pbuf *pbt = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pbt == NULL) { + return 0; + } + memcpy((uint8_t *)pbt->payload, data, len); + + ip_addr_t ip_add_copy; +#if LWIP_IPV6 && LWIP_IPV4 + ip_add_copy.type = ip->type; + memcpy(&(ip_add_copy.u_addr), &(ip->u_addr), sizeof(ip_add_copy.u_addr)); +#elif LWIP_IPV4 + ip_add_copy.addr = ip->u_addr.ip4.addr; +#elif LWIP_IPV6 +#if LWIP_IPV6_SCOPES + ip_add_copy.zone = ip->u_addr.ip6.zone; +#endif // LWIP_IPV6_SCOPES + memcpy(ip_add_copy.addr, ip->u_addr.ip6.addr, sizeof(ip_add_copy.addr)); +#endif + + mdns_api_call_t msg = { + .tcpip_if = tcpip_if, + .ip_protocol = ip_protocol, + .pbt = pbt, + .ip = &ip_add_copy, + .port = port + }; + tcpip_api_call(_mdns_udp_pcb_write_api, &msg.call); + + if (msg.err) { + return 0; + } + return len; +} + +void *_mdns_get_packet_data(mdns_rx_packet_t *packet) +{ + return packet->pb->payload; +} + +size_t _mdns_get_packet_len(mdns_rx_packet_t *packet) +{ + return packet->pb->len; +} + +void _mdns_packet_free(mdns_rx_packet_t *packet) +{ + pbuf_free(packet->pb); + free(packet); +} diff --git a/code/components/mdns/mdns_networking_socket.c b/code/components/mdns/mdns_networking_socket.c new file mode 100644 index 00000000..a99a9cc2 --- /dev/null +++ b/code/components/mdns/mdns_networking_socket.c @@ -0,0 +1,498 @@ +/* + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief MDNS Server Networking module implemented using BSD sockets + */ + +#include +#include "esp_event.h" +#include "mdns_networking.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "esp_log.h" + +#if defined(CONFIG_IDF_TARGET_LINUX) +#include +#include +#endif + +enum interface_protocol { + PROTO_IPV4 = 1 << MDNS_IP_PROTOCOL_V4, + PROTO_IPV6 = 1 << MDNS_IP_PROTOCOL_V6 +}; + +typedef struct interfaces { + int sock; + int proto; +} interfaces_t; + +static interfaces_t s_interfaces[MDNS_MAX_INTERFACES]; + +static const char *TAG = "mdns_networking"; +static bool s_run_sock_recv_task = false; +static int create_socket(esp_netif_t *netif); +static int join_mdns_multicast_group(int sock, esp_netif_t *netif, mdns_ip_protocol_t ip_protocol); + +#if defined(CONFIG_IDF_TARGET_LINUX) +// Need to define packet buffer struct on linux +struct pbuf { + struct pbuf *next; + void *payload; + size_t tot_len; + size_t len; +}; +#else +// Compatibility define to access sock-addr struct the same way for lwip and linux +#define s6_addr32 un.u32_addr +#endif // CONFIG_IDF_TARGET_LINUX + +static void __attribute__((constructor)) ctor_networking_socket(void) +{ + for (int i = 0; i < sizeof(s_interfaces) / sizeof(s_interfaces[0]); ++i) { + s_interfaces[i].sock = -1; + s_interfaces[i].proto = 0; + } +} + +static void delete_socket(int sock) +{ + close(sock); +} + +bool mdns_is_netif_ready(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + return s_interfaces[tcpip_if].proto & (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6); +} + +void *_mdns_get_packet_data(mdns_rx_packet_t *packet) +{ + return packet->pb->payload; +} + +size_t _mdns_get_packet_len(mdns_rx_packet_t *packet) +{ + return packet->pb->len; +} + +void _mdns_packet_free(mdns_rx_packet_t *packet) +{ + free(packet->pb->payload); + free(packet->pb); + free(packet); +} + +esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + s_interfaces[tcpip_if].proto &= ~(ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6); + if (s_interfaces[tcpip_if].proto == 0) { + // if the interface for both protocols uninitialized, close the interface socket + if (s_interfaces[tcpip_if].sock >= 0) { + delete_socket(s_interfaces[tcpip_if].sock); + } + } + + for (int i = 0; i < MDNS_MAX_INTERFACES; i++) { + if (s_interfaces[i].sock >= 0) { + // If any of the interfaces initialized + return ESP_OK; + } + } + + // no interface alive, stop the rx task + s_run_sock_recv_task = false; + vTaskDelay(pdMS_TO_TICKS(500)); + return ESP_OK; +} + +#if defined(CONFIG_IDF_TARGET_LINUX) +#ifdef CONFIG_LWIP_IPV6 +static char *inet6_ntoa_r(struct in6_addr addr, char *ptr, size_t size) +{ + inet_ntop(AF_INET6, &(addr.s6_addr32[0]), ptr, size); + return ptr; +} +#endif // CONFIG_LWIP_IPV6 +static char *inet_ntoa_r(struct in_addr addr, char *ptr, size_t size) +{ + char *res = inet_ntoa(addr); + if (res && strlen(res) < size) { + strcpy(ptr, res); + } + return res; +} +#endif // CONFIG_IDF_TARGET_LINUX + +static inline char *get_string_address(struct sockaddr_storage *source_addr) +{ + static char address_str[40]; // 40=(8*4+7+term) is the max size of ascii IPv6 addr "XXXX:XX...XX:XXXX" + char *res = NULL; + // Convert ip address to string +#ifdef CONFIG_LWIP_IPV4 + if (source_addr->ss_family == PF_INET) { + res = inet_ntoa_r(((struct sockaddr_in *)source_addr)->sin_addr, address_str, sizeof(address_str)); + } +#endif +#ifdef CONFIG_LWIP_IPV6 + if (source_addr->ss_family == PF_INET6) { + res = inet6_ntoa_r(((struct sockaddr_in6 *)source_addr)->sin6_addr, address_str, sizeof(address_str)); + } +#endif + if (!res) { + address_str[0] = '\0'; // Returns empty string if conversion didn't succeed + } + return address_str; +} + + +static inline size_t espaddr_to_inet(const esp_ip_addr_t *addr, const uint16_t port, const mdns_ip_protocol_t ip_protocol, struct sockaddr_storage *in_addr) +{ + size_t ss_addr_len = 0; + memset(in_addr, 0, sizeof(struct sockaddr_storage)); +#ifdef CONFIG_LWIP_IPV4 + if (ip_protocol == MDNS_IP_PROTOCOL_V4 && addr->type == ESP_IPADDR_TYPE_V4) { + in_addr->ss_family = PF_INET; +#if !defined(CONFIG_IDF_TARGET_LINUX) + in_addr->s2_len = sizeof(struct sockaddr_in); +#endif + ss_addr_len = sizeof(struct sockaddr_in); + struct sockaddr_in *in_addr_ip4 = (struct sockaddr_in *) in_addr; + in_addr_ip4->sin_port = port; + in_addr_ip4->sin_addr.s_addr = addr->u_addr.ip4.addr; + } +#endif // CONFIG_LWIP_IPV4 +#ifdef CONFIG_LWIP_IPV6 + if (ip_protocol == MDNS_IP_PROTOCOL_V6 && addr->type == ESP_IPADDR_TYPE_V6) { + memset(in_addr, 0, sizeof(struct sockaddr_storage)); + in_addr->ss_family = PF_INET6; +#if !defined(CONFIG_IDF_TARGET_LINUX) + in_addr->s2_len = sizeof(struct sockaddr_in6); +#endif + ss_addr_len = sizeof(struct sockaddr_in6); + struct sockaddr_in6 *in_addr_ip6 = (struct sockaddr_in6 *)in_addr; + uint32_t *u32_addr = in_addr_ip6->sin6_addr.s6_addr32; + in_addr_ip6->sin6_port = port; + u32_addr[0] = addr->u_addr.ip6.addr[0]; + u32_addr[1] = addr->u_addr.ip6.addr[1]; + u32_addr[2] = addr->u_addr.ip6.addr[2]; + u32_addr[3] = addr->u_addr.ip6.addr[3]; + } +#endif // CONFIG_LWIP_IPV6 + return ss_addr_len; +} + +size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t *data, size_t len) +{ + if (!(s_interfaces[tcpip_if].proto & (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6))) { + return 0; + } + int sock = s_interfaces[tcpip_if].sock; + if (sock < 0) { + return 0; + } + struct sockaddr_storage in_addr; + size_t ss_size = espaddr_to_inet(ip, htons(port), ip_protocol, &in_addr); + if (!ss_size) { + ESP_LOGE(TAG, "espaddr_to_inet() failed: Mismatch of IP protocols"); + return 0; + } + ESP_LOGD(TAG, "[sock=%d]: Sending to IP %s port %d", sock, get_string_address(&in_addr), port); + ssize_t actual_len = sendto(sock, data, len, 0, (struct sockaddr *)&in_addr, ss_size); + if (actual_len < 0) { + ESP_LOGE(TAG, "[sock=%d]: _mdns_udp_pcb_write sendto() has failed\n errno=%d: %s", sock, errno, strerror(errno)); + } + return actual_len; +} + +static inline void inet_to_espaddr(const struct sockaddr_storage *in_addr, esp_ip_addr_t *addr, uint16_t *port) +{ +#ifdef CONFIG_LWIP_IPV4 + if (in_addr->ss_family == PF_INET) { + struct sockaddr_in *in_addr_ip4 = (struct sockaddr_in *)in_addr; + memset(addr, 0, sizeof(esp_ip_addr_t)); + *port = in_addr_ip4->sin_port; + addr->u_addr.ip4.addr = in_addr_ip4->sin_addr.s_addr; + addr->type = ESP_IPADDR_TYPE_V4; + } +#endif /* CONFIG_LWIP_IPV4 */ +#ifdef CONFIG_LWIP_IPV6 + if (in_addr->ss_family == PF_INET6) { + struct sockaddr_in6 *in_addr_ip6 = (struct sockaddr_in6 *)in_addr; + memset(addr, 0, sizeof(esp_ip_addr_t)); + *port = in_addr_ip6->sin6_port; + uint32_t *u32_addr = in_addr_ip6->sin6_addr.s6_addr32; + if (u32_addr[0] == 0 && u32_addr[1] == 0 && u32_addr[2] == esp_netif_htonl(0x0000FFFFUL)) { + // Mapped IPv4 address, convert directly to IPv4 + addr->type = ESP_IPADDR_TYPE_V4; + addr->u_addr.ip4.addr = u32_addr[3]; + } else { + addr->type = ESP_IPADDR_TYPE_V6; + addr->u_addr.ip6.addr[0] = u32_addr[0]; + addr->u_addr.ip6.addr[1] = u32_addr[1]; + addr->u_addr.ip6.addr[2] = u32_addr[2]; + addr->u_addr.ip6.addr[3] = u32_addr[3]; + } + } +#endif // CONFIG_LWIP_IPV6 +} + +void sock_recv_task(void *arg) +{ + while (s_run_sock_recv_task) { + struct timeval tv = { + .tv_sec = 1, + .tv_usec = 0, + }; + fd_set rfds; + FD_ZERO(&rfds); + int max_sock = -1; + for (int i = 0; i < MDNS_MAX_INTERFACES; i++) { + int sock = s_interfaces[i].sock; + if (sock >= 0) { + FD_SET(sock, &rfds); + max_sock = MAX(max_sock, sock); + } + } + if (max_sock < 0) { + vTaskDelay(pdMS_TO_TICKS(1000)); + ESP_LOGI(TAG, "No sock!"); + continue; + } + + int s = select(max_sock + 1, &rfds, NULL, NULL, &tv); + if (s < 0) { + ESP_LOGE(TAG, "Select failed. errno=%d: %s", errno, strerror(errno)); + break; + } else if (s > 0) { + for (int tcpip_if = 0; tcpip_if < MDNS_MAX_INTERFACES; tcpip_if++) { + int sock = s_interfaces[tcpip_if].sock; + if (sock < 0) { + continue; + } + if (FD_ISSET(sock, &rfds)) { + static char recvbuf[MDNS_MAX_PACKET_SIZE]; + uint16_t port = 0; + + struct sockaddr_storage raddr; // Large enough for both IPv4 or IPv6 + socklen_t socklen = sizeof(struct sockaddr_storage); + esp_ip_addr_t addr = {0}; + int len = recvfrom(sock, recvbuf, sizeof(recvbuf), 0, + (struct sockaddr *) &raddr, &socklen); + if (len < 0) { + ESP_LOGE(TAG, "multicast recvfrom failed. errno=%d: %s", errno, strerror(errno)); + break; + } + ESP_LOGD(TAG, "[sock=%d]: Received from IP:%s", sock, get_string_address(&raddr)); + ESP_LOG_BUFFER_HEXDUMP(TAG, recvbuf, len, ESP_LOG_VERBOSE); + inet_to_espaddr(&raddr, &addr, &port); + + // Allocate the packet structure and pass it to the mdns main engine + mdns_rx_packet_t *packet = (mdns_rx_packet_t *) calloc(1, sizeof(mdns_rx_packet_t)); + struct pbuf *packet_pbuf = calloc(1, sizeof(struct pbuf)); + uint8_t *buf = malloc(len); + if (packet == NULL || packet_pbuf == NULL || buf == NULL ) { + free(buf); + free(packet_pbuf); + free(packet); + HOOK_MALLOC_FAILED; + ESP_LOGE(TAG, "Failed to allocate the mdns packet"); + continue; + } + memcpy(buf, recvbuf, len); + packet_pbuf->next = NULL; + packet_pbuf->payload = buf; + packet_pbuf->tot_len = len; + packet_pbuf->len = len; + packet->tcpip_if = tcpip_if; + packet->pb = packet_pbuf; + packet->src_port = ntohs(port); + memcpy(&packet->src, &addr, sizeof(esp_ip_addr_t)); + // TODO(IDF-3651): Add the correct dest addr -- for mdns to decide multicast/unicast + // Currently it's enough to assume the packet is multicast and mdns to check the source port of the packet + memset(&packet->dest, 0, sizeof(esp_ip_addr_t)); + packet->multicast = 1; + packet->dest.type = packet->src.type; + packet->ip_protocol = + packet->src.type == ESP_IPADDR_TYPE_V4 ? MDNS_IP_PROTOCOL_V4 : MDNS_IP_PROTOCOL_V6; + if (_mdns_send_rx_action(packet) != ESP_OK) { + ESP_LOGE(TAG, "_mdns_send_rx_action failed!"); + free(packet->pb->payload); + free(packet->pb); + free(packet); + } + } + } + } + } + vTaskDelete(NULL); +} + +static void mdns_networking_init(void) +{ + if (s_run_sock_recv_task == false) { + s_run_sock_recv_task = true; + xTaskCreate( sock_recv_task, "mdns recv task", 3 * 1024, NULL, 5, NULL ); + } +} + +static bool create_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + if (s_interfaces[tcpip_if].proto & (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6)) { + return true; + } + int sock = s_interfaces[tcpip_if].sock; + esp_netif_t *netif = _mdns_get_esp_netif(tcpip_if); + if (sock < 0) { + sock = create_socket(netif); + } + if (sock < 0) { + ESP_LOGE(TAG, "Failed to create the socket!"); + return false; + } + int err = join_mdns_multicast_group(sock, netif, ip_protocol); + if (err < 0) { + ESP_LOGE(TAG, "Failed to add ipv6 multicast group for protocol %d", ip_protocol); + } + s_interfaces[tcpip_if].proto |= (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6); + s_interfaces[tcpip_if].sock = sock; + return true; +} + +esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + ESP_LOGI(TAG, "_mdns_pcb_init(tcpip_if=%lu, ip_protocol=%lu)", (unsigned long)tcpip_if, (unsigned long)ip_protocol); + if (!create_pcb(tcpip_if, ip_protocol)) { + return ESP_FAIL; + } + + mdns_networking_init(); + return ESP_OK; +} + +static int create_socket(esp_netif_t *netif) +{ +#ifdef CONFIG_LWIP_IPV6 + int sock = socket(PF_INET6, SOCK_DGRAM, 0); +#else + int sock = socket(PF_INET, SOCK_DGRAM, 0); +#endif + if (sock < 0) { + ESP_LOGE(TAG, "Failed to create socket. errno=%d: %s", errno, strerror(errno)); + return -1; + } + + int on = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ) < 0) { + ESP_LOGE(TAG, "Failed setsockopt() to set SO_REUSEADDR. errno=%d: %s\n", errno, strerror(errno)); + } + // Bind the socket to any address +#ifdef CONFIG_LWIP_IPV6 + struct sockaddr_in6 saddr = { INADDR_ANY }; + saddr.sin6_family = AF_INET6; + saddr.sin6_port = htons(5353); + bzero(&saddr.sin6_addr.s6_addr, sizeof(saddr.sin6_addr.s6_addr)); + int err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)); + if (err < 0) { + ESP_LOGE(TAG, "Failed to bind socket. errno=%d: %s", errno, strerror(errno)); + goto err; + } +#else + struct sockaddr_in saddr = { 0 }; + saddr.sin_family = AF_INET; + saddr.sin_port = htons(5353); + bzero(&saddr.sin_addr.s_addr, sizeof(saddr.sin_addr.s_addr)); + int err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)); + if (err < 0) { + ESP_LOGE(TAG, "Failed to bind socket. errno=%d: %s", errno, strerror(errno)); + goto err; + } +#endif // CONFIG_LWIP_IPV6 + struct ifreq ifr; + esp_netif_get_netif_impl_name(netif, ifr.ifr_name); + int ret = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(struct ifreq)); + if (ret < 0) { + ESP_LOGE(TAG, "\"%s\" Unable to bind socket to specified interface. errno=%d: %s", esp_netif_get_desc(netif), errno, strerror(errno)); + goto err; + } + + return sock; + +err: + close(sock); + return -1; +} + +#ifdef CONFIG_LWIP_IPV6 +static int socket_add_ipv6_multicast_group(int sock, esp_netif_t *netif) +{ + int ifindex = esp_netif_get_netif_impl_index(netif); + int err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)); + if (err < 0) { + ESP_LOGE(TAG, "Failed to set IPV6_MULTICAST_IF. errno=%d: %s", errno, strerror(errno)); + return err; + } + + struct ipv6_mreq v6imreq = { 0 }; + esp_ip_addr_t multi_addr = ESP_IP6ADDR_INIT(0x000002ff, 0, 0, 0xfb000000); + memcpy(&v6imreq.ipv6mr_multiaddr, &multi_addr.u_addr.ip6.addr, sizeof(v6imreq.ipv6mr_multiaddr)); + v6imreq.ipv6mr_interface = ifindex; + err = setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &v6imreq, sizeof(struct ipv6_mreq)); + if (err < 0) { + ESP_LOGE(TAG, "Failed to set IPV6_ADD_MEMBERSHIP. errno=%d: %s", errno, strerror(errno)); + return err; + } + return err; +} +#endif // CONFIG_LWIP_IPV6 + +#ifdef CONFIG_LWIP_IPV4 +static int socket_add_ipv4_multicast_group(int sock, esp_netif_t *netif) +{ + struct ip_mreq imreq = { 0 }; + int err = 0; + esp_netif_ip_info_t ip_info = { 0 }; + + if (esp_netif_get_ip_info(netif, &ip_info) != ESP_OK) { + ESP_LOGE(TAG, "Failed to esp_netif_get_ip_info()"); + goto err; + } + imreq.imr_interface.s_addr = ip_info.ip.addr; + + esp_ip_addr_t multicast_addr = ESP_IP4ADDR_INIT(224, 0, 0, 251); + imreq.imr_multiaddr.s_addr = multicast_addr.u_addr.ip4.addr; + + err = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(struct ip_mreq)); + if (err < 0) { + ESP_LOGE(TAG, "[sock=%d] Failed to set IP_ADD_MEMBERSHIP. errno=%d: %s", sock, errno, strerror(errno)); + goto err; + } + +err: + return err; +} +#endif // CONFIG_LWIP_IPV4 + +static int join_mdns_multicast_group(int sock, esp_netif_t *netif, mdns_ip_protocol_t ip_protocol) +{ +#ifdef CONFIG_LWIP_IPV4 + if (ip_protocol == MDNS_IP_PROTOCOL_V4) { + return socket_add_ipv4_multicast_group(sock, netif); + } +#endif // CONFIG_LWIP_IPV4 +#ifdef CONFIG_LWIP_IPV6 + if (ip_protocol == MDNS_IP_PROTOCOL_V6) { + return socket_add_ipv6_multicast_group(sock, netif); + } +#endif // CONFIG_LWIP_IPV6 + return -1; +} diff --git a/code/components/mdns/private_include/mdns_networking.h b/code/components/mdns/private_include/mdns_networking.h new file mode 100644 index 00000000..83cbe90c --- /dev/null +++ b/code/components/mdns/private_include/mdns_networking.h @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ESP_MDNS_NETWORKING_H_ +#define ESP_MDNS_NETWORKING_H_ + +/* + * MDNS Server Networking -- private include + * + */ +#include "mdns.h" +#include "mdns_private.h" + + +/** + * @brief Queue RX packet action + */ +esp_err_t _mdns_send_rx_action(mdns_rx_packet_t *packet); + +bool mdns_is_netif_ready(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol); + +/** + * @brief Start PCB + */ +esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol); + +/** + * @brief Stop PCB + */ +esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol); + +/** + * @brief send packet over UDP + * + * @param server The server + * @param data byte array containing the packet data + * @param len length of the packet data + * + * @return length of sent packet or 0 on error + */ +size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t *data, size_t len); + +/** + * @brief Gets data pointer to the mDNS packet + */ +void *_mdns_get_packet_data(mdns_rx_packet_t *packet); + +/** + * @brief Gets data length of c + */ +size_t _mdns_get_packet_len(mdns_rx_packet_t *packet); + +/** + * @brief Free the mDNS packet + */ +void _mdns_packet_free(mdns_rx_packet_t *packet); + +#endif /* ESP_MDNS_NETWORKING_H_ */ diff --git a/code/components/mdns/private_include/mdns_private.h b/code/components/mdns/private_include/mdns_private.h new file mode 100644 index 00000000..ce4c96b6 --- /dev/null +++ b/code/components/mdns/private_include/mdns_private.h @@ -0,0 +1,468 @@ +/* + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef MDNS_PRIVATE_H_ +#define MDNS_PRIVATE_H_ + +#include "sdkconfig.h" +#include "mdns.h" +#include "esp_task.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "esp_timer.h" +#include "esp_system.h" + +#ifdef CONFIG_MDNS_ENABLE_DEBUG_PRINTS +#define MDNS_ENABLE_DEBUG +#define _mdns_dbg_printf(...) printf(__VA_ARGS__) +#endif + +/** Number of predefined interfaces */ +#ifndef CONFIG_MDNS_PREDEF_NETIF_STA +#define CONFIG_MDNS_PREDEF_NETIF_STA 0 +#endif +#ifndef CONFIG_MDNS_PREDEF_NETIF_AP +#define CONFIG_MDNS_PREDEF_NETIF_AP 0 +#endif +#ifndef CONFIG_MDNS_PREDEF_NETIF_ETH +#define CONFIG_MDNS_PREDEF_NETIF_ETH 0 +#endif +#define MDNS_MAX_PREDEF_INTERFACES (CONFIG_MDNS_PREDEF_NETIF_STA + CONFIG_MDNS_PREDEF_NETIF_AP + CONFIG_MDNS_PREDEF_NETIF_ETH) + +#ifdef CONFIG_LWIP_IPV6_NUM_ADDRESSES +#define NETIF_IPV6_MAX_NUMS CONFIG_LWIP_IPV6_NUM_ADDRESSES +#else +#define NETIF_IPV6_MAX_NUMS 3 +#endif + +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) +/* CONFIG_LWIP_IPV4 was introduced in IDF v5.1 */ +/* For IDF v5.0, set CONFIG_LWIP_IPV4 to 1 by default */ +#ifndef CONFIG_LWIP_IPV4 +#define CONFIG_LWIP_IPV4 1 +#endif // CONFIG_LWIP_IPV4 +#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) + +/** Number of configured interfaces */ +#if MDNS_MAX_PREDEF_INTERFACES > CONFIG_MDNS_MAX_INTERFACES +#warning Number of configured interfaces is less then number of predefined interfaces. Please update CONFIG_MDNS_MAX_INTERFACES. +#define MDNS_MAX_INTERFACES (MDNS_MAX_PREDEF_INTERFACES) +#else +#define MDNS_MAX_INTERFACES (CONFIG_MDNS_MAX_INTERFACES) +#endif + +/** The maximum number of services */ +#define MDNS_MAX_SERVICES CONFIG_MDNS_MAX_SERVICES + +#define MDNS_ANSWER_PTR_TTL 4500 +#define MDNS_ANSWER_TXT_TTL 4500 +#define MDNS_ANSWER_SRV_TTL 120 +#define MDNS_ANSWER_A_TTL 120 +#define MDNS_ANSWER_AAAA_TTL 120 + +#define MDNS_FLAGS_QUERY_REPSONSE 0x8000 +#define MDNS_FLAGS_AUTHORITATIVE 0x0400 +#define MDNS_FLAGS_QR_AUTHORITATIVE (MDNS_FLAGS_QUERY_REPSONSE | MDNS_FLAGS_AUTHORITATIVE) +#define MDNS_FLAGS_DISTRIBUTED 0x0200 + +#define MDNS_NAME_REF 0xC000 + +//custom type! only used by this implementation +//to help manage service discovery handling +#define MDNS_TYPE_SDPTR 0x0032 + +#define MDNS_CLASS_IN 0x0001 +#define MDNS_CLASS_ANY 0x00FF +#define MDNS_CLASS_IN_FLUSH_CACHE 0x8001 + +#define MDNS_ANSWER_ALL 0x3F +#define MDNS_ANSWER_PTR 0x08 +#define MDNS_ANSWER_TXT 0x04 +#define MDNS_ANSWER_SRV 0x02 +#define MDNS_ANSWER_A 0x01 +#define MDNS_ANSWER_AAAA 0x10 +#define MDNS_ANSWER_NSEC 0x20 +#define MDNS_ANSWER_SDPTR 0x80 +#define MDNS_ANSWER_AAAA_SIZE 16 + +#define MDNS_SERVICE_PORT 5353 // UDP port that the server runs on +#define MDNS_SERVICE_STACK_DEPTH CONFIG_MDNS_TASK_STACK_SIZE +#define MDNS_TASK_PRIORITY CONFIG_MDNS_TASK_PRIORITY +#if (MDNS_TASK_PRIORITY > ESP_TASK_PRIO_MAX) +#error "mDNS task priority is higher than ESP_TASK_PRIO_MAX" +#elif (MDNS_TASK_PRIORITY > ESP_TASKD_EVENT_PRIO) +#warning "mDNS task priority is higher than ESP_TASKD_EVENT_PRIO, mDNS library might not work correctly" +#endif +#define MDNS_TASK_AFFINITY CONFIG_MDNS_TASK_AFFINITY +#define MDNS_SERVICE_ADD_TIMEOUT_MS CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS + +#define MDNS_PACKET_QUEUE_LEN 16 // Maximum packets that can be queued for parsing +#define MDNS_ACTION_QUEUE_LEN CONFIG_MDNS_ACTION_QUEUE_LEN // Maximum actions pending to the server +#define MDNS_TXT_MAX_LEN 1024 // Maximum string length of text data in TXT record +#define MDNS_MAX_PACKET_SIZE 1460 // Maximum size of mDNS outgoing packet + +#define MDNS_HEAD_LEN 12 +#define MDNS_HEAD_ID_OFFSET 0 +#define MDNS_HEAD_FLAGS_OFFSET 2 +#define MDNS_HEAD_QUESTIONS_OFFSET 4 +#define MDNS_HEAD_ANSWERS_OFFSET 6 +#define MDNS_HEAD_SERVERS_OFFSET 8 +#define MDNS_HEAD_ADDITIONAL_OFFSET 10 + +#define MDNS_TYPE_OFFSET 0 +#define MDNS_CLASS_OFFSET 2 +#define MDNS_TTL_OFFSET 4 +#define MDNS_LEN_OFFSET 8 +#define MDNS_DATA_OFFSET 10 + +#define MDNS_SRV_PRIORITY_OFFSET 0 +#define MDNS_SRV_WEIGHT_OFFSET 2 +#define MDNS_SRV_PORT_OFFSET 4 +#define MDNS_SRV_FQDN_OFFSET 6 + +#define MDNS_TIMER_PERIOD_US (CONFIG_MDNS_TIMER_PERIOD_MS*1000) + +#define MDNS_SERVICE_LOCK() xSemaphoreTake(_mdns_service_semaphore, portMAX_DELAY) +#define MDNS_SERVICE_UNLOCK() xSemaphoreGive(_mdns_service_semaphore) + +#define queueToEnd(type, queue, item) \ + if (!queue) { \ + queue = item; \ + } else { \ + type * _q = queue; \ + while (_q->next) { _q = _q->next; } \ + _q->next = item; \ + } + +#define queueDetach(type, queue, item) \ + if (queue) { \ + if (queue == item) { \ + queue = queue->next; \ + } else { \ + type * _q = queue; \ + while (_q->next && _q->next != item) { \ + _q = _q->next; \ + } \ + if (_q->next == item) { \ + _q->next = item->next; \ + item->next = NULL; \ + } \ + } \ + } + +#define queueFree(type, queue) while (queue) { type * _q = queue; queue = queue->next; free(_q); } + +#define PCB_STATE_IS_PROBING(s) (s->state > PCB_OFF && s->state < PCB_ANNOUNCE_1) +#define PCB_STATE_IS_ANNOUNCING(s) (s->state > PCB_PROBE_3 && s->state < PCB_RUNNING) +#define PCB_STATE_IS_RUNNING(s) (s->state == PCB_RUNNING) + +#ifndef HOOK_MALLOC_FAILED +#define HOOK_MALLOC_FAILED ESP_LOGE(TAG, "Cannot allocate memory (line: %d, free heap: %" PRIu32 " bytes)", __LINE__, esp_get_free_heap_size()); +#endif + +typedef size_t mdns_if_t; + +typedef enum { + PCB_OFF, PCB_DUP, PCB_INIT, + PCB_PROBE_1, PCB_PROBE_2, PCB_PROBE_3, + PCB_ANNOUNCE_1, PCB_ANNOUNCE_2, PCB_ANNOUNCE_3, + PCB_RUNNING +} mdns_pcb_state_t; + +typedef enum { + MDNS_ANSWER, MDNS_NS, MDNS_EXTRA +} mdns_parsed_record_type_t; + +typedef enum { + ACTION_SYSTEM_EVENT, + ACTION_HOSTNAME_SET, + ACTION_INSTANCE_SET, + ACTION_SEARCH_ADD, + ACTION_SEARCH_SEND, + ACTION_SEARCH_END, + ACTION_BROWSE_ADD, + ACTION_BROWSE_SYNC, + ACTION_BROWSE_END, + ACTION_TX_HANDLE, + ACTION_RX_HANDLE, + ACTION_TASK_STOP, + ACTION_DELEGATE_HOSTNAME_ADD, + ACTION_DELEGATE_HOSTNAME_REMOVE, + ACTION_DELEGATE_HOSTNAME_SET_ADDR, + ACTION_MAX +} mdns_action_type_t; + + +typedef struct { + uint16_t id; + uint16_t flags; + uint16_t questions; //QDCOUNT + uint16_t answers; //ANCOUNT + uint16_t servers; //NSCOUNT + uint16_t additional;//ARCOUNT +} mdns_header_t; + +typedef struct { + char host[MDNS_NAME_BUF_LEN]; // hostname for A/AAAA records, instance name for SRV records + char service[MDNS_NAME_BUF_LEN]; + char proto[MDNS_NAME_BUF_LEN]; + char domain[MDNS_NAME_BUF_LEN]; + uint8_t parts; + uint8_t sub; + bool invalid; +} mdns_name_t; + +typedef struct mdns_parsed_question_s { + struct mdns_parsed_question_s *next; + uint16_t type; + bool sub; + bool unicast; + char *host; + char *service; + char *proto; + char *domain; +} mdns_parsed_question_t; + +typedef struct mdns_parsed_record_s { + struct mdns_parsed_record_s *next; + mdns_parsed_record_type_t record_type; + uint16_t type; + uint16_t clas; + uint8_t flush; + uint32_t ttl; + char *host; + char *service; + char *proto; + char *domain; + uint16_t data_len; + uint8_t *data; +} mdns_parsed_record_t; + +typedef struct { + mdns_if_t tcpip_if; + mdns_ip_protocol_t ip_protocol; + esp_ip_addr_t src; + uint16_t src_port; + uint8_t multicast; + uint8_t authoritative; + uint8_t probe; + uint8_t discovery; + uint8_t distributed; + mdns_parsed_question_t *questions; + mdns_parsed_record_t *records; + uint16_t id; +} mdns_parsed_packet_t; + +typedef struct { + mdns_if_t tcpip_if; + mdns_ip_protocol_t ip_protocol; + struct pbuf *pb; + esp_ip_addr_t src; + esp_ip_addr_t dest; + uint16_t src_port; + uint8_t multicast; +} mdns_rx_packet_t; + +typedef struct mdns_txt_linked_item_s { + const char *key; /*!< item key name */ + char *value; /*!< item value string */ + uint8_t value_len; /*!< item value length */ + struct mdns_txt_linked_item_s *next; /*!< next result, or NULL for the last result in the list */ +} mdns_txt_linked_item_t; + +typedef struct mdns_subtype_s { + const char *subtype; /*!< subtype */ + struct mdns_subtype_s *next; /*!< next result, or NULL for the last result in the list */ +} mdns_subtype_t; + +typedef struct { + const char *instance; + const char *service; + const char *proto; + const char *hostname; + uint16_t priority; + uint16_t weight; + uint16_t port; + mdns_txt_linked_item_t *txt; + mdns_subtype_t *subtype; +} mdns_service_t; + +typedef struct mdns_srv_item_s { + struct mdns_srv_item_s *next; + mdns_service_t *service; +} mdns_srv_item_t; + +typedef struct mdns_out_question_s { + struct mdns_out_question_s *next; + uint16_t type; + bool unicast; + const char *host; + const char *service; + const char *proto; + const char *domain; + bool own_dynamic_memory; +} mdns_out_question_t; + +typedef struct mdns_host_item_t { + const char *hostname; + mdns_ip_addr_t *address_list; + struct mdns_host_item_t *next; +} mdns_host_item_t; + +typedef struct mdns_out_answer_s { + struct mdns_out_answer_s *next; + uint16_t type; + uint8_t bye; + uint8_t flush; + mdns_service_t *service; + mdns_host_item_t *host; + const char *custom_instance; + const char *custom_service; + const char *custom_proto; +} mdns_out_answer_t; + +typedef struct mdns_tx_packet_s { + struct mdns_tx_packet_s *next; + uint32_t send_at; + mdns_if_t tcpip_if; + mdns_ip_protocol_t ip_protocol; + esp_ip_addr_t dst; + uint16_t port; + uint16_t flags; + uint8_t distributed; + mdns_out_question_t *questions; + mdns_out_answer_t *answers; + mdns_out_answer_t *servers; + mdns_out_answer_t *additional; + bool queued; + uint16_t id; +} mdns_tx_packet_t; + +typedef struct { + mdns_pcb_state_t state; + mdns_srv_item_t **probe_services; + uint8_t probe_services_len; + uint8_t probe_ip; + uint8_t probe_running; + uint16_t failed_probes; +} mdns_pcb_t; + +typedef enum { + SEARCH_OFF, + SEARCH_INIT, + SEARCH_RUNNING, + SEARCH_MAX +} mdns_search_once_state_t; + +typedef enum { + BROWSE_OFF, + BROWSE_INIT, + BROWSE_RUNNING, + BROWSE_MAX +} mdns_browse_state_t; + +typedef struct mdns_search_once_s { + struct mdns_search_once_s *next; + + mdns_search_once_state_t state; + uint32_t started_at; + uint32_t sent_at; + uint32_t timeout; + mdns_query_notify_t notifier; + SemaphoreHandle_t done_semaphore; + uint16_t type; + bool unicast; + uint8_t max_results; + uint8_t num_results; + char *instance; + char *service; + char *proto; + mdns_result_t *result; +} mdns_search_once_t; + +typedef struct mdns_browse_s { + struct mdns_browse_s *next; + + mdns_browse_state_t state; + mdns_browse_notify_t notifier; + + char *service; + char *proto; + mdns_result_t *result; +} mdns_browse_t; + +typedef struct mdns_browse_result_sync_t { + mdns_result_t *result; + struct mdns_browse_result_sync_t *next; +} mdns_browse_result_sync_t; + +typedef struct mdns_browse_sync { + mdns_browse_t *browse; + mdns_browse_result_sync_t *sync_result; +} mdns_browse_sync_t; + +typedef struct mdns_server_s { + struct { + mdns_pcb_t pcbs[MDNS_IP_PROTOCOL_MAX]; + } interfaces[MDNS_MAX_INTERFACES]; + const char *hostname; + const char *instance; + mdns_srv_item_t *services; + QueueHandle_t action_queue; + SemaphoreHandle_t action_sema; + mdns_tx_packet_t *tx_queue_head; + mdns_search_once_t *search_once; + esp_timer_handle_t timer_handle; + mdns_browse_t *browse; +} mdns_server_t; + +typedef struct { + mdns_action_type_t type; + union { + struct { + char *hostname; + } hostname_set; + char *instance; + struct { + mdns_if_t interface; + mdns_event_actions_t event_action; + } sys_event; + struct { + mdns_search_once_t *search; + } search_add; + struct { + mdns_tx_packet_t *packet; + } tx_handle; + struct { + mdns_rx_packet_t *packet; + } rx_handle; + struct { + const char *hostname; + mdns_ip_addr_t *address_list; + } delegate_hostname; + struct { + mdns_browse_t *browse; + } browse_add; + struct { + mdns_browse_sync_t *browse_sync; + } browse_sync; + } data; +} mdns_action_t; + +/* + * @brief Convert mnds if to esp-netif handle + * + * @param tcpip_if mdns supported interface as internal enum + * + * @return + * - ptr to esp-netif on success + * - NULL if no available netif for current interface index + */ +esp_netif_t *_mdns_get_esp_netif(mdns_if_t tcpip_if); + + +#endif /* MDNS_PRIVATE_H_ */ diff --git a/code/components/mdns/tests/host_test/CMakeLists.txt b/code/components/mdns/tests/host_test/CMakeLists.txt new file mode 100644 index 00000000..5f0d7bdd --- /dev/null +++ b/code/components/mdns/tests/host_test/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.5) + + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +if(${IDF_TARGET} STREQUAL "linux") + set(EXTRA_COMPONENT_DIRS "../../../../common_components/linux_compat") + set(COMPONENTS main) +endif() + +project(mdns_host) + +# Enable sanitizers only without console (we'd see some leaks on argtable when console exits) +if(NOT CONFIG_TEST_CONSOLE AND CONFIG_IDF_TARGET_LINUX) +idf_component_get_property(mdns mdns COMPONENT_LIB) +target_link_options(${mdns} INTERFACE -fsanitize=address -fsanitize=undefined) +endif() diff --git a/code/components/mdns/tests/host_test/README.md b/code/components/mdns/tests/host_test/README.md new file mode 100644 index 00000000..4953f0e2 --- /dev/null +++ b/code/components/mdns/tests/host_test/README.md @@ -0,0 +1,33 @@ +# Setup dummy network interfaces + +Note: Set two addresses so we could use one as source and another as destination +``` +sudo ip link add eth2 type dummy +sudo ip addr add 192.168.1.200/24 dev eth2 +sudo ip addr add 192.168.1.201/24 dev eth2 +sudo ip link set eth2 up +sudo ifconfig eth2 multicast +``` + +# Dig on a specified interface + +``` +dig +short -b 192.168.1.200 -p 5353 @224.0.0.251 myesp.local +``` + +or a reverse query: +``` +dig +short -b 192.168.2.200 -p 5353 @224.0.0.251 -x 192.168.1.200 +``` + +# Run avahi to browse services + +Avahi needs the netif to have the "multicast" flag set + +```bash +david@david-comp:~/esp/idf (feature/mdns_networking_socket)$ avahi-browse -a -r -p ++;eth2;IPv6;myesp-service2;Web Site;local ++;eth2;IPv4;myesp-service2;Web Site;local +=;eth2;IPv6;myesp-service2;Web Site;local;myesp.local;192.168.1.200;80;"board=esp32" "u=user" "p=password" +=;eth2;IPv4;myesp-service2;Web Site;local;myesp.local;192.168.1.200;80;"board=esp32" "u=user" "p=password" +``` diff --git a/code/components/mdns/tests/host_test/components/esp_netif_linux/CMakeLists.txt b/code/components/mdns/tests/host_test/components/esp_netif_linux/CMakeLists.txt new file mode 100644 index 00000000..6f8d51ec --- /dev/null +++ b/code/components/mdns/tests/host_test/components/esp_netif_linux/CMakeLists.txt @@ -0,0 +1,8 @@ +idf_build_get_property(idf_target IDF_TARGET) +if(${IDF_TARGET} STREQUAL "linux") + idf_component_register(SRCS esp_netif_linux.c + INCLUDE_DIRS include $ENV{IDF_PATH}/components/esp_netif/include + REQUIRES esp_event) +else() + idf_component_register() +endif() diff --git a/code/components/mdns/tests/host_test/components/esp_netif_linux/Kconfig b/code/components/mdns/tests/host_test/components/esp_netif_linux/Kconfig new file mode 100644 index 00000000..3b3c3099 --- /dev/null +++ b/code/components/mdns/tests/host_test/components/esp_netif_linux/Kconfig @@ -0,0 +1,15 @@ +menu "LWIP-MOCK-CONFIG" + + config LWIP_IPV6 + bool "Enable IPv6" + default y + help + Enable/disable IPv6 + + config LWIP_IPV4 + bool "Enable IPv4" + default y + help + Enable/disable IPv4 + +endmenu diff --git a/code/components/mdns/tests/host_test/components/esp_netif_linux/esp_netif_linux.c b/code/components/mdns/tests/host_test/components/esp_netif_linux/esp_netif_linux.c new file mode 100644 index 00000000..93e4386c --- /dev/null +++ b/code/components/mdns/tests/host_test/components/esp_netif_linux/esp_netif_linux.c @@ -0,0 +1,197 @@ +/* + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#include "esp_netif.h" +#include "esp_err.h" +#include //strlen +#include +#include //inet_addr +#include +#include +#include +#include "esp_netif_types.h" +#include "esp_log.h" + +#define MAX_NETIFS 4 + +static const char *TAG = "esp_netif_linux"; + +static esp_netif_t *s_netif_list[MAX_NETIFS] = { 0 }; + +struct esp_netif_obj { + const char *if_key; + const char *if_desc; +}; + +esp_netif_t *esp_netif_get_handle_from_ifkey(const char *if_key) +{ + for (int i = 0; i < MAX_NETIFS; ++i) { + if (s_netif_list[i] && strcmp(s_netif_list[i]->if_key, if_key) == 0) { + return s_netif_list[i]; + } + } + return NULL; +} + +esp_err_t esp_netif_get_ip_info(esp_netif_t *esp_netif, esp_netif_ip_info_t *ip_info) +{ + if (esp_netif == NULL) { + return ESP_ERR_INVALID_STATE; + } + struct ifaddrs *addrs, *tmp; + getifaddrs(&addrs); + tmp = addrs; + + while (tmp) { + if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) { + char addr[20]; + struct sockaddr_in *pAddr = (struct sockaddr_in *) tmp->ifa_addr; + inet_ntop(AF_INET, &pAddr->sin_addr, addr, sizeof(addr) ); + if (strcmp(esp_netif->if_desc, tmp->ifa_name) == 0) { + ESP_LOGD(TAG, "AF_INET4: %s: %s\n", tmp->ifa_name, addr); + memcpy(&ip_info->ip.addr, &pAddr->sin_addr, 4); + } + } + tmp = tmp->ifa_next; + } + freeifaddrs(addrs); + return ESP_OK; +} + +esp_err_t esp_netif_dhcpc_get_status(esp_netif_t *esp_netif, esp_netif_dhcp_status_t *status) +{ + return ESP_OK; +} + +int esp_netif_get_all_ip6(esp_netif_t *esp_netif, esp_ip6_addr_t if_ip6[]) +{ + if (esp_netif == NULL) { + return 0; + } + struct ifaddrs *addrs, *tmp; + int addr_count = 0; + getifaddrs(&addrs); + tmp = addrs; + + while (tmp) { + if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET6) { + struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)tmp->ifa_addr; + if (strcmp(esp_netif->if_desc, tmp->ifa_name) == 0) { + memcpy(&if_ip6[addr_count++], &pAddr->sin6_addr, 4 * 4); + } + } + tmp = tmp->ifa_next; + } + + freeifaddrs(addrs); + return addr_count; +} + +esp_err_t esp_netif_get_ip6_linklocal(esp_netif_t *esp_netif, esp_ip6_addr_t *if_ip6) +{ + if (esp_netif == NULL) { + return ESP_ERR_INVALID_STATE; + } + struct ifaddrs *addrs, *tmp; + getifaddrs(&addrs); + tmp = addrs; + + while (tmp) { + if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET6) { + char addr[64]; + struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)tmp->ifa_addr; + inet_ntop(AF_INET6, &pAddr->sin6_addr, addr, sizeof(addr) ); + if (strcmp(esp_netif->if_desc, tmp->ifa_name) == 0) { + ESP_LOGD(TAG, "AF_INET6: %s: %s\n", tmp->ifa_name, addr); + memcpy(if_ip6->addr, &pAddr->sin6_addr, 4 * 4); + break; + } + } + tmp = tmp->ifa_next; + } + + freeifaddrs(addrs); + return ESP_OK; +} + + +int esp_netif_get_netif_impl_index(esp_netif_t *esp_netif) +{ + if (esp_netif == NULL) { + return -1; + } + uint32_t interfaceIndex = if_nametoindex(esp_netif->if_desc); + return interfaceIndex; +} + +esp_err_t esp_netif_get_netif_impl_name(esp_netif_t *esp_netif, char *name) +{ + if (esp_netif == NULL) { + return ESP_ERR_INVALID_STATE; + } + strcpy(name, esp_netif->if_desc); + return ESP_OK; +} + +const char *esp_netif_get_desc(esp_netif_t *esp_netif) +{ + if (esp_netif == NULL) { + return NULL; + } + return esp_netif->if_desc; +} + +esp_netif_t *esp_netif_new(const esp_netif_config_t *config) +{ + if (esp_netif_get_handle_from_ifkey(config->base->if_key)) { + return NULL; + } + esp_netif_t *netif = calloc(1, sizeof(struct esp_netif_obj)); + if (netif) { + netif->if_desc = config->base->if_desc; + netif->if_key = config->base->if_key; + } + + for (int i = 0; i < MAX_NETIFS; ++i) { + if (s_netif_list[i] == NULL) { + s_netif_list[i] = netif; + break; + } + } + + return netif; +} + +void esp_netif_destroy(esp_netif_t *esp_netif) +{ + for (int i = 0; i < MAX_NETIFS; ++i) { + if (s_netif_list[i] == esp_netif) { + s_netif_list[i] = NULL; + break; + } + } + free(esp_netif); +} + +const char *esp_netif_get_ifkey(esp_netif_t *esp_netif) +{ + return esp_netif->if_key; +} + +esp_err_t esp_netif_str_to_ip4(const char *src, esp_ip4_addr_t *dst) +{ + if (src == NULL || dst == NULL) { + return ESP_ERR_INVALID_ARG; + } + struct in_addr addr; + if (inet_pton(AF_INET, src, &addr) != 1) { + return ESP_FAIL; + } + dst->addr = addr.s_addr; + return ESP_OK; +} diff --git a/code/components/mdns/tests/host_test/components/esp_netif_linux/include/esp_wifi_types.h b/code/components/mdns/tests/host_test/components/esp_netif_linux/include/esp_wifi_types.h new file mode 100644 index 00000000..e878e271 --- /dev/null +++ b/code/components/mdns/tests/host_test/components/esp_netif_linux/include/esp_wifi_types.h @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once +#include "esp_event.h" diff --git a/code/components/mdns/tests/host_test/components/esp_netif_linux/include/machine/endian.h b/code/components/mdns/tests/host_test/components/esp_netif_linux/include/machine/endian.h new file mode 100644 index 00000000..488bce0e --- /dev/null +++ b/code/components/mdns/tests/host_test/components/esp_netif_linux/include/machine/endian.h @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include_next "endian.h" diff --git a/code/components/mdns/tests/host_test/dnsfixture.py b/code/components/mdns/tests/host_test/dnsfixture.py new file mode 100644 index 00000000..6dcf0c99 --- /dev/null +++ b/code/components/mdns/tests/host_test/dnsfixture.py @@ -0,0 +1,130 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 +import logging +import re +import socket +import sys + +import dns.message +import dns.query +import dns.rdataclass +import dns.rdatatype +import dns.resolver + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +class DnsPythonWrapper: + def __init__(self, server='224.0.0.251', port=5353, retries=3): + self.server = server + self.port = port + self.retries = retries + + def send_and_receive_query(self, query, timeout=3): + logger.info(f'Sending DNS query to {self.server}:{self.port}') + try: + # Create a UDP socket + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: + sock.settimeout(timeout) + + # Send the DNS query + query_data = query.to_wire() + sock.sendto(query_data, (self.server, self.port)) + + # Receive the DNS response + response_data, _ = sock.recvfrom(512) # 512 bytes is the typical size for a DNS response + + # Parse the response + response = dns.message.from_wire(response_data) + + return response + + except socket.timeout as e: + logger.warning(f'DNS query timed out: {e}') + return None + except dns.exception.DNSException as e: + logger.error(f'DNS query failed: {e}') + return None + + def run_query(self, name, query_type='PTR', timeout=3): + logger.info(f'Running DNS query for {name} with type {query_type}') + query = dns.message.make_query(name, dns.rdatatype.from_text(query_type), dns.rdataclass.IN) + + # Print the DNS question section + logger.info(f'DNS question section: {query.question}') + + # Send and receive the DNS query + response = None + for attempt in range(1, self.retries + 1): + logger.info(f'Attempt {attempt}/{self.retries}') + response = self.send_and_receive_query(query, timeout) + if response: + break + + if response: + logger.info(f'DNS query response:\n{response}') + else: + logger.warning('No response received or response was invalid.') + + return response + + def parse_answer_section(self, response, query_type): + answers = [] + if response: + for answer in response.answer: + if dns.rdatatype.to_text(answer.rdtype) == query_type: + for item in answer.items: + full_answer = ( + f'{answer.name} {answer.ttl} ' + f'{dns.rdataclass.to_text(answer.rdclass)} ' + f'{dns.rdatatype.to_text(answer.rdtype)} ' + f'{item.to_text()}' + ) + answers.append(full_answer) + return answers + + def check_record(self, name, query_type, expected=True, expect=None): + output = self.run_query(name, query_type=query_type) + answers = self.parse_answer_section(output, query_type) + logger.info(f'answers: {answers}') + if expect is None: + expect = name + if expected: + assert any(expect in answer for answer in answers), f"Expected record '{expect}' not found in answer section" + else: + assert not any(expect in answer for answer in answers), f"Unexpected record '{expect}' found in answer section" + + +if __name__ == '__main__': + if len(sys.argv) < 3: + print('Usage: python dns_fixture.py ') + sys.exit(1) + + query_type = sys.argv[1] + name = sys.argv[2] + ip_only = len(sys.argv) > 3 and sys.argv[3] == '--ip_only' + if ip_only: + logger.setLevel(logging.WARNING) + + dns_wrapper = DnsPythonWrapper() + if query_type == 'X' and '.' in name: + # Sends an IPv4 reverse query + reversed_ip = '.'.join(reversed(name.split('.'))) + name = f'{reversed_ip}.in-addr.arpa' + query_type = 'PTR' + response = dns_wrapper.run_query(name, query_type=query_type) + answers = dns_wrapper.parse_answer_section(response, query_type) + + if answers: + for answer in answers: + logger.info(f'DNS query response: {answer}') + if ip_only: + ipv4_pattern = re.compile(r'\b(?:\d{1,3}\.){3}\d{1,3}\b') + ipv4_addresses = ipv4_pattern.findall(answer) + if ipv4_addresses: + print(f"{', '.join(ipv4_addresses)}") + else: + logger.info(f'No response for {name} with query type {query_type}') + exit(9) # Same as dig timeout diff --git a/code/components/mdns/tests/host_test/main/CMakeLists.txt b/code/components/mdns/tests/host_test/main/CMakeLists.txt new file mode 100644 index 00000000..0e6f226a --- /dev/null +++ b/code/components/mdns/tests/host_test/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS + "." + REQUIRES mdns console nvs_flash) diff --git a/code/components/mdns/tests/host_test/main/Kconfig.projbuild b/code/components/mdns/tests/host_test/main/Kconfig.projbuild new file mode 100644 index 00000000..9bf4bba2 --- /dev/null +++ b/code/components/mdns/tests/host_test/main/Kconfig.projbuild @@ -0,0 +1,21 @@ +menu "Test Configuration" + + config TEST_HOSTNAME + string "mDNS Hostname" + default "esp32-mdns" + help + mDNS Hostname for example to use + + config TEST_NETIF_NAME + string "Network interface name" + default "eth2" + help + Name/ID if the network interface on which we run the mDNS host test + + config TEST_CONSOLE + bool "Start console" + default n + help + Test uses esp_console for interactive testing. + +endmenu diff --git a/code/components/mdns/tests/host_test/main/idf_component.yml b/code/components/mdns/tests/host_test/main/idf_component.yml new file mode 100644 index 00000000..e2d4fe9b --- /dev/null +++ b/code/components/mdns/tests/host_test/main/idf_component.yml @@ -0,0 +1,7 @@ +dependencies: + idf: ">=5.0" + espressif/mdns: + version: "^1.0.0" + override_path: "../../.." + protocol_examples_common: + path: ${IDF_PATH}/examples/common_components/protocol_examples_common diff --git a/code/components/mdns/tests/host_test/main/main.c b/code/components/mdns/tests/host_test/main/main.c new file mode 100644 index 00000000..581231f1 --- /dev/null +++ b/code/components/mdns/tests/host_test/main/main.c @@ -0,0 +1,126 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_console.h" +#include "mdns.h" +#include "mdns_console.h" + +static const char *TAG = "mdns-test"; + +static void mdns_test_app(esp_netif_t *interface); + +#ifdef CONFIG_TEST_CONSOLE +static EventGroupHandle_t s_exit_signal = NULL; + +static int exit_console(int argc, char **argv) +{ + xEventGroupSetBits(s_exit_signal, 1); + return 0; +} + +#else +static void query_mdns_host(const char *host_name) +{ + ESP_LOGI(TAG, "Query A: %s.local", host_name); + + struct esp_ip4_addr addr; + addr.addr = 0; + + esp_err_t err = mdns_query_a(host_name, 2000, &addr); + if (err) { + if (err == ESP_ERR_NOT_FOUND) { + ESP_LOGW(TAG, "%x: Host was not found!", (err)); + return; + } + ESP_LOGE(TAG, "Query Failed: %x", (err)); + return; + } + + ESP_LOGI(TAG, "Query A: %s.local resolved to: " IPSTR, host_name, IP2STR(&addr)); +} +#endif // TEST_CONSOLE + +#ifndef CONFIG_IDF_TARGET_LINUX +#include "protocol_examples_common.h" +#include "esp_event.h" +#include "nvs_flash.h" + +/** + * @brief This is an entry point for the real target device, + * need to init few components and connect to a network interface + */ +void app_main(void) +{ + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + ESP_ERROR_CHECK(example_connect()); + + mdns_test_app(EXAMPLE_INTERFACE); + + ESP_ERROR_CHECK(example_disconnect()); +} +#else + +/** + * @brief This is an entry point for the linux target (simulator on host) + * need to create a dummy WiFi station and use it as mdns network interface + */ +int main(int argc, char *argv[]) +{ + setvbuf(stdout, NULL, _IONBF, 0); + const esp_netif_inherent_config_t base_cg = { .if_key = "WIFI_STA_DEF", .if_desc = CONFIG_TEST_NETIF_NAME }; + esp_netif_config_t cfg = { .base = &base_cg }; + esp_netif_t *sta = esp_netif_new(&cfg); + + mdns_test_app(sta); + + esp_netif_destroy(sta); + return 0; +} +#endif + +static void mdns_test_app(esp_netif_t *interface) +{ + ESP_ERROR_CHECK(mdns_init()); + ESP_ERROR_CHECK(mdns_hostname_set(CONFIG_TEST_HOSTNAME)); + ESP_LOGI(TAG, "mdns hostname set to: [%s]", CONFIG_TEST_HOSTNAME); + ESP_ERROR_CHECK(mdns_register_netif(interface)); + ESP_ERROR_CHECK(mdns_netif_action(interface, MDNS_EVENT_ENABLE_IP4 /*| MDNS_EVENT_ENABLE_IP6 */ | MDNS_EVENT_IP4_REVERSE_LOOKUP | MDNS_EVENT_IP6_REVERSE_LOOKUP)); + +#ifdef CONFIG_TEST_CONSOLE + esp_console_repl_t *repl = NULL; + esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); + esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); + s_exit_signal = xEventGroupCreate(); + + repl_config.prompt = "mdns>"; + // init console REPL environment + ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl)); + + const esp_console_cmd_t cmd_exit = { + .command = "exit", + .help = "exit mDNS console application", + .hint = NULL, + .func = exit_console, + .argtable = NULL + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_exit) ); + mdns_console_register(); + ESP_ERROR_CHECK(esp_console_start_repl(repl)); + xEventGroupWaitBits(s_exit_signal, 1, pdTRUE, pdFALSE, portMAX_DELAY); + repl->del(repl); +#else + vTaskDelay(pdMS_TO_TICKS(10000)); + query_mdns_host("david-work"); + vTaskDelay(pdMS_TO_TICKS(1000)); +#endif + mdns_free(); + ESP_LOGI(TAG, "Exit"); +} diff --git a/code/components/mdns/tests/host_test/pytest_mdns.py b/code/components/mdns/tests/host_test/pytest_mdns.py new file mode 100644 index 00000000..f8b95f55 --- /dev/null +++ b/code/components/mdns/tests/host_test/pytest_mdns.py @@ -0,0 +1,180 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 +import logging + +import pexpect +import pytest +from dnsfixture import DnsPythonWrapper + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) +ipv6_enabled = False + + +class MdnsConsole: + def __init__(self, command): + self.process = pexpect.spawn(command, encoding='utf-8') + self.process.logfile = open('mdns_interaction.log', 'w') # Log all interactions + self.process.expect('mdns> ', timeout=10) + + def send_input(self, input_data): + logger.info(f'Sending to stdin: {input_data}') + self.process.sendline(input_data) + + def get_output(self, expected_data): + logger.info(f'Expecting: {expected_data}') + self.process.expect(expected_data, timeout=10) + output = self.process.before.strip() + logger.info(f'Received from stdout: {output}') + return output + + def terminate(self): + self.send_input('exit') + self.get_output('Exit') + self.process.wait() + self.process.close() + assert self.process.exitstatus == 0 + + +@pytest.fixture(scope='module') +def mdns_console(): + app = MdnsConsole('./build_linux_console/mdns_host.elf') + yield app + app.terminate() + + +@pytest.fixture(scope='module') +def dig_app(): + return DnsPythonWrapper() + + +def test_mdns_init(mdns_console, dig_app): + mdns_console.send_input('mdns_init -h hostname') + mdns_console.get_output('MDNS: Hostname: hostname') + dig_app.check_record('hostname.local', query_type='A', expected=True) + if ipv6_enabled: + dig_app.check_record('hostname.local', query_type='AAAA', expected=True) + + +def test_add_service(mdns_console, dig_app): + mdns_console.send_input('mdns_service_add _http _tcp 80 -i test_service') + mdns_console.get_output('MDNS: Service Instance: test_service') + mdns_console.send_input('mdns_service_lookup _http _tcp') + mdns_console.get_output('PTR : test_service') + dig_app.check_record('_http._tcp.local', query_type='PTR', expected=True) + + +def test_remove_service(mdns_console, dig_app): + mdns_console.send_input('mdns_service_remove _http _tcp') + mdns_console.send_input('mdns_service_lookup _http _tcp') + mdns_console.get_output('No results found!') + dig_app.check_record('_http._tcp.local', query_type='PTR', expected=False) + + +def test_delegate_host(mdns_console, dig_app): + mdns_console.send_input('mdns_delegate_host delegated 1.2.3.4') + dig_app.check_record('delegated.local', query_type='A', expected=True) + + +def test_undelegate_host(mdns_console, dig_app): + mdns_console.send_input('mdns_undelegate_host delegated') + dig_app.check_record('delegated.local', query_type='A', expected=False) + + +def test_add_delegated_service(mdns_console, dig_app): + mdns_console.send_input('mdns_delegate_host delegated 1.2.3.4') + dig_app.check_record('delegated.local', query_type='A', expected=True) + mdns_console.send_input('mdns_service_add _test _tcp 80 -i local') + mdns_console.get_output('MDNS: Service Instance: local') + mdns_console.send_input('mdns_service_add _test2 _tcp 80 -i extern -h delegated') + mdns_console.get_output('MDNS: Service Instance: extern') + mdns_console.send_input('mdns_service_lookup _test _tcp') + mdns_console.get_output('PTR : local') + mdns_console.send_input('mdns_service_lookup _test2 _tcp -d') + mdns_console.get_output('PTR : extern') + dig_app.check_record('_test2._tcp.local', query_type='PTR', expected=True) + dig_app.check_record('extern._test2._tcp.local', query_type='SRV', expected=True) + + +def test_remove_delegated_service(mdns_console, dig_app): + mdns_console.send_input('mdns_service_remove _test2 _tcp -h delegated') + mdns_console.send_input('mdns_service_lookup _test2 _tcp -d') + mdns_console.get_output('No results found!') + dig_app.check_record('_test2._tcp.local', query_type='PTR', expected=False) + # add the delegated service again, would be used in the TXT test + mdns_console.send_input('mdns_service_add _test2 _tcp 80 -i extern -h delegated') + mdns_console.get_output('MDNS: Service Instance: extern') + + +def check_txt_for_service(instance, service, proto, mdns_console, dig_app, host=None, with_inst=False): + for_host_arg = f'-h {host}' if host is not None else '' + for_inst_arg = f'-i {instance}' if with_inst else '' + mdns_console.send_input(f'mdns_service_txt_set {service} {proto} {for_host_arg} {for_inst_arg} key1 value1') + dig_app.check_record(f'{instance}.{service}.{proto}.local', query_type='SRV', expected=True) + dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=True, expect='key1=value1') + mdns_console.send_input(f'mdns_service_txt_set {service} {proto} {for_host_arg} {for_inst_arg} key2 value2') + dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=True, expect='key2=value2') + mdns_console.send_input(f'mdns_service_txt_remove {service} {proto} {for_host_arg} {for_inst_arg} key2') + dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=False, expect='key2=value2') + dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=True, expect='key1=value1') + mdns_console.send_input(f'mdns_service_txt_replace {service} {proto} {for_host_arg} {for_inst_arg} key3=value3 key4=value4') + dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=False, expect='key1=value1') + dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=True, expect='key3=value3') + dig_app.check_record(f'{service}.{proto}.local', query_type='TXT', expected=True, expect='key4=value4') + + +def test_update_txt(mdns_console, dig_app): + check_txt_for_service('local', '_test', '_tcp', mdns_console=mdns_console, dig_app=dig_app) + check_txt_for_service('local', '_test', '_tcp', mdns_console=mdns_console, dig_app=dig_app, with_inst=True) + + +def test_update_delegated_txt(mdns_console, dig_app): + check_txt_for_service('extern', '_test2', '_tcp', mdns_console=mdns_console, dig_app=dig_app, host='delegated') + check_txt_for_service('extern', '_test2', '_tcp', mdns_console=mdns_console, dig_app=dig_app, host='delegated', with_inst=True) + + +def test_service_port_set(mdns_console, dig_app): + dig_app.check_record('local._test._tcp.local', query_type='SRV', expected=True, expect='80') + mdns_console.send_input('mdns_service_port_set _test _tcp 81') + dig_app.check_record('local._test._tcp.local', query_type='SRV', expected=True, expect='81') + mdns_console.send_input('mdns_service_port_set _test2 _tcp -h delegated 82') + dig_app.check_record('extern._test2._tcp.local', query_type='SRV', expected=True, expect='82') + mdns_console.send_input('mdns_service_port_set _test2 _tcp -h delegated -i extern 83') + dig_app.check_record('extern._test2._tcp.local', query_type='SRV', expected=True, expect='83') + mdns_console.send_input('mdns_service_port_set _test2 _tcp -h delegated -i invalid_inst 84') + mdns_console.get_output('ESP_ERR_NOT_FOUND') + dig_app.check_record('extern._test2._tcp.local', query_type='SRV', expected=True, expect='83') + + +def test_service_subtype(mdns_console, dig_app): + dig_app.check_record('local._test._tcp.local', query_type='SRV', expected=True) + mdns_console.send_input('mdns_service_subtype _test _tcp _subtest -i local') + dig_app.check_record('_subtest._sub._test._tcp.local', query_type='PTR', expected=True) + mdns_console.send_input('mdns_service_subtype _test2 _tcp _subtest2 -i extern -h delegated') + dig_app.check_record('_subtest2._sub._test2._tcp.local', query_type='PTR', expected=True) + + +def test_service_set_instance(mdns_console, dig_app): + dig_app.check_record('local._test._tcp.local', query_type='SRV', expected=True) + mdns_console.send_input('mdns_service_instance_set _test _tcp local2') + dig_app.check_record('local2._test._tcp.local', query_type='SRV', expected=True) + mdns_console.send_input('mdns_service_instance_set _test2 _tcp extern2 -h delegated') + mdns_console.send_input('mdns_service_lookup _test2 _tcp -d') + mdns_console.get_output('PTR : extern2') + dig_app.check_record('extern2._test2._tcp.local', query_type='SRV', expected=True) + mdns_console.send_input('mdns_service_instance_set _test2 _tcp extern3 -h delegated -i extern') + mdns_console.get_output('ESP_ERR_NOT_FOUND') + + +def test_service_remove_all(mdns_console, dig_app): + mdns_console.send_input('mdns_service_remove_all') + mdns_console.send_input('mdns_service_lookup _test2 _tcp -d') + mdns_console.get_output('No results found!') + mdns_console.send_input('mdns_service_lookup _test _tcp') + mdns_console.get_output('No results found!') + dig_app.check_record('_test._tcp.local', query_type='PTR', expected=False) + + +if __name__ == '__main__': + pytest.main(['-s', 'test_mdns.py']) diff --git a/code/components/mdns/tests/host_test/sdkconfig.ci.app b/code/components/mdns/tests/host_test/sdkconfig.ci.app new file mode 100644 index 00000000..809c518a --- /dev/null +++ b/code/components/mdns/tests/host_test/sdkconfig.ci.app @@ -0,0 +1,4 @@ +CONFIG_IDF_TARGET="linux" +CONFIG_TEST_HOSTNAME="myesp" +CONFIG_MDNS_ENABLE_DEBUG_PRINTS=y +CONFIG_MDNS_RESPOND_REVERSE_QUERIES=y diff --git a/code/components/mdns/tests/host_test/sdkconfig.ci.console b/code/components/mdns/tests/host_test/sdkconfig.ci.console new file mode 100644 index 00000000..3b6dcb67 --- /dev/null +++ b/code/components/mdns/tests/host_test/sdkconfig.ci.console @@ -0,0 +1,4 @@ +CONFIG_IDF_TARGET="linux" +CONFIG_ESP_EVENT_POST_FROM_ISR=n +CONFIG_MDNS_ENABLE_CONSOLE_CLI=y +CONFIG_TEST_CONSOLE=y diff --git a/code/components/mdns/tests/host_test/sdkconfig.ci.target b/code/components/mdns/tests/host_test/sdkconfig.ci.target new file mode 100644 index 00000000..2a43df1a --- /dev/null +++ b/code/components/mdns/tests/host_test/sdkconfig.ci.target @@ -0,0 +1 @@ +CONFIG_IDF_TARGET="esp32" diff --git a/code/components/mdns/tests/host_test/sdkconfig.defaults b/code/components/mdns/tests/host_test/sdkconfig.defaults new file mode 100644 index 00000000..7e79ce49 --- /dev/null +++ b/code/components/mdns/tests/host_test/sdkconfig.defaults @@ -0,0 +1,6 @@ +CONFIG_TEST_NETIF_NAME="eth0" +CONFIG_ESP_EVENT_POST_FROM_ISR=n +CONFIG_MDNS_NETWORKING_SOCKET=y +CONFIG_MDNS_SKIP_SUPPRESSING_OWN_QUERIES=y +CONFIG_MDNS_PREDEF_NETIF_STA=n +CONFIG_MDNS_PREDEF_NETIF_AP=n diff --git a/code/components/mdns/tests/test_afl_fuzz_host/CMakeLists.txt b/code/components/mdns/tests/test_afl_fuzz_host/CMakeLists.txt new file mode 100644 index 00000000..d6584519 --- /dev/null +++ b/code/components/mdns/tests/test_afl_fuzz_host/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following four 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) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(fuzz_test_update) diff --git a/code/components/mdns/tests/test_afl_fuzz_host/README.md b/code/components/mdns/tests/test_afl_fuzz_host/README.md new file mode 100644 index 00000000..e22a8960 --- /dev/null +++ b/code/components/mdns/tests/test_afl_fuzz_host/README.md @@ -0,0 +1,80 @@ +## Introduction +This test uses [american fuzzy lop](http://lcamtuf.coredump.cx/afl/) to mangle real mdns packets and look for exceptions caused by the parser. + +A few actual packets are collected and exported as bins in the `in` folder, which is then passed as input to AFL when testing. The setup procedure for the test includes all possible services and scenarios that could be used with the given input packets.The output of the parser before fuzzing can be found in [input_packets.txt](input_packets.txt) + +## Building and running the tests using AFL +To build and run the tests using AFL(afl-clang-fast) instrumentation + +```bash +cd $IDF_PATH/components/mdns/test_afl_host +make fuzz +``` + +(Please note you have to install AFL instrumentation first, check `Installing AFL` section) + +## Building the tests using GCC INSTR(off) + +To build the tests without AFL instrumentations and instead of that use GCC compiler(In this case it will only check for compilation issues and will not run AFL tests). + +```bash +cd $IDF_PATH/components/mdns/test_afl_host +make INSTR=off +``` + +Note, that this setup is useful if we want to reproduce issues reported by fuzzer tests executed in the CI, or to simulate how the packet parser treats the input packets on the host machine. + +## Installing AFL +To run the test yourself, you need to download the [latest afl archive](http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz) and extract it to a folder on your computer. + +The rest of the document will refer to that folder as ```PATH_TO_AFL```. + +### Preparation +- On Mac, you will need to install the latest Xcode and llvm support from [Homebrew](https://brew.sh) + + ```bash + /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + brew install --with-clang --with-lld --HEAD llvm + export PATH="/usr/local/opt/llvm/bin:$PATH" + ``` + +- On Ubuntu you need the following packages: + + ```bash + sudo apt-get install make clang-4.0(or <=4.0) llvm-4.0(or <=4.0) libbsd-dev + ``` + +Please note that if specified package version can't be installed(due to system is the latest), you can download, build and install it manually. + +### Compile AFL +Compiling AFL is as easy as running make: + +```bash +cd [PATH_TO_AFL] +make +cd llvm_mode/ +make +``` + +After successful compilation, you can export the following variables to your shell (you can also add them to your profile if you want to use AFL in other projects). + +```bash +export AFL_PATH=[PATH_TO_AFL] +export PATH="$AFL_PATH:$PATH" +``` + +Please note LLVM must be <=4.0.0, otherwise afl does not compile, as there are some limitations with building AFL on MacOS/Linux with the latest LLVM. Also, Windows build on cygwin is not fully supported. + +## Additional info +Apple has a crash reporting service that could interfere with AFLs normal operation. To turn that off, run the following command: + +```bash +launchctl unload -w /System/Library/LaunchAgents/com.apple.ReportCrash.plist +sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.ReportCrash.Root.plist +``` + +Ubuntu has a similar service. To turn that off, run as root: + +```bash +echo core >/proc/sys/kernel/core_pattern +``` diff --git a/code/components/mdns/tests/test_afl_fuzz_host/esp32_mock.c b/code/components/mdns/tests/test_afl_fuzz_host/esp32_mock.c new file mode 100644 index 00000000..5830c251 --- /dev/null +++ b/code/components/mdns/tests/test_afl_fuzz_host/esp32_mock.c @@ -0,0 +1,127 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include +#include +#include +#include +#include "esp32_mock.h" +#include "esp_log.h" + +void *g_queue; +int g_queue_send_shall_fail = 0; +int g_size = 0; + +const char *WIFI_EVENT = "wifi_event"; +const char *ETH_EVENT = "eth_event"; + +esp_err_t esp_event_handler_register(const char *event_base, + int32_t event_id, + void *event_handler, + void *event_handler_arg) +{ + return ESP_OK; +} + +esp_err_t esp_event_handler_unregister(const char *event_base, int32_t event_id, void *event_handler) +{ + return ESP_OK; +} + +esp_err_t esp_timer_delete(esp_timer_handle_t timer) +{ + return ESP_OK; +} + +esp_err_t esp_timer_stop(esp_timer_handle_t timer) +{ + return ESP_OK; +} + +esp_err_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period) +{ + return ESP_OK; +} + +esp_err_t esp_timer_create(const esp_timer_create_args_t *create_args, + esp_timer_handle_t *out_handle) +{ + return ESP_OK; +} + +uint32_t xTaskGetTickCount(void) +{ + static uint32_t tick = 0; + return tick++; +} + +/// Queue mock +QueueHandle_t xQueueCreate( uint32_t uxQueueLength, uint32_t uxItemSize ) +{ + g_size = uxItemSize; + g_queue = malloc((uxQueueLength) * (uxItemSize)); + return g_queue; +} + + +void vQueueDelete( QueueHandle_t xQueue ) +{ + free(xQueue); +} + +uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait) +{ + if (g_queue_send_shall_fail) { + return pdFALSE; + } else { + memcpy(xQueue, pvItemToQueue, g_size); + return pdPASS; + } +} + + +uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait) +{ + return pdFALSE; +} + +void GetLastItem(void *pvBuffer) +{ + memcpy(pvBuffer, g_queue, g_size); +} + +void ForceTaskDelete(void) +{ + g_queue_send_shall_fail = 1; +} + +TaskHandle_t xTaskGetCurrentTaskHandle(void) +{ + return NULL; +} + +void xTaskNotifyGive(TaskHandle_t task) +{ + return; +} + +BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time) +{ + return pdTRUE; +} + +void esp_log_write(esp_log_level_t level, const char *tag, const char *format, ...) +{ +} + +void esp_log(esp_log_config_t config, const char *tag, const char *format, ...) +{ +} + +uint32_t esp_log_timestamp(void) +{ + return 0; +} diff --git a/code/components/mdns/tests/test_afl_fuzz_host/esp32_mock.h b/code/components/mdns/tests/test_afl_fuzz_host/esp32_mock.h new file mode 100644 index 00000000..e8e84ceb --- /dev/null +++ b/code/components/mdns/tests/test_afl_fuzz_host/esp32_mock.h @@ -0,0 +1,136 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _ESP32_COMPAT_H_ +#define _ESP32_COMPAT_H_ + +// Skip these include files +#define ESP_MDNS_NETWORKING_H_ +#define INC_FREERTOS_H +#define QUEUE_H +#define SEMAPHORE_H +#define _ESP_TASK_H_ + +#ifdef USE_BSD_STRING +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include "esp_timer.h" + +#define ESP_FAIL -1 + +#define ESP_ERR_NO_MEM 0x101 +#define ESP_ERR_INVALID_ARG 0x102 +#define ESP_ERR_INVALID_STATE 0x103 +#define ESP_ERR_INVALID_SIZE 0x104 +#define ESP_ERR_NOT_FOUND 0x105 +#define ESP_ERR_NOT_SUPPORTED 0x106 +#define ESP_ERR_TIMEOUT 0x107 +#define ESP_ERR_INVALID_RESPONSE 0x108 +#define ESP_ERR_INVALID_CRC 0x109 + +#define pdTRUE true +#define pdFALSE false +#define pdPASS ( pdTRUE ) +#define pdFAIL ( pdFALSE ) + +#define portMAX_DELAY 0xFFFFFFFF +#define portTICK_PERIOD_MS 1 +#define LWIP_HDR_PBUF_H +#define __ESP_RANDOM_H__ +#define INC_TASK_H + +#define pdMS_TO_TICKS(a) a +#define xSemaphoreTake(s,d) true +#define xTaskDelete(a) +#define vTaskDelete(a) free(a) +#define xSemaphoreGive(s) +#define xQueueCreateMutex(s) +#define _mdns_pcb_init(a,b) true +#define _mdns_pcb_deinit(a,b) true +#define xSemaphoreCreateMutex() malloc(1) +#define xSemaphoreCreateBinary() malloc(1) +#define vSemaphoreDelete(s) free(s) +#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U +#define xTaskCreatePinnedToCore(a,b,c,d,e,f,g) *(f) = malloc(1) +#define vTaskDelay(m) usleep((m)*0) +#define esp_random() (rand()%UINT32_MAX) + + +#define ESP_TASK_PRIO_MAX 25 +#define ESP_TASKD_EVENT_PRIO 5 +#define _mdns_udp_pcb_write(tcpip_if, ip_protocol, ip, port, data, len) len +#define TaskHandle_t TaskHandle_t + + +typedef int32_t esp_err_t; + +typedef void *SemaphoreHandle_t; +typedef void *QueueHandle_t; +typedef void *TaskHandle_t; +typedef int BaseType_t; +typedef uint32_t TickType_t; + + +struct udp_pcb { + uint8_t dummy; +}; + +struct ip4_addr { + uint32_t addr; +}; +typedef struct ip4_addr ip4_addr_t; + +struct ip6_addr { + uint32_t addr[4]; +}; +typedef struct ip6_addr ip6_addr_t; + +typedef void *system_event_t; + +struct pbuf { + struct pbuf *next; + void *payload; + uint16_t tot_len; + uint16_t len; + uint8_t /*pbuf_type*/ type; + uint8_t flags; + uint16_t ref; +}; + +uint32_t xTaskGetTickCount(void); +typedef void (*esp_timer_cb_t)(void *arg); + +// Queue mock +QueueHandle_t xQueueCreate( uint32_t uxQueueLength, + uint32_t uxItemSize ); + +void vQueueDelete( QueueHandle_t xQueue ); + +uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait); + +uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait); + +void GetLastItem(void *pvBuffer); + +void ForceTaskDelete(void); + +esp_err_t esp_event_handler_register(const char *event_base, int32_t event_id, void *event_handler, void *event_handler_arg); + +esp_err_t esp_event_handler_unregister(const char *event_base, int32_t event_id, void *event_handler); + + +TaskHandle_t xTaskGetCurrentTaskHandle(void); +void xTaskNotifyGive(TaskHandle_t task); +BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time ); + +#endif //_ESP32_COMPAT_H_ diff --git a/code/components/mdns/tests/test_afl_fuzz_host/esp_attr.h b/code/components/mdns/tests/test_afl_fuzz_host/esp_attr.h new file mode 100644 index 00000000..75b66786 --- /dev/null +++ b/code/components/mdns/tests/test_afl_fuzz_host/esp_attr.h @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#pragma once +#define IRAM_ATTR +#define FLAG_ATTR(TYPE) +#define QUEUE_H +#define __ARCH_CC_H__ +#define __XTENSA_API_H__ +#define SSIZE_MAX INT_MAX +#define LWIP_HDR_IP6_ADDR_H +#define LWIP_HDR_IP4_ADDR_H diff --git a/code/components/mdns/tests/test_afl_fuzz_host/esp_netif_mock.c b/code/components/mdns/tests/test_afl_fuzz_host/esp_netif_mock.c new file mode 100644 index 00000000..80629ad6 --- /dev/null +++ b/code/components/mdns/tests/test_afl_fuzz_host/esp_netif_mock.c @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "esp32_mock.h" + +typedef struct esp_netif_s esp_netif_t; +typedef struct esp_netif_ip_info esp_netif_ip_info_t; +typedef struct esp_netif_dhcp_status esp_netif_dhcp_status_t; + + +const char *IP_EVENT = "IP_EVENT"; + + +esp_err_t esp_netif_add_to_list(esp_netif_t *netif) +{ + return ESP_OK; +} + +esp_err_t esp_netif_remove_from_list(esp_netif_t *netif) +{ + return ESP_ERR_NOT_FOUND; +} + +esp_netif_t *esp_netif_next(esp_netif_t *netif) +{ + return NULL; +} + +esp_netif_t *esp_netif_next_unsafe(esp_netif_t *netif) +{ + return NULL; +} + +esp_netif_t *esp_netif_get_handle_from_ifkey(const char *if_key) +{ + return NULL; +} + +esp_err_t esp_netif_get_ip_info(esp_netif_t *esp_netif, esp_netif_ip_info_t *ip_info) +{ + return ESP_ERR_NOT_SUPPORTED; +} + +esp_err_t esp_netif_dhcpc_get_status(esp_netif_t *esp_netif, esp_netif_dhcp_status_t *status) +{ + return ESP_ERR_NOT_SUPPORTED; +} diff --git a/code/components/mdns/tests/test_afl_fuzz_host/in/file2.bin b/code/components/mdns/tests/test_afl_fuzz_host/in/file2.bin new file mode 100644 index 0000000000000000000000000000000000000000..a7ce859cc0fdf7cef7985d51d480e03cef0bde7e GIT binary patch literal 33 kcmZQz00Kr3!N9*z=%+RBzQm% hBz1s?L6LzGXixSyIIkUy@wFnv;?$zD)FPJn;?g9x__U(Tk}8(?lH>x`oc!d(90mp+2F3$CAd-PW Ra1sL#XKrR*CRo)0NdPDN5l#RA literal 0 HcmV?d00001 diff --git a/code/components/mdns/tests/test_afl_fuzz_host/in/telnet_ptr.bin b/code/components/mdns/tests/test_afl_fuzz_host/in/telnet_ptr.bin new file mode 100644 index 0000000000000000000000000000000000000000..1cd8e85d8021f55039566cdd619235aeb952ffa6 GIT binary patch literal 72 zcmZQzXkh>XMi9XnUz}Q0mYJMd%pRYTSFBr{!V+JaQox#%pPZP(z`z4iB{+paggw3_ PH775%geATt872z=Qoj-} literal 0 HcmV?d00001 diff --git a/code/components/mdns/tests/test_afl_fuzz_host/in/test-14.bin b/code/components/mdns/tests/test_afl_fuzz_host/in/test-14.bin new file mode 100644 index 0000000000000000000000000000000000000000..b9d059d8202682602e7232a8505c495a3047e3a4 GIT binary patch literal 568 zcmaiwPfNov7{)Vc-GqW7j)|wjsGcf~S~hRiF&W~a2k#+k0~WgWB}sAnMEe#aev*0f zYnYmKpg5L8@;*Gj_g@gw=M=T;r$cDQq2$scG66d;6PhFBpq5SEqy*Ki;|>9y6_W8k z(juaoD$gKwot!QVViQiwP4*$FL_!N=w2csr$^a@cZR*xs(X_FMHk)!f+~gb+NOM1> zk_yQgof*O+&`j$PkuwH9@N&MeNR@)Rs7LhfhXIZT<8b&C;fD#&geg za3+LG&Fd@xs>KLTNcn;-*5pXh{6Hl3Gf@0Bv?l!J7wTd;niT zpQFAGPvJB)ejMCnH#5JT{b&BO0M9^cBj1M?YNV6sL|5fVbt9Ej+lyF*S{proq+$Y_ zy(EZB<0$aQ0F|dTfW0^Pc%S>15?e0gf=P$9ZRU2E6gBl1yUeqg)y}c?t9peygs&#} z7|j&Zdeo=X8zO0VLkn$-((43yUL=X?$Ick}!j;^kSawken}R!yj@dNbnpQ!IPSfSw z6t(+@>poibXblSGtk>cz>tEHauqc}6a-MwrlizE$|MzI+T(JBX1>O+*nyq$)j zQylnX9d7k~Ow=qiw8BP1a9+~%qG`nQ)xD8Uv1!|NXD5mA#_QH@&$~uSCEPYe#h+j? zazWa{-WhziU)BS1Dn2W)3#)mrXUl8}NBQ(&0aZF-|Mg&HNV7tR6XMi3VDD9S7@$yYB{2+7P%Rd7x$C@#%O<%mzrEGoz^Dq)E)NiJZ`$xlwq zVPFtw0BRAO!mwg_d7`hQt+|1&xv{RZldhAAuCt}Cld(<_s<4x;fr+lGIY`(DNi{^+ z0x0a4YnzvpS*%-zV0U=oA+vm)V+|Sr{1T8R{fu1*g~=8yw(a(1*I26Ub&|U}#`) F005APLjnK* literal 0 HcmV?d00001 diff --git a/code/components/mdns/tests/test_afl_fuzz_host/in/test-28.bin b/code/components/mdns/tests/test_afl_fuzz_host/in/test-28.bin new file mode 100644 index 0000000000000000000000000000000000000000..537352ae67db92f820ff0d1299dae3534eef1f48 GIT binary patch literal 62 zcmZQz00Kr3!N4HuQIuI+lCNH@5R#djs^FYhP+Xdm${nAWR*+woT2zu;z!DE6Sab4| L6LT0C6dM=;y^9dW literal 0 HcmV?d00001 diff --git a/code/components/mdns/tests/test_afl_fuzz_host/in/test-29.bin b/code/components/mdns/tests/test_afl_fuzz_host/in/test-29.bin new file mode 100644 index 0000000000000000000000000000000000000000..837737b32e12e3f33ff3f1c3b59f6ff351b05fba GIT binary patch literal 39 qcmZQz00Jfu!N9XCLm_y%+1WpOe@MPsbbB^PfpBXV328G1WHse2>fdR(m)`h7xI6{k-wY= bco-O=ax4c{{1X83^`U$YAe+U3fq?@6q#+cG literal 0 HcmV?d00001 diff --git a/code/components/mdns/tests/test_afl_fuzz_host/in/test-53.bin b/code/components/mdns/tests/test_afl_fuzz_host/in/test-53.bin new file mode 100644 index 0000000000000000000000000000000000000000..73181ea8c378d33efed725aa209387113edffdd3 GIT binary patch literal 140 zcmZQz00Jf;1Ok@$;@l*b_>$xT)|~w0#2f|&9tKA7;QX}A%0ykyvc$anvczIt-^666 z{QT?#VhoH7j0bo?Y8V)}zcR>5K~yS0RX%E%S*!q4%mY{WjX^@xqbReuBwxK)AtW<5 PRlzy2ptv+A^#Bh5gdisG literal 0 HcmV?d00001 diff --git a/code/components/mdns/tests/test_afl_fuzz_host/in/test-56.bin b/code/components/mdns/tests/test_afl_fuzz_host/in/test-56.bin new file mode 100644 index 0000000000000000000000000000000000000000..980b6516a20908c4b681b1cf3717c7ef35f062b6 GIT binary patch literal 262 zcmZQzXkh>XCLm@L_9)6MF3Eq?Ftb>}H!)cuH#0AjCq5;$EHgP(H#09SpC!H|xqvk% zKRGdnfkA+Qk%2*Q3WJP5ZhlH?jxA6JNQ0S<5m$b3MOkW5v8|yQOMG!|(g9rt9;jk5 zY<3)oKTyCR&;T)xkpak21aTND7=%D>W8BLLGB~X$v!v>P(SfB5GEiB8e+?jc1_lwm xkpDZ5{N)77F+$~74y^bm05nM-YAh#^&C0;gz~BH>1QrAu&jDn!I5049006}!N5=pF literal 0 HcmV?d00001 diff --git a/code/components/mdns/tests/test_afl_fuzz_host/in/test-63.bin b/code/components/mdns/tests/test_afl_fuzz_host/in/test-63.bin new file mode 100644 index 0000000000000000000000000000000000000000..883d444ba269c8938aa87f3ba8da53a1305f9763 GIT binary patch literal 147 zcmZQz00Jf;1Oo2(#I%C^vecrIAisgkVmtU4x ztm~VY?3ACMeL$9hk%9354@eEr1V;v0DTqo1sLDqTGm8~qig}O}N{D(CWfqs@s~0PT SWag$SI42eqm*%7%-~j;3q9}|2 literal 0 HcmV?d00001 diff --git a/code/components/mdns/tests/test_afl_fuzz_host/in/test-83.bin b/code/components/mdns/tests/test_afl_fuzz_host/in/test-83.bin new file mode 100644 index 0000000000000000000000000000000000000000..c6cc159a2597bc35b4316a796af18534bfee7dc3 GIT binary patch literal 105 zcmZQzXkh>XMj&Pq56(}^tW4DPEKAJGFH0=e^-WB6%FoYc&B;$r%mFHD04b|rU^%ej opU43o1{o+{;9mn!90-_XHvj*(sc11!P9G}A0c5i{Ffecc09-p6=Kufz literal 0 HcmV?d00001 diff --git a/code/components/mdns/tests/test_afl_fuzz_host/in/test-88.bin b/code/components/mdns/tests/test_afl_fuzz_host/in/test-88.bin new file mode 100644 index 0000000000000000000000000000000000000000..fcbb384438a28961526215b8b66e32193b6fd0e8 GIT binary patch literal 48 xcmZQz00Jfu!N9;0UzC=_5?_*Bz?zevoS4JFz{9}E5ucb@RFGd(azGHo1pvO@39SGC literal 0 HcmV?d00001 diff --git a/code/components/mdns/tests/test_afl_fuzz_host/in/test-89.bin b/code/components/mdns/tests/test_afl_fuzz_host/in/test-89.bin new file mode 100644 index 0000000000000000000000000000000000000000..d60a987cad92fe5e9e86e83ea8d2ee16f7fe93e2 GIT binary patch literal 459 zcmZut!AiqG6nsjH6?&*>t%!$uQZQS0laj>4LYts?QG|-ulBSVH(x&XjT5mmg=~3_> ztY`gzT*ZHizSvY0e6Z}id9$-S3-AC~%eYnwZO0eGP(-mV!P{&eAgrX;5v-UP|9p7oBgos1w8f0EOdo2ePiw} zNje}4^KDp~;Po(uTs%uyd;q1i$LGwR@0_zHHz+YQVq3(j6Z?o*nj-$v7NK==Y^1dM zOFgGeq|f_&(D5aSJQ-nY_c&vUuzMxFc@pCZ#o|bZ%N*uF-f3J6VQOGQliZLVkQEZ||>|XE&P_Qhxn0o}&II^Gj0M Z$Nq4kL7Zy<6!6?PhySpOI1ey^#uu&jeZK$z literal 0 HcmV?d00001 diff --git a/code/components/mdns/tests/test_afl_fuzz_host/in/test-95.bin b/code/components/mdns/tests/test_afl_fuzz_host/in/test-95.bin new file mode 100644 index 0000000000000000000000000000000000000000..26553090fa6d7cddb5fb7c748fc72d8d20c77364 GIT binary patch literal 286 zcmZQz00L$PHW0-fpO{vVUzS=_l3c(N4XCLm@O^(e|LF3DFfRtU+=O;vDCEGRC`N#%)8NiEAvPSwrKOUq}8FG((7 z&B;$r%wb>1yv^6)dGB>tzcCvCZv2wPwax&IQObK(dHK;H&FfedS$u_cu z^DV$~ezs8iK?*}{O-<7hlaefwbQ8^tlXOi@Obm4`EzC`H%~Dg6Obtv@lZ*`#fi^3G pY-XrnkOhSV1E&Zi7>ad4{?UaRc)$dxP#@|pb->payload; +} + +static inline size_t _mdns_get_packet_len(mdns_rx_packet_t *packet) +{ + return packet->pb->len; +} + +static inline void _mdns_packet_free(mdns_rx_packet_t *packet) +{ + free(packet->pb); + free(packet); +} + +static inline bool mdns_is_netif_ready(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol) +{ + return true; +} diff --git a/code/components/mdns/tests/test_afl_fuzz_host/sdkconfig.h b/code/components/mdns/tests/test_afl_fuzz_host/sdkconfig.h new file mode 100644 index 00000000..da75d147 --- /dev/null +++ b/code/components/mdns/tests/test_afl_fuzz_host/sdkconfig.h @@ -0,0 +1,424 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* + * This config file commited in order to not run `idf.py reconfigure` each time when running fuzzer test. You can modify it manually or run `idf.py reconfigure` to generate new one if needed. + * Espressif IoT Development Framework (ESP-IDF) Configuration Header + */ +#pragma once +#define CONFIG_IDF_TARGET "esp32" +#define CONFIG_IDF_TARGET_ESP32 1 +#define CONFIG_IDF_FIRMWARE_CHIP_ID 0x0000 +#define CONFIG_SDK_TOOLPREFIX "xtensa-esp32-elf-" +#define CONFIG_APP_BUILD_TYPE_APP_2NDBOOT 1 +#define CONFIG_APP_BUILD_GENERATE_BINARIES 1 +#define CONFIG_APP_BUILD_BOOTLOADER 1 +#define CONFIG_APP_BUILD_USE_FLASH_SECTIONS 1 +#define CONFIG_APP_COMPILE_TIME_DATE 1 +#define CONFIG_APP_RETRIEVE_LEN_ELF_SHA 16 +#define CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE 1 +#define CONFIG_BOOTLOADER_LOG_LEVEL_INFO 1 +#define CONFIG_BOOTLOADER_LOG_LEVEL 3 +#define CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V 1 +#define CONFIG_BOOTLOADER_WDT_ENABLE 1 +#define CONFIG_BOOTLOADER_WDT_TIME_MS 9000 +#define CONFIG_BOOTLOADER_RESERVE_RTC_SIZE 0x0 +#define CONFIG_ESPTOOLPY_WITH_STUB 1 +#define CONFIG_ESPTOOLPY_FLASHMODE_DIO 1 +#define CONFIG_ESPTOOLPY_FLASHMODE "dio" +#define CONFIG_ESPTOOLPY_FLASHFREQ_40M 1 +#define CONFIG_ESPTOOLPY_FLASHFREQ "40m" +#define CONFIG_ESPTOOLPY_FLASHSIZE_2MB 1 +#define CONFIG_ESPTOOLPY_FLASHSIZE "2MB" +#define CONFIG_ESPTOOLPY_FLASHSIZE_DETECT 1 +#define CONFIG_ESPTOOLPY_BEFORE_RESET 1 +#define CONFIG_ESPTOOLPY_BEFORE "default_reset" +#define CONFIG_ESPTOOLPY_AFTER_RESET 1 +#define CONFIG_ESPTOOLPY_AFTER "hard_reset" +#define CONFIG_PARTITION_TABLE_SINGLE_APP 1 +#define CONFIG_PARTITION_TABLE_CUSTOM_FILENAME "partitions.csv" +#define CONFIG_PARTITION_TABLE_FILENAME "partitions_singleapp.csv" +#define CONFIG_PARTITION_TABLE_OFFSET 0x8000 +#define CONFIG_PARTITION_TABLE_MD5 1 +#define CONFIG_COMPILER_OPTIMIZATION_DEFAULT 1 +#define CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE 1 +#define CONFIG_COMPILER_STACK_CHECK_MODE_NONE 1 +#define CONFIG_APPTRACE_DEST_NONE 1 +#define CONFIG_APPTRACE_LOCK_ENABLE 1 +#define CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_EFF 0 +#define CONFIG_BTDM_CTRL_BLE_MAX_CONN_EFF 0 +#define CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN_EFF 0 +#define CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN_EFF 0 +#define CONFIG_BTDM_CTRL_PINNED_TO_CORE 0 +#define CONFIG_BTDM_BLE_SLEEP_CLOCK_ACCURACY_INDEX_EFF 1 +#define CONFIG_BTDM_RESERVE_DRAM 0x0 +#define CONFIG_COAP_MBEDTLS_PSK 1 +#define CONFIG_COAP_LOG_DEFAULT_LEVEL 0 +#define CONFIG_ADC_DISABLE_DAC 1 +#define CONFIG_SPI_MASTER_ISR_IN_IRAM 1 +#define CONFIG_SPI_SLAVE_ISR_IN_IRAM 1 +#define CONFIG_EFUSE_CODE_SCHEME_COMPAT_3_4 1 +#define CONFIG_EFUSE_MAX_BLK_LEN 192 +#define CONFIG_ESP_TLS_USING_MBEDTLS 1 +#define CONFIG_ESP32_REV_MIN_0 1 +#define CONFIG_ESP32_REV_MIN 0 +#define CONFIG_ESP32_DPORT_WORKAROUND 1 +#define CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160 1 +#define CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ 160 +#define CONFIG_ESP32_TRACEMEM_RESERVE_DRAM 0x0 +#define CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_FOUR 1 +#define CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES 4 +#define CONFIG_ULP_COPROC_RESERVE_MEM 0 +#define CONFIG_ESP_DEBUG_OCDAWARE 1 +#define CONFIG_ESP_BROWNOUT_DET 1 +#define CONFIG_ESP_BROWNOUT_DET_LVL_SEL_0 1 +#define CONFIG_ESP_BROWNOUT_DET_LVL 0 +#define CONFIG_ESP32_REDUCE_PHY_TX_POWER 1 +#define CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT 1 +#define CONFIG_RTC_CLK_SRC_INT_RC 1 +#define CONFIG_RTC_CLK_CAL_CYCLES 1024 +#define CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY 2000 +#define CONFIG_ESP32_XTAL_FREQ_40 1 +#define CONFIG_ESP32_XTAL_FREQ 40 +#define CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL 5 +#define CONFIG_ADC_CAL_EFUSE_TP_ENABLE 1 +#define CONFIG_ADC_CAL_EFUSE_VREF_ENABLE 1 +#define CONFIG_ADC_CAL_LUT_ENABLE 1 +#define CONFIG_ESP_ERR_TO_NAME_LOOKUP 1 +#define CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE 32 +#define CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE 2304 +#define CONFIG_ESP_MAIN_TASK_STACK_SIZE 3584 +#define CONFIG_ESP_IPC_TASK_STACK_SIZE 1024 +#define CONFIG_ESP_IPC_USES_CALLERS_PRIORITY 1 +#define CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE 2048 +#define CONFIG_ESP_CONSOLE_UART_DEFAULT 1 +#define CONFIG_ESP_CONSOLE_UART_NUM 0 +#define CONFIG_ESP_CONSOLE_UART_TX_GPIO 1 +#define CONFIG_ESP_CONSOLE_UART_RX_GPIO 3 +#define CONFIG_ESP_CONSOLE_UART_BAUDRATE 115200 +#define CONFIG_ESP_INT_WDT 1 +#define CONFIG_ESP_INT_WDT_TIMEOUT_MS 300 +#define CONFIG_ESP_INT_WDT_CHECK_CPU1 1 +#define CONFIG_ESP_TASK_WDT 1 +#define CONFIG_ESP_TASK_WDT_TIMEOUT_S 5 +#define CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 1 +#define CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 1 +#define CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_STA 1 +#define CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_AP 1 +#define CONFIG_ESP_MAC_ADDR_UNIVERSE_BT 1 +#define CONFIG_ESP_MAC_ADDR_UNIVERSE_BT_OFFSET 2 +#define CONFIG_ESP_MAC_ADDR_UNIVERSE_ETH 1 +#define CONFIG_ETH_ENABLED 1 +#define CONFIG_ETH_USE_ESP32_EMAC 1 +#define CONFIG_ETH_PHY_INTERFACE_RMII 1 +#define CONFIG_ETH_RMII_CLK_INPUT 1 +#define CONFIG_ETH_RMII_CLK_IN_GPIO 0 +#define CONFIG_ETH_DMA_BUFFER_SIZE 512 +#define CONFIG_ETH_DMA_RX_BUFFER_NUM 10 +#define CONFIG_ETH_DMA_TX_BUFFER_NUM 10 +#define CONFIG_ETH_USE_SPI_ETHERNET 1 +#define CONFIG_ESP_EVENT_POST_FROM_ISR 1 +#define CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR 1 +#define CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS 1 +#define CONFIG_HTTPD_MAX_REQ_HDR_LEN 512 +#define CONFIG_HTTPD_MAX_URI_LEN 512 +#define CONFIG_HTTPD_ERR_RESP_NO_DELAY 1 +#define CONFIG_HTTPD_PURGE_BUF_LEN 32 +#define CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL 120 +#define CONFIG_ESP_NETIF_TCPIP_LWIP 1 +#define CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT 1 +#define CONFIG_ESP_TIMER_TASK_STACK_SIZE 3584 +#define CONFIG_ESP_TIMER_IMPL_TG0_LAC 1 +#define CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM 10 +#define CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM 32 +#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER 1 +#define CONFIG_ESP32_WIFI_TX_BUFFER_TYPE 1 +#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM 32 +#define CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED 1 +#define CONFIG_ESP32_WIFI_TX_BA_WIN 6 +#define CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED 1 +#define CONFIG_ESP32_WIFI_RX_BA_WIN 6 +#define CONFIG_ESP32_WIFI_NVS_ENABLED 1 +#define CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0 1 +#define CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN 752 +#define CONFIG_ESP32_WIFI_MGMT_SBUF_NUM 32 +#define CONFIG_ESP32_WIFI_IRAM_OPT 1 +#define CONFIG_ESP32_WIFI_RX_IRAM_OPT 1 +#define CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE 1 +#define CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE 1 +#define CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER 20 +#define CONFIG_ESP32_PHY_MAX_TX_POWER 20 +#define CONFIG_ESP_COREDUMP_ENABLE_TO_NONE 1 +#define CONFIG_FATFS_CODEPAGE_437 1 +#define CONFIG_FATFS_CODEPAGE 437 +#define CONFIG_FATFS_LFN_NONE 1 +#define CONFIG_FATFS_FS_LOCK 0 +#define CONFIG_FATFS_TIMEOUT_MS 10000 +#define CONFIG_FATFS_PER_FILE_CACHE 1 +#define CONFIG_FMB_COMM_MODE_RTU_EN 1 +#define CONFIG_FMB_COMM_MODE_ASCII_EN 1 +#define CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND 150 +#define CONFIG_FMB_MASTER_DELAY_MS_CONVERT 200 +#define CONFIG_FMB_QUEUE_LENGTH 20 +#define CONFIG_FMB_SERIAL_TASK_STACK_SIZE 2048 +#define CONFIG_FMB_SERIAL_BUF_SIZE 256 +#define CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB 8 +#define CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS 1000 +#define CONFIG_FMB_SERIAL_TASK_PRIO 10 +#define CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT 20 +#define CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE 20 +#define CONFIG_FMB_CONTROLLER_STACK_SIZE 4096 +#define CONFIG_FMB_EVENT_QUEUE_TIMEOUT 20 +#define CONFIG_FMB_TIMER_PORT_ENABLED 1 +#define CONFIG_FMB_TIMER_GROUP 0 +#define CONFIG_FMB_TIMER_INDEX 0 +#define CONFIG_FREERTOS_NO_AFFINITY 0x7FFFFFFF +#define CONFIG_FREERTOS_CORETIMER_0 1 +#define CONFIG_FREERTOS_HZ 100 +#define CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION 1 +#define CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY 1 +#define CONFIG_FREERTOS_INTERRUPT_BACKTRACE 1 +#define CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS 1 +#define CONFIG_FREERTOS_IDLE_TASK_STACKSIZE 1536 +#define CONFIG_FREERTOS_ISR_STACKSIZE 1536 +#define CONFIG_FREERTOS_MAX_TASK_NAME_LEN 16 +#define CONFIG_FREERTOS_TIMER_TASK_PRIORITY 1 +#define CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH 2048 +#define CONFIG_FREERTOS_TIMER_QUEUE_LENGTH 10 +#define CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE 0 +#define CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER 1 +#define CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER 1 +#define CONFIG_FREERTOS_DEBUG_OCDAWARE 1 +#define CONFIG_HEAP_POISONING_DISABLED 1 +#define CONFIG_HEAP_TRACING_OFF 1 +#define CONFIG_LOG_DEFAULT_LEVEL_INFO 1 +#define CONFIG_LOG_DEFAULT_LEVEL 3 +#define CONFIG_LOG_MAXIMUM_LEVEL 3 +#define CONFIG_LOG_COLORS 1 +#define CONFIG_LOG_TIMESTAMP_SOURCE_RTOS 1 +#define CONFIG_LWIP_LOCAL_HOSTNAME "espressif" +#define CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES 1 +#define CONFIG_LWIP_TIMERS_ONDEMAND 1 +#define CONFIG_LWIP_MAX_SOCKETS 10 +#define CONFIG_LWIP_SO_REUSE 1 +#define CONFIG_LWIP_SO_REUSE_RXTOALL 1 +#define CONFIG_LWIP_IP_FRAG 1 +#define CONFIG_LWIP_ESP_GRATUITOUS_ARP 1 +#define CONFIG_LWIP_GARP_TMR_INTERVAL 60 +#define CONFIG_LWIP_TCPIP_RECVMBOX_SIZE 32 +#define CONFIG_LWIP_DHCP_DOES_ARP_CHECK 1 +#define CONFIG_LWIP_DHCPS_LEASE_UNIT 60 +#define CONFIG_LWIP_DHCPS_MAX_STATION_NUM 8 +#define CONFIG_LWIP_NETIF_LOOPBACK 1 +#define CONFIG_LWIP_LOOPBACK_MAX_PBUFS 8 +#define CONFIG_LWIP_MAX_ACTIVE_TCP 16 +#define CONFIG_LWIP_MAX_LISTENING_TCP 16 +#define CONFIG_LWIP_TCP_MAXRTX 12 +#define CONFIG_LWIP_TCP_SYNMAXRTX 6 +#define CONFIG_LWIP_TCP_MSS 1440 +#define CONFIG_LWIP_TCP_TMR_INTERVAL 250 +#define CONFIG_LWIP_TCP_MSL 60000 +#define CONFIG_LWIP_TCP_SND_BUF_DEFAULT 5744 +#define CONFIG_LWIP_TCP_WND_DEFAULT 5744 +#define CONFIG_LWIP_TCP_RECVMBOX_SIZE 6 +#define CONFIG_LWIP_TCP_QUEUE_OOSEQ 1 +#define CONFIG_LWIP_TCP_OVERSIZE_MSS 1 +#define CONFIG_LWIP_MAX_UDP_PCBS 16 +#define CONFIG_LWIP_UDP_RECVMBOX_SIZE 6 +#define CONFIG_LWIP_TCPIP_TASK_STACK_SIZE 3072 +#define CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY 1 +#define CONFIG_LWIP_TCPIP_TASK_AFFINITY 0x7FFFFFFF +#define CONFIG_LWIP_MAX_RAW_PCBS 16 +#define CONFIG_LWIP_DHCP_MAX_NTP_SERVERS 1 +#define CONFIG_LWIP_SNTP_UPDATE_DELAY 3600000 +#define CONFIG_LWIP_ESP_LWIP_ASSERT 1 +#define CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC 1 +#define CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN 1 +#define CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN 16384 +#define CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN 4096 +#define CONFIG_MBEDTLS_CERTIFICATE_BUNDLE 1 +#define CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL 1 +#define CONFIG_MBEDTLS_HARDWARE_AES 1 +#define CONFIG_MBEDTLS_HARDWARE_MPI 1 +#define CONFIG_MBEDTLS_HARDWARE_SHA 1 +#define CONFIG_MBEDTLS_HAVE_TIME 1 +#define CONFIG_MBEDTLS_ECDSA_DETERMINISTIC 1 +#define CONFIG_MBEDTLS_SHA512_C 1 +#define CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT 1 +#define CONFIG_MBEDTLS_TLS_SERVER 1 +#define CONFIG_MBEDTLS_TLS_CLIENT 1 +#define CONFIG_MBEDTLS_TLS_ENABLED 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_RSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA 1 +#define CONFIG_MBEDTLS_SSL_RENEGOTIATION 1 +#define CONFIG_MBEDTLS_SSL_PROTO_TLS1 1 +#define CONFIG_MBEDTLS_SSL_PROTO_TLS1_1 1 +#define CONFIG_MBEDTLS_SSL_PROTO_TLS1_2 1 +#define CONFIG_MBEDTLS_SSL_ALPN 1 +#define CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS 1 +#define CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS 1 +#define CONFIG_MBEDTLS_AES_C 1 +#define CONFIG_MBEDTLS_RC4_DISABLED 1 +#define CONFIG_MBEDTLS_CCM_C 1 +#define CONFIG_MBEDTLS_GCM_C 1 +#define CONFIG_MBEDTLS_PEM_PARSE_C 1 +#define CONFIG_MBEDTLS_PEM_WRITE_C 1 +#define CONFIG_MBEDTLS_X509_CRL_PARSE_C 1 +#define CONFIG_MBEDTLS_X509_CSR_PARSE_C 1 +#define CONFIG_MBEDTLS_ECP_C 1 +#define CONFIG_MBEDTLS_ECDH_C 1 +#define CONFIG_MBEDTLS_ECDSA_C 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_NIST_OPTIM 1 +#define CONFIG_MDNS_MAX_SERVICES 25 +#define CONFIG_MDNS_MAX_INTERFACES 3 +#define CONFIG_MDNS_TASK_PRIORITY 1 +#define CONFIG_MDNS_ACTION_QUEUE_LEN 16 +#define CONFIG_MDNS_TASK_STACK_SIZE 4096 +#define CONFIG_MDNS_TASK_AFFINITY_CPU0 1 +#define CONFIG_MDNS_TASK_AFFINITY 0x0 +#define CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS 1 +#define CONFIG_MDNS_TIMER_PERIOD_MS 100 +#define CONFIG_MQTT_PROTOCOL_311 1 +#define CONFIG_MQTT_TRANSPORT_SSL 1 +#define CONFIG_MQTT_TRANSPORT_WEBSOCKET 1 +#define CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE 1 +#define CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF 1 +#define CONFIG_NEWLIB_STDIN_LINE_ENDING_CR 1 +#define CONFIG_OPENSSL_ASSERT_EXIT 1 +#define CONFIG_PTHREAD_TASK_PRIO_DEFAULT 5 +#define CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT 3072 +#define CONFIG_PTHREAD_STACK_MIN 768 +#define CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY 1 +#define CONFIG_PTHREAD_TASK_CORE_DEFAULT -1 +#define CONFIG_PTHREAD_TASK_NAME_DEFAULT "pthread" +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 +#define CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS 1 +#define CONFIG_SPI_FLASH_YIELD_DURING_ERASE 1 +#define CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS 20 +#define CONFIG_SPI_FLASH_ERASE_YIELD_TICKS 1 +#define CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP 1 +#define CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP 1 +#define CONFIG_SPI_FLASH_SUPPORT_GD_CHIP 1 +#define CONFIG_SPIFFS_MAX_PARTITIONS 3 +#define CONFIG_SPIFFS_CACHE 1 +#define CONFIG_SPIFFS_CACHE_WR 1 +#define CONFIG_SPIFFS_PAGE_CHECK 1 +#define CONFIG_SPIFFS_GC_MAX_RUNS 10 +#define CONFIG_SPIFFS_PAGE_SIZE 256 +#define CONFIG_SPIFFS_OBJ_NAME_LEN 32 +#define CONFIG_SPIFFS_USE_MAGIC 1 +#define CONFIG_SPIFFS_USE_MAGIC_LENGTH 1 +#define CONFIG_SPIFFS_META_LENGTH 4 +#define CONFIG_SPIFFS_USE_MTIME 1 +#define CONFIG_USB_DESC_CUSTOM_VID 0x1234 +#define CONFIG_USB_DESC_CUSTOM_PID 0x5678 +#define CONFIG_UNITY_ENABLE_FLOAT 1 +#define CONFIG_UNITY_ENABLE_DOUBLE 1 +#define CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER 1 +#define CONFIG_VFS_SUPPORT_IO 1 +#define CONFIG_VFS_SUPPORT_DIR 1 +#define CONFIG_VFS_SUPPORT_SELECT 1 +#define CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT 1 +#define CONFIG_VFS_SUPPORT_TERMIOS 1 +#define CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS 1 +#define CONFIG_VFS_SEMIHOSTFS_HOST_PATH_MAX_LEN 128 +#define CONFIG_WL_SECTOR_SIZE_4096 1 +#define CONFIG_WL_SECTOR_SIZE 4096 +#define CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES 16 +#define CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT 30 +#define CONFIG_WPA_MBEDTLS_CRYPTO 1 + +/* List of deprecated options */ +#define CONFIG_ADC2_DISABLE_DAC CONFIG_ADC_DISABLE_DAC +#define CONFIG_BROWNOUT_DET CONFIG_ESP_BROWNOUT_DET +#define CONFIG_BROWNOUT_DET_LVL_SEL_0 CONFIG_ESP_BROWNOUT_DET_LVL_SEL_0 +#define CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG CONFIG_COMPILER_OPTIMIZATION_DEFAULT +#define CONFIG_CONSOLE_UART_BAUDRATE CONFIG_ESP_CONSOLE_UART_BAUDRATE +#define CONFIG_CONSOLE_UART_DEFAULT CONFIG_ESP_CONSOLE_UART_DEFAULT +#define CONFIG_CONSOLE_UART_RX_GPIO CONFIG_ESP_CONSOLE_UART_RX_GPIO +#define CONFIG_CONSOLE_UART_TX_GPIO CONFIG_ESP_CONSOLE_UART_TX_GPIO +#define CONFIG_ESP32S2_PANIC_PRINT_REBOOT CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT +#define CONFIG_ESP32_APPTRACE_DEST_NONE CONFIG_APPTRACE_DEST_NONE +#define CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY +#define CONFIG_ESP32_PANIC_PRINT_REBOOT CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT +#define CONFIG_ESP32_PTHREAD_STACK_MIN CONFIG_PTHREAD_STACK_MIN +#define CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT CONFIG_PTHREAD_TASK_NAME_DEFAULT +#define CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT CONFIG_PTHREAD_TASK_PRIO_DEFAULT +#define CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT +#define CONFIG_ESP_GRATUITOUS_ARP CONFIG_LWIP_ESP_GRATUITOUS_ARP +#define CONFIG_FLASHMODE_DIO CONFIG_ESPTOOLPY_FLASHMODE_DIO +#define CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_FOUR +#define CONFIG_GARP_TMR_INTERVAL CONFIG_LWIP_GARP_TMR_INTERVAL +#define CONFIG_INT_WDT CONFIG_ESP_INT_WDT +#define CONFIG_INT_WDT_CHECK_CPU1 CONFIG_ESP_INT_WDT_CHECK_CPU1 +#define CONFIG_INT_WDT_TIMEOUT_MS CONFIG_ESP_INT_WDT_TIMEOUT_MS +#define CONFIG_IPC_TASK_STACK_SIZE CONFIG_ESP_IPC_TASK_STACK_SIZE +#define CONFIG_LOG_BOOTLOADER_LEVEL_INFO CONFIG_BOOTLOADER_LOG_LEVEL_INFO +#define CONFIG_MAIN_TASK_STACK_SIZE CONFIG_ESP_MAIN_TASK_STACK_SIZE +#define CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE +#define CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT +#define CONFIG_MB_CONTROLLER_STACK_SIZE CONFIG_FMB_CONTROLLER_STACK_SIZE +#define CONFIG_MB_EVENT_QUEUE_TIMEOUT CONFIG_FMB_EVENT_QUEUE_TIMEOUT +#define CONFIG_MB_MASTER_DELAY_MS_CONVERT CONFIG_FMB_MASTER_DELAY_MS_CONVERT +#define CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND +#define CONFIG_MB_QUEUE_LENGTH CONFIG_FMB_QUEUE_LENGTH +#define CONFIG_MB_SERIAL_BUF_SIZE CONFIG_FMB_SERIAL_BUF_SIZE +#define CONFIG_MB_SERIAL_TASK_PRIO CONFIG_FMB_SERIAL_TASK_PRIO +#define CONFIG_MB_SERIAL_TASK_STACK_SIZE CONFIG_FMB_SERIAL_TASK_STACK_SIZE +#define CONFIG_MB_TIMER_GROUP CONFIG_FMB_TIMER_GROUP +#define CONFIG_MB_TIMER_INDEX CONFIG_FMB_TIMER_INDEX +#define CONFIG_MB_TIMER_PORT_ENABLED CONFIG_FMB_TIMER_PORT_ENABLED +#define CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE +#define CONFIG_OPTIMIZATION_LEVEL_DEBUG CONFIG_COMPILER_OPTIMIZATION_DEFAULT +#define CONFIG_POST_EVENTS_FROM_IRAM_ISR CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR +#define CONFIG_POST_EVENTS_FROM_ISR CONFIG_ESP_EVENT_POST_FROM_ISR +#define CONFIG_REDUCE_PHY_TX_POWER CONFIG_ESP32_REDUCE_PHY_TX_POWER +#define CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN CONFIG_VFS_SEMIHOSTFS_HOST_PATH_MAX_LEN +#define CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS +#define CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS +#define CONFIG_STACK_CHECK_NONE CONFIG_COMPILER_STACK_CHECK_MODE_NONE +#define CONFIG_SUPPORT_TERMIOS CONFIG_VFS_SUPPORT_TERMIOS +#define CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT +#define CONFIG_SYSTEM_EVENT_QUEUE_SIZE CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE +#define CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE +#define CONFIG_TASK_WDT CONFIG_ESP_TASK_WDT +#define CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 +#define CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 +#define CONFIG_TASK_WDT_TIMEOUT_S CONFIG_ESP_TASK_WDT_TIMEOUT_S +#define CONFIG_TCPIP_RECVMBOX_SIZE CONFIG_LWIP_TCPIP_RECVMBOX_SIZE +#define CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY +#define CONFIG_TCPIP_TASK_STACK_SIZE CONFIG_LWIP_TCPIP_TASK_STACK_SIZE +#define CONFIG_TCP_MAXRTX CONFIG_LWIP_TCP_MAXRTX +#define CONFIG_TCP_MSL CONFIG_LWIP_TCP_MSL +#define CONFIG_TCP_MSS CONFIG_LWIP_TCP_MSS +#define CONFIG_TCP_OVERSIZE_MSS CONFIG_LWIP_TCP_OVERSIZE_MSS +#define CONFIG_TCP_QUEUE_OOSEQ CONFIG_LWIP_TCP_QUEUE_OOSEQ +#define CONFIG_TCP_RECVMBOX_SIZE CONFIG_LWIP_TCP_RECVMBOX_SIZE +#define CONFIG_TCP_SND_BUF_DEFAULT CONFIG_LWIP_TCP_SND_BUF_DEFAULT +#define CONFIG_TCP_SYNMAXRTX CONFIG_LWIP_TCP_SYNMAXRTX +#define CONFIG_TCP_WND_DEFAULT CONFIG_LWIP_TCP_WND_DEFAULT +#define CONFIG_TIMER_QUEUE_LENGTH CONFIG_FREERTOS_TIMER_QUEUE_LENGTH +#define CONFIG_TIMER_TASK_PRIORITY CONFIG_FREERTOS_TIMER_TASK_PRIORITY +#define CONFIG_TIMER_TASK_STACK_DEPTH CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH +#define CONFIG_TIMER_TASK_STACK_SIZE CONFIG_ESP_TIMER_TASK_STACK_SIZE +#define CONFIG_TOOLPREFIX CONFIG_SDK_TOOLPREFIX +#define CONFIG_UDP_RECVMBOX_SIZE CONFIG_LWIP_UDP_RECVMBOX_SIZE diff --git a/code/components/mdns/tests/test_afl_fuzz_host/test.c b/code/components/mdns/tests/test_afl_fuzz_host/test.c new file mode 100644 index 00000000..5556c563 --- /dev/null +++ b/code/components/mdns/tests/test_afl_fuzz_host/test.c @@ -0,0 +1,276 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include "esp32_mock.h" +#include "mdns.h" +#include "mdns_private.h" + +// +// Global stuctures containing packet payload, search +mdns_rx_packet_t g_packet; +struct pbuf mypbuf; +mdns_search_once_t *search = NULL; + +// +// Dependency injected test functions +void mdns_test_execute_action(void *action); +mdns_srv_item_t *mdns_test_mdns_get_service_item(const char *service, const char *proto); +mdns_search_once_t *mdns_test_search_init(const char *name, const char *service, const char *proto, uint16_t type, uint32_t timeout, uint8_t max_results); +esp_err_t mdns_test_send_search_action(mdns_action_type_t type, mdns_search_once_t *search); +void mdns_test_search_free(mdns_search_once_t *search); +void mdns_test_init_di(void); +extern mdns_server_t *_mdns_server; + +// +// mdns function wrappers for mdns setup in test mode +static int mdns_test_hostname_set(const char *mdns_hostname) +{ + for (int i = 0; i < MDNS_MAX_INTERFACES; i++) { + _mdns_server->interfaces[i].pcbs[MDNS_IP_PROTOCOL_V4].state = PCB_RUNNING; // mark the PCB running to exercise mdns in fully operational mode + _mdns_server->interfaces[i].pcbs[MDNS_IP_PROTOCOL_V6].state = PCB_RUNNING; + } + int ret = mdns_hostname_set(mdns_hostname); + mdns_action_t *a = NULL; + GetLastItem(&a); + mdns_test_execute_action(a); + return ret; +} + +static int mdns_test_add_delegated_host(const char *mdns_hostname) +{ + mdns_ip_addr_t addr = { .addr = { .u_addr = ESP_IPADDR_TYPE_V4 } }; + addr.addr.u_addr.ip4.addr = 0x11111111; + int ret = mdns_delegate_hostname_add(mdns_hostname, &addr); + mdns_action_t *a = NULL; + GetLastItem(&a); + mdns_test_execute_action(a); + return ret; +} + + +static int mdns_test_service_instance_name_set(const char *service, const char *proto, const char *instance) +{ + int ret = mdns_service_instance_name_set(service, proto, instance); + mdns_action_t *a = NULL; + GetLastItem(&a); + mdns_test_execute_action(a); + return ret; +} + +static int mdns_test_service_txt_set(const char *service, const char *proto, uint8_t num_items, mdns_txt_item_t txt[]) +{ + int ret = mdns_service_txt_set(service, proto, txt, num_items); + mdns_action_t *a = NULL; + GetLastItem(&a); + mdns_test_execute_action(a); + return ret; +} + +static int mdns_test_sub_service_add(const char *sub_name, const char *service_name, const char *proto, uint32_t port) +{ + if (mdns_service_add(NULL, service_name, proto, port, NULL, 0)) { + // This is expected failure as the service thread is not running + } + mdns_action_t *a = NULL; + GetLastItem(&a); + mdns_test_execute_action(a); + + if (mdns_test_mdns_get_service_item(service_name, proto) == NULL) { + return ESP_FAIL; + } + int ret = mdns_service_subtype_add_for_host(NULL, service_name, proto, NULL, sub_name); + a = NULL; + GetLastItem(&a); + mdns_test_execute_action(a); + return ret; +} + +static int mdns_test_service_add(const char *service_name, const char *proto, uint32_t port) +{ + if (mdns_service_add(NULL, service_name, proto, port, NULL, 0)) { + // This is expected failure as the service thread is not running + } + mdns_action_t *a = NULL; + GetLastItem(&a); + mdns_test_execute_action(a); + + if (mdns_test_mdns_get_service_item(service_name, proto) == NULL) { + return ESP_FAIL; + } + return ESP_OK; +} + +static mdns_result_t *mdns_test_query(const char *name, const char *service, const char *proto, uint16_t type) +{ + search = mdns_test_search_init(name, service, proto, type, 3000, 20); + if (!search) { + abort(); + } + + if (mdns_test_send_search_action(ACTION_SEARCH_ADD, search)) { + mdns_test_search_free(search); + abort(); + } + + mdns_action_t *a = NULL; + GetLastItem(&a); + mdns_test_execute_action(a); + return NULL; +} + +static void mdns_test_query_free(void) +{ + mdns_test_search_free(search); +} + +// +// function "under test" where afl-mangled packets passed +// +void mdns_parse_packet(mdns_rx_packet_t *packet); + +// +// Test starts here +// +int main(int argc, char **argv) +{ + int i; + const char *mdns_hostname = "minifritz"; + const char *mdns_instance = "Hristo's Time Capsule"; + mdns_txt_item_t arduTxtData[4] = { + {"board", "esp32"}, + {"tcp_check", "no"}, + {"ssh_upload", "no"}, + {"auth_upload", "no"} + }; + + const uint8_t mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x32}; + + uint8_t buf[1460]; + char winstance[21 + strlen(mdns_hostname)]; + + sprintf(winstance, "%s [%02x:%02x:%02x:%02x:%02x:%02x]", mdns_hostname, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + // Init depencency injected methods + mdns_test_init_di(); + + if (mdns_init()) { + abort(); + } + + if (mdns_test_hostname_set(mdns_hostname)) { + abort(); + } + + if (mdns_test_add_delegated_host(mdns_hostname) || mdns_test_add_delegated_host("megafritz")) { + abort(); + } + +#ifndef MDNS_NO_SERVICES + + if (mdns_test_sub_service_add("_server", "_fritz", "_tcp", 22)) { + abort(); + } + + if (mdns_test_service_add("_telnet", "_tcp", 22)) { + abort(); + } + + if (mdns_test_service_add("_workstation", "_tcp", 9)) { + abort(); + } + if (mdns_test_service_instance_name_set("_workstation", "_tcp", winstance)) { + abort(); + } + + if (mdns_test_service_add("_arduino", "_tcp", 3232)) { + abort(); + } + + if (mdns_test_service_txt_set("_arduino", "_tcp", 4, arduTxtData)) { + abort(); + } + + if (mdns_test_service_add("_http", "_tcp", 80)) { + abort(); + } + + if (mdns_test_service_instance_name_set("_http", "_tcp", "ESP WebServer")) { + abort(); + } + + if ( + mdns_test_service_add("_afpovertcp", "_tcp", 548) + || mdns_test_service_add("_rfb", "_tcp", 885) + || mdns_test_service_add("_smb", "_tcp", 885) + || mdns_test_service_add("_adisk", "_tcp", 885) + || mdns_test_service_add("_airport", "_tcp", 885) + || mdns_test_service_add("_printer", "_tcp", 885) + || mdns_test_service_add("_airplay", "_tcp", 885) + || mdns_test_service_add("_raop", "_tcp", 885) + || mdns_test_service_add("_uscan", "_tcp", 885) + || mdns_test_service_add("_uscans", "_tcp", 885) + || mdns_test_service_add("_ippusb", "_tcp", 885) + || mdns_test_service_add("_scanner", "_tcp", 885) + || mdns_test_service_add("_ipp", "_tcp", 885) + || mdns_test_service_add("_ipps", "_tcp", 885) + || mdns_test_service_add("_pdl-datastream", "_tcp", 885) + || mdns_test_service_add("_ptp", "_tcp", 885) + || mdns_test_service_add("_sleep-proxy", "_udp", 885)) { + abort(); + } +#endif + mdns_result_t *results = NULL; + FILE *file; + size_t nread; + +#ifdef INSTR_IS_OFF + size_t len = 1460; + memset(buf, 0, 1460); + + if (argc != 2) { + printf("Non-instrumentation mode: please supply a file name created by AFL to reproduce crash\n"); + return 1; + } else { + // + // Note: parameter1 is a file (mangled packet) which caused the crash + file = fopen(argv[1], "r"); + assert(file >= 0 ); + len = fread(buf, 1, 1460, file); + fclose(file); + } + + for (i = 0; i < 1; i++) { +#else + while (__AFL_LOOP(1000)) { + memset(buf, 0, 1460); + size_t len = read(0, buf, 1460); +#endif + mypbuf.payload = malloc(len); + memcpy(mypbuf.payload, buf, len); + mypbuf.len = len; + g_packet.pb = &mypbuf; + mdns_test_query("minifritz", "_fritz", "_tcp", MDNS_TYPE_ANY); + mdns_test_query(NULL, "_fritz", "_tcp", MDNS_TYPE_PTR); + mdns_test_query(NULL, "_afpovertcp", "_tcp", MDNS_TYPE_PTR); + mdns_parse_packet(&g_packet); + free(mypbuf.payload); + } +#ifndef MDNS_NO_SERVICES + mdns_service_remove_all(); + mdns_action_t *a = NULL; + GetLastItem(&a); + mdns_test_execute_action(a); +#endif + ForceTaskDelete(); + mdns_free(); + return 0; +} diff --git a/code/components/mdns/tests/test_apps/CMakeLists.txt b/code/components/mdns/tests/test_apps/CMakeLists.txt new file mode 100644 index 00000000..62d1565f --- /dev/null +++ b/code/components/mdns/tests/test_apps/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following four 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) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(mdns_test_app) diff --git a/code/components/mdns/tests/test_apps/README.md b/code/components/mdns/tests/test_apps/README.md new file mode 100644 index 00000000..96b76dcb --- /dev/null +++ b/code/components/mdns/tests/test_apps/README.md @@ -0,0 +1,49 @@ +| Supported Targets | ESP32 | ESP32-S2 | ESP32-C3 | +| ----------------- | ----- | -------- | -------- | + +# mDNS test project + +Main purpose of this application is to test the mDNS library to correctly advertise, lookup services and hosts. +The "app_test.py" logically separated from two sets of test cases 1. "ESP32 board sends queries -> Host sends answers" and 2. "Host sends queries" -> "ESP32 board sends answers". +Two first sets of test cases are starting by 'test_query_' and the second ones by 'start_case'. + +## Runtime settings + +1. For first sets of test cases python creates and sends dns queries using "dpkt" library +2. For the second sets of test cases this app waits for user input to provide test case(e.g. CONFIG_TEST_QUERY_HOST, CONFIG_TEST_QUERY_HOST_ASYNC or CONFIG_TEST_QUERY_SERVICE) +In order to run both of them just needed to set up the project and run by 'python app_test.py' + +## Test app workflow + +- mDNS is initialized with hostname and instance name defined through the project configuration and `_http._tcp` service is added to be advertised +- A delegated host `esp32-delegated._local` is added and another `_http._tcp` service is added for this host. +- WiFi STA is started and tries to connect to the access point defined through the project configuration + +### Configure the project + +* Open the project configuration menu (`idf.py menuconfig`) + +* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. + +* Set `mDNS Hostname` as host name prefix for the device and its instance name in `mDNS Instance Name` + +### Build and Flash + +Build the project and flash it to the board, then run the monitor tool to view the serial output: + +``` +idf.py -p PORT flash monitor +``` + +- Wait for WiFi to connect to your access point +- You can now ping the device at `[board-hostname].local`, where `[board-hostname]` is preconfigured hostname, `esp32-mdns` by default. +- You can also browse for `_http._tcp` on the same network to find the advertised service +- Note that for purpose of CI tests, configuration options of `MDNS_RESOLVE_TEST_SERVICES` and `MDNS_ADD_MAC_TO_HOSTNAME` are available, but disabled by default. If enabled, then the hostname suffix of last 3 bytes from device MAC address is added, e.g. `esp32-mdns-80FFFF`, and a query for test service is issued. + + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Hardware Required +This test-app can be executed on any ESP32 board, the only required interface is WiFi and connection to a local network and tls server. diff --git a/code/components/mdns/tests/test_apps/main/CMakeLists.txt b/code/components/mdns/tests/test_apps/main/CMakeLists.txt new file mode 100644 index 00000000..b5902d58 --- /dev/null +++ b/code/components/mdns/tests/test_apps/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" "mdns_test.c" + INCLUDE_DIRS ".") diff --git a/code/components/mdns/tests/test_apps/main/Kconfig.projbuild b/code/components/mdns/tests/test_apps/main/Kconfig.projbuild new file mode 100644 index 00000000..a1c0d729 --- /dev/null +++ b/code/components/mdns/tests/test_apps/main/Kconfig.projbuild @@ -0,0 +1,40 @@ +menu "Example Configuration" + + config TEST_MDNS_HOSTNAME + string "mDNS Hostname" + default "esp32-mdns" + help + mDNS Hostname for example to use + + config TEST_MDNS_INSTANCE + string "mDNS Instance Name" + default "ESP32 with mDNS" + help + mDNS Instance Name for example to use + + config TEST_MDNS_PUBLISH_DELEGATE_HOST + bool "Publish a delegated host" + help + Enable publishing a delegated host other than ESP32. + The example will also add a mock service for this host. + + config TEST_MDNS_ADD_MAC_TO_HOSTNAME + bool "Add mac suffix to hostname" + default n + help + If enabled, a portion of MAC address is added to the hostname, this is used + for evaluation of tests in CI + config MDNS_ADD_MAC_TO_HOSTNAME + bool "Add mac suffix to hostname" + default n + help + If enabled, a portion of MAC address is added to the hostname, this is used + for evaluation of tests in CI + config MDNS_PUBLISH_DELEGATE_HOST + bool "Publish a delegated host" + help + Enable publishing a delegated host other than ESP32. + The example will also add a mock service for this host. + + +endmenu diff --git a/code/components/mdns/tests/test_apps/main/idf_component.yml b/code/components/mdns/tests/test_apps/main/idf_component.yml new file mode 100644 index 00000000..e9277dfc --- /dev/null +++ b/code/components/mdns/tests/test_apps/main/idf_component.yml @@ -0,0 +1,8 @@ +dependencies: + ## Required IDF version + idf: ">=5.0" + espressif/mdns: + version: "^1.0.0" + override_path: "../../../" + protocol_examples_common: + path: ${IDF_PATH}/examples/common_components/protocol_examples_common diff --git a/code/components/mdns/tests/test_apps/main/main.c b/code/components/mdns/tests/test_apps/main/main.c new file mode 100644 index 00000000..e67014ce --- /dev/null +++ b/code/components/mdns/tests/test_apps/main/main.c @@ -0,0 +1,117 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "esp_mac.h" +#include "nvs_flash.h" +#include "esp_event.h" +#include "esp_netif.h" +#include "esp_log.h" +#include "protocol_examples_common.h" +#include "mdns.h" + +static const char *TAG = "mdns_test"; +void mdns_test(char *line); + +static void get_string(char *line, size_t size) +{ + int count = 0; + while (count < size) { + int c = fgetc(stdin); + if (c == '\n') { + line[count] = '\0'; + break; + } else if (c > 0 && c < 127) { + line[count] = c; + ++count; + } + vTaskDelay(20 / portTICK_PERIOD_MS); + } +} + +/** Generate host name based on sdkconfig, optionally adding a portion of MAC address to it. + * @return host name string allocated from the heap + */ +static char *generate_hostname(void) +{ +#ifndef CONFIG_TEST_MDNS_ADD_MAC_TO_HOSTNAME + return strdup(CONFIG_TEST_MDNS_HOSTNAME); +#else + uint8_t mac[6]; + char *hostname; + esp_read_mac(mac, ESP_MAC_WIFI_STA); + if (-1 == asprintf(&hostname, "%s-%02X%02X%02X", CONFIG_TEST_MDNS_HOSTNAME, mac[3], mac[4], mac[5])) { + abort(); + } + return hostname; +#endif +} + +static void initialise_mdns(void) +{ + char *hostname = generate_hostname(); + + //initialize mDNS + ESP_ERROR_CHECK( mdns_init() ); + + //set mDNS hostname (required if you want to advertise services) + ESP_ERROR_CHECK( mdns_hostname_set(hostname) ); + + ESP_LOGI(TAG, "mdns hostname set to: [%s]", hostname); + //set default mDNS instance name + ESP_ERROR_CHECK( mdns_instance_name_set(CONFIG_TEST_MDNS_INSTANCE) ); + + //initialize service + ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, NULL, 0) ); + +#if CONFIG_TEST_MDNS_PUBLISH_DELEGATE_HOST + char *delegated_hostname; + if (-1 == asprintf(&delegated_hostname, "%s-delegated", hostname)) { + abort(); + } + + mdns_ip_addr_t addr4, addr6; + esp_netif_str_to_ip4("10.0.0.1", &addr4.addr.u_addr.ip4); + addr4.addr.type = ESP_IPADDR_TYPE_V4; + esp_netif_str_to_ip6("fd11:22::1", &addr6.addr.u_addr.ip6); + addr6.addr.type = ESP_IPADDR_TYPE_V6; + addr4.next = &addr6; + addr6.next = NULL; + ESP_ERROR_CHECK( mdns_delegate_hostname_add(delegated_hostname, &addr4) ); + ESP_ERROR_CHECK( mdns_service_add_for_host("test0", "_http", "_tcp", delegated_hostname, 1234, NULL, 0) ); + free(delegated_hostname); +#endif // CONFIG_TEST_MDNS_PUBLISH_DELEGATE_HOST + + ESP_ERROR_CHECK( mdns_service_subtype_add_for_host("ESP32-WebServer", "_http", "_tcp", NULL, "_server") ); + + free(hostname); +} + +void app_main(void) +{ + ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); + ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); + + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + initialise_mdns(); + + /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + */ + ESP_ERROR_CHECK(example_connect()); + + while (1) { + char line[256]; + + get_string(line, sizeof(line)); + mdns_test(line); + continue; + } +} diff --git a/code/components/mdns/tests/test_apps/main/mdns_test.c b/code/components/mdns/tests/test_apps/main/mdns_test.c new file mode 100644 index 00000000..06df424f --- /dev/null +++ b/code/components/mdns/tests/test_apps/main/mdns_test.c @@ -0,0 +1,189 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "mdns.h" +#include "esp_log.h" +#include "esp_netif.h" + +static const char *TAG = "mdns_test_app"; +static const int RETRY_COUNT = 10; + +static void mdns_print_results(mdns_result_t *results) +{ + mdns_result_t *r = results; + mdns_ip_addr_t *a = NULL; + int t; + while (r) { + if (r->instance_name) { + printf("PTR:%s.%s.%s\n", r->instance_name, r->service_type, r->proto); + } + if (r->hostname) { + printf("SRV:%s.local:%u\n", r->hostname, r->port); + } + if (r->txt_count) { + printf("TXT:[%zu] ", r->txt_count); + for (t = 0; t < r->txt_count; t++) { + printf("%s=%s(%d); ", r->txt[t].key, r->txt[t].value ? r->txt[t].value : "NULL", r->txt_value_len[t]); + } + printf("\n"); + } + a = r->addr; + while (a) { + if (a->addr.type == ESP_IPADDR_TYPE_V6) { + printf(" AAAA: " IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6)); + } else { + printf(" A : " IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4))); + } + a = a->next; + } + r = r->next; + } +} + +static bool check_and_print_result(mdns_search_once_t *search) +{ + // Check if any result is available + mdns_result_t *result = NULL; + if (!mdns_query_async_get_results(search, 0, &result, NULL)) { + return false; + } + + if (!result) { // search timeout, but no result + return false; + } + + // If yes, print the result + mdns_ip_addr_t *a = result->addr; + while (a) { + if (a->addr.type == ESP_IPADDR_TYPE_V6) { + printf("Async query resolved to AAAA:" IPV6STR "\n", IPV62STR(a->addr.u_addr.ip6)); + } else { + printf("Async query resolved to A:" IPSTR "\n", IP2STR(&(a->addr.u_addr.ip4))); + } + a = a->next; + } + // and free the result + mdns_query_results_free(result); + return true; +} + +static bool query_mdns_hosts_async(const char *host_name) +{ + ESP_LOGI(TAG, "Query both A and AAA: %s.local", host_name); + bool res = false; + + mdns_search_once_t *s_a = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_A, 1000, 1, NULL); + mdns_query_async_delete(s_a); + mdns_search_once_t *s_aaaa = mdns_query_async_new(host_name, NULL, NULL, MDNS_TYPE_AAAA, 1000, 1, NULL); + while (s_a || s_aaaa) { + if (s_a && check_and_print_result(s_a)) { + ESP_LOGI(TAG, "Query A %s.local finished", host_name); + mdns_query_async_delete(s_a); + s_a = NULL; + res = true; + } + if (s_aaaa && check_and_print_result(s_aaaa)) { + ESP_LOGI(TAG, "Query AAAA %s.local finished", host_name); + mdns_query_async_delete(s_aaaa); + s_aaaa = NULL; + res = true; + } + } + return res; +} + +static esp_err_t query_mdns_host(const char *host_name) +{ + ESP_LOGI(TAG, "Query A: %s.local", host_name); + + struct esp_ip4_addr addr; + addr.addr = 0; + + esp_err_t err = mdns_query_a(host_name, 2000, &addr); + if (err) { + if (err == ESP_ERR_NOT_FOUND) { + ESP_LOGW(TAG, "%s: Host was not found!", esp_err_to_name(err)); + } + ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err)); + return err; + } + + ESP_LOGI(TAG, "Query A: %s.local resolved to: " IPSTR, host_name, IP2STR(&addr)); + return ESP_OK; +} + +static esp_err_t query_mdns_service(const char *instance, const char *service_name, const char *proto) +{ + ESP_LOGI(TAG, "Query SRV: %s.%s.local", service_name, proto); + + mdns_result_t *results = NULL; + esp_err_t err = mdns_query_srv(instance, service_name, proto, 3000, &results); + if (err) { + ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err)); + return err; + } + if (!results) { + ESP_LOGW(TAG, "No results found!"); + } + + mdns_print_results(results); + mdns_query_results_free(results); + return ESP_OK; +} + +void query_mdns_service_sub_type(const char *subtype, const char *service_name, const char *proto) +{ + ESP_LOGI(TAG, "Query PTR: %s.%s.local", service_name, proto); + + mdns_result_t *results = NULL; + esp_err_t err = mdns_query_ptr(service_name, proto, 3000, 20, &results); + if (err) { + ESP_LOGE(TAG, "Query Failed: %s", esp_err_to_name(err)); + } + if (!results) { + ESP_LOGW(TAG, "No results found!"); + } + + mdns_print_results(results); + mdns_query_results_free(results); +} + +void mdns_test(const char *line) +{ + char test_case[32]; + int i = 0; + const TickType_t xDelay = 1000 / portTICK_PERIOD_MS; + + sscanf(line, "%s", test_case); + ESP_LOGI(TAG, "test case = %s", test_case); + + if (strcmp(test_case, "CONFIG_TEST_QUERY_HOST") == 0) { + i = 0; + while (query_mdns_host("tinytester") != ESP_OK && i != RETRY_COUNT) { + query_mdns_host("tinytester"); + i++; + vTaskDelay(xDelay); + } + } else if (strcmp(test_case, "CONFIG_TEST_QUERY_HOST_ASYNC") == 0) { + i = 0; + while (query_mdns_hosts_async("tinytester") == false && i != RETRY_COUNT) { + query_mdns_hosts_async("tinytester"); + i++; + vTaskDelay(xDelay); + } + } else if (strcmp(test_case, "CONFIG_TEST_QUERY_SERVICE") == 0) { + i = 0; + while (query_mdns_service("ESP32", "_http", "_tcp") != ESP_OK && i != RETRY_COUNT) { + query_mdns_service("ESP32", "_http", "_tcp"); + i++; + vTaskDelay(xDelay); + } + } else { + ESP_LOGE(TAG, "%s: No such test case", test_case); + } +} diff --git a/code/components/mdns/tests/test_apps/pytest_mdns_app.py b/code/components/mdns/tests/test_apps/pytest_mdns_app.py new file mode 100644 index 00000000..2b04cb75 --- /dev/null +++ b/code/components/mdns/tests/test_apps/pytest_mdns_app.py @@ -0,0 +1,320 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +import re +import select +import socket +import struct +import time +from threading import Event, Thread + +import dpkt +import dpkt.dns +import pytest + +UDP_PORT = 5353 +MCAST_GRP = '224.0.0.251' + +# This service is created from esp board startup +SERVICE_NAME = u'ESP32-WebServer._http._tcp.local' +SUB_SERVICE_NAME = u'_server._sub._http._tcp.local' + +# This host name answer sent by host, when there is query from board +HOST_NAME = u'tinytester.local' + +# This servce answer sent by host, when there is query from board +MDNS_HOST_SERVICE = u'ESP32._http._tcp.local' + +# Number of retries to receive mdns answear +RETRY_COUNT = 10 + +stop_mdns_listener = Event() +start_mdns_listener = Event() +esp_service_answered = Event() +esp_sub_service_answered = Event() +esp_host_answered = Event() +esp_delegated_host_answered = Event() + + +@pytest.mark.skip +# Get query of ESP32-WebServer._http._tcp.local service +def get_mdns_service_query(service): # type:(str) -> dpkt.dns.Msg + dns = dpkt.dns.DNS() + dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA + dns.rcode = dpkt.dns.DNS_RCODE_NOERR + arr = dpkt.dns.DNS.RR() + arr.cls = dpkt.dns.DNS_IN + arr.type = dpkt.dns.DNS_SRV + arr.name = service + arr.target = socket.inet_aton('127.0.0.1') + arr.srvname = service + dns.qd.append(arr) + print('Created mdns service query: {} '.format(dns.__repr__())) + return dns.pack() + + +@pytest.mark.skip +# Get query of _server_.sub._http._tcp.local sub service +def get_mdns_sub_service_query(sub_service): # type:(str) -> dpkt.dns.Msg + dns = dpkt.dns.DNS() + dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA + dns.rcode = dpkt.dns.DNS_RCODE_NOERR + arr = dpkt.dns.DNS.RR() + arr.cls = dpkt.dns.DNS_IN + arr.type = dpkt.dns.DNS_PTR + arr.name = sub_service + arr.target = socket.inet_aton('127.0.0.1') + arr.ptrname = sub_service + dns.qd.append(arr) + print('Created mdns subtype service query: {} '.format(dns.__repr__())) + return dns.pack() + + +@pytest.mark.skip +# Get query for host resolution +def get_dns_query_for_esp(esp_host): # type:(str) -> dpkt.dns.Msg + dns = dpkt.dns.DNS() + arr = dpkt.dns.DNS.RR() + arr.cls = dpkt.dns.DNS_IN + arr.name = esp_host + u'.local' + dns.qd.append(arr) + print('Created query for esp host: {} '.format(dns.__repr__())) + return dns.pack() + + +@pytest.mark.skip +# Get mdns answer for host resoloution +def get_dns_answer_to_mdns(tester_host): # type:(str) -> dpkt.dns.Msg + dns = dpkt.dns.DNS() + dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA + dns.rcode = dpkt.dns.DNS_RCODE_NOERR + arr = dpkt.dns.DNS.RR() + arr.cls = dpkt.dns.DNS_IN + arr.type = dpkt.dns.DNS_A + arr.name = tester_host + arr.ip = socket.inet_aton('127.0.0.1') + dns.an.append(arr) + print('Created answer to mdns query: {} '.format(dns.__repr__())) + return dns.pack() + + +@pytest.mark.skip +# Get mdns answer for service query +def get_dns_answer_to_service_query( + mdns_service): # type:(str) -> dpkt.dns.Msg + dns = dpkt.dns.DNS() + dns.op = dpkt.dns.DNS_QR | dpkt.dns.DNS_AA + dns.rcode = dpkt.dns.DNS_RCODE_NOERR + arr = dpkt.dns.DNS.RR() + arr.name = mdns_service + arr.cls = dpkt.dns.DNS_IN + arr.type = dpkt.dns.DNS_SRV + arr.priority = 0 + arr.weight = 0 + arr.port = 100 + arr.srvname = mdns_service + arr.ip = socket.inet_aton('127.0.0.1') + dns.an.append(arr) + print('Created answer to mdns query: {} '.format(dns.__repr__())) + return dns.pack() + + +@pytest.mark.skip +def mdns_listener(esp_host): # type:(str) -> None + print('mdns_listener thread started') + + UDP_IP = '0.0.0.0' + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + sock.setblocking(False) + sock.bind((UDP_IP, UDP_PORT)) + mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + last_query_timepoint = time.time() + QUERY_TIMEOUT = 0.2 + while not stop_mdns_listener.is_set(): + try: + start_mdns_listener.set() + current_time = time.time() + if current_time - last_query_timepoint > QUERY_TIMEOUT: + last_query_timepoint = current_time + timeout = max( + 0, QUERY_TIMEOUT - (current_time - last_query_timepoint)) + read_socks, _, _ = select.select([sock], [], [], timeout) + if not read_socks: + continue + data, _ = sock.recvfrom(1024) + dns = dpkt.dns.DNS(data) + # Receives queries from esp board and sends answers + if len(dns.qd) > 0: + if dns.qd[0].name == HOST_NAME: + print('Received query: {} '.format(dns.__repr__())) + sock.sendto(get_dns_answer_to_mdns(HOST_NAME), + (MCAST_GRP, UDP_PORT)) + if dns.qd[0].name == HOST_NAME: + print('Received query: {} '.format(dns.__repr__())) + sock.sendto(get_dns_answer_to_mdns(HOST_NAME), + (MCAST_GRP, UDP_PORT)) + if dns.qd[0].name == MDNS_HOST_SERVICE: + print('Received query: {} '.format(dns.__repr__())) + sock.sendto( + get_dns_answer_to_service_query(MDNS_HOST_SERVICE), + (MCAST_GRP, UDP_PORT)) + # Receives answers from esp board and sets event flags for python test cases + if len(dns.an) == 1: + if dns.an[0].name.startswith(SERVICE_NAME): + print('Received answer to service query: {}'.format( + dns.__repr__())) + esp_service_answered.set() + if len(dns.an) > 1: + if dns.an[1].name.startswith(SUB_SERVICE_NAME): + print('Received answer for sub service query: {}'.format( + dns.__repr__())) + esp_sub_service_answered.set() + if len(dns.an) > 0 and dns.an[0].type == dpkt.dns.DNS_A: + if dns.an[0].name == esp_host + u'.local': + print('Received answer to esp32-mdns query: {}'.format( + dns.__repr__())) + esp_host_answered.set() + if dns.an[0].name == esp_host + u'-delegated.local': + print('Received answer to esp32-mdns-delegate query: {}'. + format(dns.__repr__())) + esp_delegated_host_answered.set() + except socket.timeout: + break + except dpkt.UnpackError: + continue + + +@pytest.mark.skip +def create_socket(): # type:() -> socket.socket + UDP_IP = '0.0.0.0' + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + sock.setblocking(False) + sock.bind((UDP_IP, UDP_PORT)) + mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY) + sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + return sock + + +@pytest.mark.skip +def test_query_dns_http_service(service): # type: (str) -> None + print('SRV: Query {}'.format(service)) + sock = create_socket() + packet = get_mdns_service_query(service) + for _ in range(RETRY_COUNT): + if esp_service_answered.wait(timeout=25): + break + sock.sendto(packet, (MCAST_GRP, UDP_PORT)) + else: + raise RuntimeError( + 'Test has failed: did not receive mdns answer within timeout') + + +@pytest.mark.skip +def test_query_dns_sub_service(sub_service): # type: (str) -> None + print('PTR: Query {}'.format(sub_service)) + sock = create_socket() + packet = get_mdns_sub_service_query(sub_service) + for _ in range(RETRY_COUNT): + if esp_sub_service_answered.wait(timeout=25): + break + sock.sendto(packet, (MCAST_GRP, UDP_PORT)) + else: + raise RuntimeError( + 'Test has failed: did not receive mdns answer within timeout') + + +@pytest.mark.skip +def test_query_dns_host(esp_host): # type: (str) -> None + print('A: {}'.format(esp_host)) + sock = create_socket() + packet = get_dns_query_for_esp(esp_host) + for _ in range(RETRY_COUNT): + if esp_host_answered.wait(timeout=25): + break + sock.sendto(packet, (MCAST_GRP, UDP_PORT)) + else: + raise RuntimeError( + 'Test has failed: did not receive mdns answer within timeout') + + +@pytest.mark.skip +def test_query_dns_host_delegated(esp_host): # type: (str) -> None + print('A: {}'.format(esp_host)) + sock = create_socket() + packet = get_dns_query_for_esp(esp_host + '-delegated') + for _ in range(RETRY_COUNT): + if esp_delegated_host_answered.wait(timeout=25): + break + sock.sendto(packet, (MCAST_GRP, UDP_PORT)) + else: + raise RuntimeError( + 'Test has failed: did not receive mdns answer within timeout') + + +@pytest.mark.esp32 +@pytest.mark.esp32s2 +@pytest.mark.esp32c3 +@pytest.mark.generic +def test_app_esp_mdns(dut): + + # 1. get the dut host name (and IP address) + specific_host = dut.expect( + re.compile(b'mdns hostname set to: \[([^\]]+)\]'), # noqa: W605 + timeout=30).group(1).decode() + + esp_ip = dut.expect( + re.compile( + b' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), # noqa: W605 + timeout=30).group(1).decode() + print('Got IP={}'.format(esp_ip)) + + mdns_responder = Thread(target=mdns_listener, args=(str(specific_host), )) + + def start_case(case, desc, result): # type: (str, str, str) -> None + print('Starting {}: {}'.format(case, desc)) + dut.write(case) + res = bytes(result, encoding='utf8') + dut.expect(re.compile(res), timeout=30) + + try: + # start dns listener thread + mdns_responder.start() + + # wait untill mdns listener thred started + if not start_mdns_listener.wait(timeout=5): + raise ValueError( + 'Test has failed: mdns listener thread did not start') + + # query dns service from host, answer should be received from esp board + test_query_dns_http_service(SERVICE_NAME) + + # query dns sub-service from host, answer should be received from esp board + test_query_dns_sub_service(SUB_SERVICE_NAME) + + # query dns host name, answer should be received from esp board + test_query_dns_host(specific_host) + + # query dns host name delegated, answer should be received from esp board + test_query_dns_host_delegated(specific_host) + + # query service from esp board, answer should be received from host + start_case('CONFIG_TEST_QUERY_SERVICE', + 'Query SRV ESP32._http._tcp.local', 'SRV:ESP32') + + # query dns-host from esp board, answer should be received from host + start_case('CONFIG_TEST_QUERY_HOST', 'Query tinytester.local', + 'tinytester.local resolved to: 127.0.0.1') + + # query dns-host aynchrounusely from esp board, answer should be received from host + start_case('CONFIG_TEST_QUERY_HOST_ASYNC', + 'Query tinytester.local async', + 'Async query resolved to A:127.0.0.1') + + finally: + stop_mdns_listener.set() + mdns_responder.join() diff --git a/code/components/mdns/tests/test_apps/sdkconfig.ci b/code/components/mdns/tests/test_apps/sdkconfig.ci new file mode 100644 index 00000000..d89171dd --- /dev/null +++ b/code/components/mdns/tests/test_apps/sdkconfig.ci @@ -0,0 +1,13 @@ +CONFIG_IDF_TARGET="esp32" +CONFIG_MDNS_ADD_MAC_TO_HOSTNAME=y +CONFIG_MDNS_PUBLISH_DELEGATE_HOST=y +CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y +CONFIG_EXAMPLE_CONNECT_ETHERNET=y +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y +CONFIG_EXAMPLE_ETH_PHY_IP101=y +CONFIG_EXAMPLE_ETH_MDC_GPIO=23 +CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 +CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 +CONFIG_EXAMPLE_ETH_PHY_ADDR=1 +CONFIG_EXAMPLE_CONNECT_IPV6=y diff --git a/code/components/mdns/tests/test_apps/sdkconfig.defaults b/code/components/mdns/tests/test_apps/sdkconfig.defaults new file mode 100644 index 00000000..0c4ffe91 --- /dev/null +++ b/code/components/mdns/tests/test_apps/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_TEST_MDNS_ADD_MAC_TO_HOSTNAME=y +CONFIG_TEST_MDNS_PUBLISH_DELEGATE_HOST=y diff --git a/code/components/mdns/tests/unit_test/CMakeLists.txt b/code/components/mdns/tests/unit_test/CMakeLists.txt new file mode 100644 index 00000000..450d475e --- /dev/null +++ b/code/components/mdns/tests/unit_test/CMakeLists.txt @@ -0,0 +1,7 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.16) + +set(EXTRA_COMPONENT_DIRS ../.. "$ENV{IDF_PATH}/tools/unit-test-app/components") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(mdns_test) diff --git a/code/components/mdns/tests/unit_test/main/CMakeLists.txt b/code/components/mdns/tests/unit_test/main/CMakeLists.txt new file mode 100644 index 00000000..d04d217e --- /dev/null +++ b/code/components/mdns/tests/unit_test/main/CMakeLists.txt @@ -0,0 +1,5 @@ + +idf_component_register(SRCS "test_mdns.c" + REQUIRES test_utils + INCLUDE_DIRS "." + PRIV_REQUIRES unity mdns) diff --git a/code/components/mdns/tests/unit_test/main/test_mdns.c b/code/components/mdns/tests/unit_test/main/test_mdns.c new file mode 100644 index 00000000..ff024e2f --- /dev/null +++ b/code/components/mdns/tests/unit_test/main/test_mdns.c @@ -0,0 +1,299 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include "mdns.h" +#include "esp_event.h" +#include "unity.h" +#include "test_utils.h" + +#include "unity_fixture.h" +#include "memory_checks.h" + +#define MDNS_HOSTNAME "test-hostname" +#define MDNS_DELEGATE_HOSTNAME "delegate-hostname" +#define MDNS_INSTANCE "test-instance" +#define MDNS_SERVICE_NAME "_http" +#define MDNS_SERVICE_PROTO "_tcp" +#define MDNS_SERVICE_PORT 80 + +TEST_GROUP(mdns); + +TEST_SETUP(mdns) +{ + test_utils_record_free_mem(); + TEST_ESP_OK(test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL)); +} + +TEST_TEAR_DOWN(mdns) +{ + test_utils_finish_and_evaluate_leaks(32, 64); +} + +static void yield_to_all_priorities(void) +{ + // Lower the test-task priority before testing to ensure other tasks got executed on forced context switch + size_t test_task_prio_before = uxTaskPriorityGet(NULL); + vTaskPrioritySet(NULL, tskIDLE_PRIORITY); + taskYIELD(); // Let the RTOS to switch context + vTaskPrioritySet(NULL, test_task_prio_before); +} + + +TEST(mdns, api_fails_with_invalid_state) +{ + TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_init() ); + TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME) ); + TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_instance_name_set(MDNS_INSTANCE) ); + TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0) ); +} + +TEST(mdns, init_deinit) +{ + test_case_uses_tcpip(); + TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default()); + TEST_ASSERT_EQUAL(ESP_OK, mdns_init() ); + yield_to_all_priorities(); // Make sure that mdns task has executed to complete initialization + mdns_free(); + esp_event_loop_delete_default(); +} + +TEST(mdns, api_fails_with_expected_err) +{ + mdns_txt_item_t serviceTxtData[CONFIG_MDNS_MAX_SERVICES] = { {NULL, NULL}, + }; + mdns_ip_addr_t addr; + addr.addr.type = ESP_IPADDR_TYPE_V4; + addr.addr.u_addr.ip4.addr = esp_ip4addr_aton("127.0.0.1"); + addr.next = NULL; + for (int i = 0; i < CONFIG_MDNS_MAX_SERVICES; ++i) { + serviceTxtData[i].key = "Key"; + serviceTxtData[i].value = "Value"; + } + test_case_uses_tcpip(); + TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default()); + + TEST_ASSERT_EQUAL(ESP_OK, mdns_init() ); + TEST_ASSERT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME) ); + TEST_ASSERT_EQUAL(ESP_OK, mdns_delegate_hostname_add(MDNS_DELEGATE_HOSTNAME, &addr) ); + yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname + TEST_ASSERT_TRUE(mdns_hostname_exists(MDNS_DELEGATE_HOSTNAME) ); + TEST_ASSERT_EQUAL(ESP_OK, mdns_instance_name_set(MDNS_INSTANCE) ); + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, serviceTxtData, CONFIG_MDNS_MAX_SERVICES) ); + TEST_ASSERT_FALSE(mdns_service_exists(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME) ); + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add_for_host(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME, + MDNS_SERVICE_PORT, serviceTxtData, CONFIG_MDNS_MAX_SERVICES) ); + TEST_ASSERT_TRUE(mdns_service_exists(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME) ); + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, serviceTxtData, CONFIG_MDNS_MAX_SERVICES) ); + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_item_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, "key1", "value1") ); + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_item_remove(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, "key1") ); + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_port_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 8080) ); + yield_to_all_priorities(); // to remove the service with the updated txt records + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_remove(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO) ); + yield_to_all_priorities(); // Make sure that mdns task has executed to remove the service + + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0) ); + TEST_ASSERT_EQUAL(ESP_OK, mdns_delegate_hostname_remove(MDNS_DELEGATE_HOSTNAME) ); + yield_to_all_priorities(); // Make sure that mdns task has executed to remove the hostname + TEST_ASSERT_FALSE(mdns_service_exists(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME) ); + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_remove_all() ); + yield_to_all_priorities(); // Make sure that mdns task has executed to remove all services + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, mdns_service_port_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 8080) ); + + mdns_free(); + esp_event_loop_delete_default(); +} + +TEST(mdns, query_api_fails_with_expected_err) +{ + mdns_result_t *results = NULL; + esp_ip6_addr_t addr6; + esp_ip4_addr_t addr4; + test_case_uses_tcpip(); + TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default()); + + TEST_ASSERT_EQUAL(ESP_OK, mdns_init() ); + // check it is not possible to register a service or set an instance without configuring the hostname + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, mdns_instance_name_set(MDNS_INSTANCE)); + TEST_ASSERT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME)); + // hostname is set, now adding a service and instance should succeed + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0)); + TEST_ASSERT_EQUAL(ESP_OK, mdns_instance_name_set(MDNS_INSTANCE)); + + TEST_ASSERT_EQUAL(ESP_OK, mdns_query_ptr(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 10, CONFIG_MDNS_MAX_SERVICES, &results) ); + mdns_query_results_free(results); + + TEST_ASSERT_EQUAL(ESP_OK, mdns_query_srv(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 10, &results) ); + mdns_query_results_free(results); + + TEST_ASSERT_EQUAL(ESP_OK, mdns_query_txt(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 10, &results) ); + mdns_query_results_free(results); + + TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, mdns_query_a(MDNS_HOSTNAME, 10, &addr4) ); + mdns_query_results_free(results); + + TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, mdns_query_aaaa(MDNS_HOSTNAME, 10, &addr6) ); + mdns_query_results_free(results); + + mdns_free(); + esp_event_loop_delete_default(); +} + +TEST(mdns, add_remove_service) +{ + mdns_result_t *results = NULL; + test_case_uses_tcpip(); + TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default()); + TEST_ASSERT_EQUAL(ESP_OK, mdns_init() ); + TEST_ASSERT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME)); + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0)); + yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname + TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_selfhosted_service(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results)); + TEST_ASSERT_NOT_EQUAL(NULL, results); + TEST_ASSERT_EQUAL_STRING(MDNS_INSTANCE, results->instance_name); + TEST_ASSERT_EQUAL_STRING(MDNS_SERVICE_NAME, results->service_type); + TEST_ASSERT_EQUAL(MDNS_SERVICE_PORT, results->port); + TEST_ASSERT_EQUAL(NULL, results->txt); + mdns_query_results_free(results); + + // Update service properties: port + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_port_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT + 1)); + yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname + TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_selfhosted_service(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results)); + TEST_ASSERT_NOT_EQUAL(NULL, results); + TEST_ASSERT_EQUAL(MDNS_SERVICE_PORT + 1, results->port); + mdns_query_results_free(results); + + // Update service properties: instance + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_instance_name_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_INSTANCE "1" )); + yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname + TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_selfhosted_service(MDNS_INSTANCE "1", MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results)); + TEST_ASSERT_NOT_EQUAL(NULL, results); + TEST_ASSERT_EQUAL_STRING(MDNS_INSTANCE "1", results->instance_name); + mdns_query_results_free(results); + + // Update service properties: txt + mdns_txt_item_t txt_data[] = { + {"key1", "esp32"}, + {"key2", "value"}, + {"key3", "value3"}, + }; + const size_t txt_data_cout = sizeof(txt_data) / sizeof(txt_data[0]); + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, txt_data, txt_data_cout)); + yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname + TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_selfhosted_service(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results)); + TEST_ASSERT_NOT_EQUAL(NULL, results); + TEST_ASSERT_NOT_EQUAL(NULL, results->txt); + TEST_ASSERT_EQUAL(txt_data_cout, results->txt_count); + // compare txt values by keys + size_t matches = 0; + for (int i = 0; i < results->txt_count; ++i) // iterates over the results we get from mdns_lookup() + for (int j = 0; j < txt_data_cout; ++j) // iterates over our test records + if (strcmp(results->txt[i].key, txt_data[j].key) == 0) { // we compare the value only if the key matches + TEST_ASSERT_EQUAL_STRING(results->txt[i].value, txt_data[j].value); + ++matches; + } + TEST_ASSERT_EQUAL(txt_data_cout, matches); // checks that we went over all our txt items + mdns_query_results_free(results); + + // Now remove the service + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_remove(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO)); + yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname + TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_selfhosted_service(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results)); + TEST_ASSERT_EQUAL(NULL, results); + + mdns_free(); + esp_event_loop_delete_default(); +} + +TEST(mdns, add_remove_deleg_service) +{ + mdns_ip_addr_t addr; + addr.addr.type = ESP_IPADDR_TYPE_V4; + addr.addr.u_addr.ip4.addr = esp_ip4addr_aton("127.0.0.1"); + addr.next = NULL; + mdns_result_t *results = NULL; + test_case_uses_tcpip(); + TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default()); + TEST_ASSERT_EQUAL(ESP_OK, mdns_init() ); + TEST_ASSERT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME)); + TEST_ASSERT_EQUAL(ESP_OK, mdns_delegate_hostname_add(MDNS_DELEGATE_HOSTNAME, &addr) ); + + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add_for_host(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME, MDNS_SERVICE_PORT, NULL, 0)); + yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname + TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_delegated_service(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results)); + TEST_ASSERT_NOT_EQUAL(NULL, results); + TEST_ASSERT_EQUAL_STRING(MDNS_INSTANCE, results->instance_name); + TEST_ASSERT_EQUAL_STRING(MDNS_SERVICE_NAME, results->service_type); + TEST_ASSERT_EQUAL(MDNS_SERVICE_PORT, results->port); + TEST_ASSERT_EQUAL(NULL, results->txt); + mdns_query_results_free(results); + + // Update service properties: port + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_port_set_for_host(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME, MDNS_SERVICE_PORT + 1)); + yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname + TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_delegated_service(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results)); + TEST_ASSERT_NOT_EQUAL(NULL, results); + TEST_ASSERT_EQUAL(MDNS_SERVICE_PORT + 1, results->port); + mdns_query_results_free(results); + + // Update service properties: instance + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_instance_name_set_for_host(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME, MDNS_INSTANCE "1" )); + yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname + TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_delegated_service(MDNS_INSTANCE "1", MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results)); + TEST_ASSERT_NOT_EQUAL(NULL, results); + TEST_ASSERT_EQUAL_STRING(MDNS_INSTANCE "1", results->instance_name); + mdns_query_results_free(results); + + // Update service properties: txt + mdns_txt_item_t txt_data[] = { + {"key1", "esp32"}, + {"key2", "value"}, + }; + const size_t txt_data_cout = sizeof(txt_data) / sizeof(txt_data[0]); + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_set_for_host(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME, txt_data, txt_data_cout)); + yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname + TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_delegated_service(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results)); + TEST_ASSERT_NOT_EQUAL(NULL, results); + TEST_ASSERT_NOT_EQUAL(NULL, results->txt); + TEST_ASSERT_EQUAL(txt_data_cout, results->txt_count); + // compare txt values by keys + size_t matches = 0; + for (int i = 0; i < results->txt_count; ++i) // iterates over the results we get from mdns_lookup() + for (int j = 0; j < txt_data_cout; ++j) // iterates over our test records + if (strcmp(results->txt[i].key, txt_data[j].key) == 0) { // we compare the value only if the key matches + TEST_ASSERT_EQUAL_STRING(results->txt[i].value, txt_data[j].value); + ++matches; + } + TEST_ASSERT_EQUAL(txt_data_cout, matches); // checks that we went over all our txt items + mdns_query_results_free(results); + + // Now remove the service + TEST_ASSERT_EQUAL(ESP_OK, mdns_service_remove_for_host(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME)); + yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname + TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_delegated_service(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results)); + TEST_ASSERT_EQUAL(NULL, results); + + mdns_free(); + esp_event_loop_delete_default(); +} +TEST_GROUP_RUNNER(mdns) +{ + RUN_TEST_CASE(mdns, api_fails_with_invalid_state) + RUN_TEST_CASE(mdns, api_fails_with_expected_err) + RUN_TEST_CASE(mdns, query_api_fails_with_expected_err) + RUN_TEST_CASE(mdns, init_deinit) + RUN_TEST_CASE(mdns, add_remove_service) + RUN_TEST_CASE(mdns, add_remove_deleg_service) + +} + +void app_main(void) +{ + UNITY_MAIN(mdns); +} diff --git a/code/components/mdns/tests/unit_test/pytest_app_mdns.py b/code/components/mdns/tests/unit_test/pytest_app_mdns.py new file mode 100644 index 00000000..51061127 --- /dev/null +++ b/code/components/mdns/tests/unit_test/pytest_app_mdns.py @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +from pytest_embedded import Dut + + +def test_mdns(dut: Dut) -> None: + dut.expect_unity_test_output() diff --git a/code/components/mdns/tests/unit_test/sdkconfig.ci b/code/components/mdns/tests/unit_test/sdkconfig.ci new file mode 100644 index 00000000..96607191 --- /dev/null +++ b/code/components/mdns/tests/unit_test/sdkconfig.ci @@ -0,0 +1,3 @@ +CONFIG_IDF_TARGET="esp32" +CONFIG_UNITY_ENABLE_FIXTURE=y +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n diff --git a/code/components/mdns/tests/unit_test/sdkconfig.defaults b/code/components/mdns/tests/unit_test/sdkconfig.defaults new file mode 100644 index 00000000..168e08d4 --- /dev/null +++ b/code/components/mdns/tests/unit_test/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_UNITY_ENABLE_FIXTURE=y +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n diff --git a/code/components/openmetrics/CMakeLists.txt b/code/components/openmetrics/CMakeLists.txt index e2543939..ab93f745 100644 --- a/code/components/openmetrics/CMakeLists.txt +++ b/code/components/openmetrics/CMakeLists.txt @@ -1,7 +1,7 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." - REQUIRES jomjol_image_proc) + INCLUDE_DIRS "." "../../include" + REQUIRES jomjol_image_proc jomjol_helper) diff --git a/code/components/openmetrics/openmetrics.cpp b/code/components/openmetrics/openmetrics.cpp index fd893e7d..bf315261 100644 --- a/code/components/openmetrics/openmetrics.cpp +++ b/code/components/openmetrics/openmetrics.cpp @@ -12,34 +12,39 @@ std::string createMetric(const std::string &metricName, const std::string &help, metricName + " " + value + "\n"; } -typedef struct sequence_metric { +typedef struct sequence_metric +{ const char *name; const char *help; const char *type; std::function valueFunc; } sequence_metric_t; - sequence_metric_t sequenceMetrics[4] = { - { "flow_value", "current value of meter readout", "gauge", [](NumberPost *number)-> std::string {return number->ReturnValue;} }, - { "flow_raw_value", "current raw value of meter readout", "gauge", [](NumberPost *number)-> std::string {return number->ReturnRawValue;} }, - { "flow_pre_value", "previous value of meter readout", "gauge", [](NumberPost *number)-> std::string {return number->ReturnPreValue;} }, - { "flow_error", "Error message text != 'no error'", "gauge", [](NumberPost *number)-> std::string {return number->ErrorMessageText.compare("no error") == 0 ? "0" : "1";} }, + {"flow_value", "current value of meter readout", "gauge", [](NumberPost *number) -> std::string + { return number->ReturnValue; }}, + {"flow_raw_value", "current raw value of meter readout", "gauge", [](NumberPost *number) -> std::string + { return number->ReturnRawValue; }}, + {"flow_pre_value", "previous value of meter readout", "gauge", [](NumberPost *number) -> std::string + { return number->ReturnPreValue; }}, + {"flow_error", "Error message text != 'no error'", "gauge", [](NumberPost *number) -> std::string + { return number->ErrorMessageText.compare("no error") == 0 ? "0" : "1"; }}, }; std::string createSequenceMetrics(std::string prefix, const std::vector &numbers) { std::string result; - for (int i = 0; iname.c_str(), value.c_str()); + ESP_LOGD("METRICS", "metric=%s, name=%s, value = %s ", sequenceMetrics[i].name, number->name.c_str(), value.c_str()); // only valid data is reported (https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#missing-data) if (value.length() > 0) @@ -47,9 +52,9 @@ std::string createSequenceMetrics(std::string prefix, const std::vectorname; // except newline, double quote, and backslash (https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#abnf) // to keep it simple, these characters are just removed from the label - replaceAll(label, "\\", ""); - replaceAll(label, "\"", ""); - replaceAll(label, "\n", ""); + replace_all(label, "\\", ""); + replace_all(label, "\"", ""); + replace_all(label, "\n", ""); res += prefix + "_" + sequenceMetrics[i].name + "{sequence=\"" + label + "\"} " + value + "\n"; } @@ -57,9 +62,7 @@ std::string createSequenceMetrics(std::string prefix, const std::vector 0) { - res = "# HELP " + prefix + "_" + sequenceMetrics[i].name + " " + sequenceMetrics[i].help + "\n" - + "# TYPE " + prefix + "_" + sequenceMetrics[i].name + " " + sequenceMetrics[i].type + "\n" - + res; + res = "# HELP " + prefix + "_" + sequenceMetrics[i].name + " " + sequenceMetrics[i].help + "\n" + "# TYPE " + prefix + "_" + sequenceMetrics[i].name + " " + sequenceMetrics[i].type + "\n" + res; } result += res; } diff --git a/code/dependencies.lock b/code/dependencies.lock index 6c272e31..041ba108 100644 --- a/code/dependencies.lock +++ b/code/dependencies.lock @@ -1,15 +1,21 @@ dependencies: espressif/esp-nn: - component_hash: f6f2851ce82137a66e4265071c9b852bbe0130b882a18dea9f03faea7bf1295a + component_hash: f4633a02f05fef53b80de65ade1f733ebdef637e4c8b8be4eb11f5e27f6d9f3e source: service_url: https://api.components.espressif.com/ type: service - version: 1.1.0 + version: 1.1.2 + espressif/esp_jpeg: + component_hash: defb83669293cbf86d0fa86b475ba5517aceed04ed70db435388c151ab37b5d7 + source: + service_url: https://api.components.espressif.com/ + type: service + version: 1.3.1 idf: component_hash: null source: type: idf - version: 5.3.1 -manifest_hash: f88c9e5c2d75a9d5d6968fc67a90ef0cd7146dd6a3905a79c4dfcfc3b4fe6731 -target: esp32 + version: 5.4.1 +manifest_hash: b9fce080e2dac87f5ab93f569ed7da36d7d334e2f640d0cb4080b850ddce99a6 +target: esp32s3 version: 1.0.0 diff --git a/code/include/defines.h b/code/include/defines.h index e4117895..50146adc 100644 --- a/code/include/defines.h +++ b/code/include/defines.h @@ -1,4 +1,5 @@ #pragma once + #ifndef defines_h #define defines_h @@ -10,20 +11,7 @@ //can be set in platformio with -D OPTION_TO_ACTIVATE - //#define DEBUG_DETAIL_ON //#define DEBUG_DISABLE_BROWNOUT_DETECTOR - //#define DEBUG_ENABLE_SYSINFO - //#define DEBUG_ENABLE_PERFMON - //#define DEBUG_HIMEM_MEMORY_CHECK - // need [env:esp32cam-dev-himem] - //=> CONFIG_SPIRAM_BANKSWITCH_ENABLE=y - //=> CONFIG_SPIRAM_BANKSWITCH_RESERVE=4 - - - // use himem //https://github.com/jomjol/AI-on-the-edge-device/issues/1842 - #if (CONFIG_SPIRAM_BANKSWITCH_ENABLE) - #define USE_HIMEM_IF_AVAILABLE 1 - #endif /* Uncomment this to generate task list with stack sizes using the /heap handler PLEASE BE AWARE: The following CONFIG parameters have to to be set in @@ -66,6 +54,9 @@ //server_GPIO #define __LEDGLOBAL + + #define MOUNT_POINT "/sdcard" + //server_GPIO + server_file + SoftAP #define CONFIG_FILE "/sdcard/config/config.ini" @@ -74,10 +65,11 @@ //interface_mqtt + read_wlanini #define __HIDE_PASSWORD + #define STRING_ENCRYPTED_LABEL "**##**" - + //ClassFlowControll + Main + SoftAP - #define WLAN_CONFIG_FILE "/sdcard/wlan.ini" + #define NETWORK_CONFIG_FILE "/sdcard/wlan.ini" //main @@ -184,8 +176,6 @@ #define Digit_Transition_Area_Predecessor 0.7 // 9.3 - 0.7 #define Digit_Transition_Area_Forward 9.7 // Pre-run zero crossing only happens from approx. 9.7 onwards - //#define DEBUG_DETAIL_ON - ///////////////////////////////////////////// //// PSRAM Allocations //// @@ -198,141 +188,406 @@ ///////////////////////////////////////////// -// ******* Board type -#if defined(BOARD_WROVER_KIT) // WROVER-KIT PIN Map - // SD card (operated with SDMMC peripheral) - //------------------------------------------------- - #define GPIO_SDCARD_CLK GPIO_NUM_14 - #define GPIO_SDCARD_CMD GPIO_NUM_15 - #define GPIO_SDCARD_D0 GPIO_NUM_2 - #ifndef __SD_USE_ONE_LINE_MODE__ - #define GPIO_SDCARD_D1 GPIO_NUM_4 - #define GPIO_SDCARD_D2 GPIO_NUM_12 - #define GPIO_SDCARD_D3 GPIO_NUM_13 - #else - #define GPIO_SDCARD_D1 GPIO_NUM_NC - #define GPIO_SDCARD_D2 GPIO_NUM_NC - #define GPIO_SDCARD_D3 GPIO_NUM_13 - #endif +// ******* Board type ESP32CAM_AITHINKER +#if defined(BOARD_ESP32CAM_AITHINKER) // ESP32Cam (AiThinker) PIN Map +#define BoardType "ESP32CAM" - #define CAM_PIN_PWDN GPIO_NUM_NC //power down is not used - #define CAM_PIN_RESET GPIO_NUM_NC //software reset will be performed - #define CAM_PIN_XCLK GPIO_NUM_21 - #define CAM_PIN_SIOD GPIO_NUM_26 - #define CAM_PIN_SIOC GPIO_NUM_27 +// Uart +//------------------------------------------------- +#define UART_PORT_NUM UART_NUM_0 +#define UART_TX_GPIO_NUM GPIO_NUM_1 +#define UART_RX_GPIO_NUM GPIO_NUM_3 +#define UART_RTS_GPIO_NUM GPIO_NUM_NC +#define UART_CTS_GPIO_NUM GPIO_NUM_NC +#define UART_BUFFER_SIZE 2048 - #define CAM_PIN_D7 GPIO_NUM_35 - #define CAM_PIN_D6 GPIO_NUM_34 - #define CAM_PIN_D5 GPIO_NUM_39 - #define CAM_PIN_D4 GPIO_NUM_36 - #define CAM_PIN_D3 GPIO_NUM_19 - #define CAM_PIN_D2 GPIO_NUM_18 - #define CAM_PIN_D1 GPIO_NUM_5 - #define CAM_PIN_D0 GPIO_NUM_4 - #define CAM_PIN_VSYNC GPIO_NUM_25 - #define CAM_PIN_HREF GPIO_NUM_23 - #define CAM_PIN_PCLK GPIO_NUM_22 +// SD card (operated with SDMMC peripheral) +//------------------------------------------------- +#define GPIO_SDCARD_CLK GPIO_NUM_14 +#define GPIO_SDCARD_CMD GPIO_NUM_15 +#define GPIO_SDCARD_D0 GPIO_NUM_2 +#ifndef __SD_USE_ONE_LINE_MODE__ +#error "Board not suppotred SD_FOUR_LINE_MODE" +// #define GPIO_SDCARD_D1 GPIO_NUM_4 +// #define GPIO_SDCARD_D2 GPIO_NUM_12 +// #define GPIO_SDCARD_D3 GPIO_NUM_13 +#else +#define GPIO_SDCARD_D1 GPIO_NUM_NC +#define GPIO_SDCARD_D2 GPIO_NUM_NC +#define GPIO_SDCARD_D3 GPIO_NUM_13 +#endif - //Statusled + ClassControllCamera - #define BLINK_GPIO GPIO_NUM_33 // PIN for red board LED, On the board the LED is on the IO2, but it is used for the SD - - //ClassControllCamera - #define FLASH_GPIO GPIO_NUM_12 // PIN for flashlight LED - #define USE_PWM_LEDFLASH // if __LEDGLOBAL is defined, a global variable is used for LED control, otherwise locally and each time a new +// Camera (suppotred OV2640 or OV5640) +//------------------------------------------------- +#define CAM_PIN_PWDN GPIO_NUM_32 +#define CAM_PIN_RESET GPIO_NUM_NC // software reset will be performed -#elif defined(BOARD_M5STACK_PSRAM) // M5STACK PSRAM PIN Map - #define CAM_PIN_PWDN GPIO_NUM_NC - #define CAM_PIN_RESET GPIO_NUM_15 - #define CAM_PIN_XCLK GPIO_NUM_27 - #define CAM_PIN_SIOD GPIO_NUM_25 - #define CAM_PIN_SIOC GPIO_NUM_23 +#define CAM_PIN_XCLK GPIO_NUM_0 +#define CAM_PIN_SIOD GPIO_NUM_26 +#define CAM_PIN_SIOC GPIO_NUM_27 - #define CAM_PIN_D7 GPIO_NUM_19 - #define CAM_PIN_D6 GPIO_NUM_36 - #define CAM_PIN_D5 GPIO_NUM_18 - #define CAM_PIN_D4 GPIO_NUM_39 - #define CAM_PIN_D3 GPIO_NUM_5 - #define CAM_PIN_D2 GPIO_NUM_34 - #define CAM_PIN_D1 GPIO_NUM_35 - #define CAM_PIN_D0 GPIO_NUM_32 - #define CAM_PIN_VSYNC GPIO_NUM_22 - #define CAM_PIN_HREF GPIO_NUM_26 - #define CAM_PIN_PCLK GPIO_NUM_21 +#define CAM_PIN_D7 GPIO_NUM_35 +#define CAM_PIN_D6 GPIO_NUM_34 +#define CAM_PIN_D5 GPIO_NUM_39 +#define CAM_PIN_D4 GPIO_NUM_36 +#define CAM_PIN_D3 GPIO_NUM_21 +#define CAM_PIN_D2 GPIO_NUM_19 +#define CAM_PIN_D1 GPIO_NUM_18 +#define CAM_PIN_D0 GPIO_NUM_5 - //Statusled + ClassControllCamera - #define BLINK_GPIO GPIO_NUM_33 // PIN for red board LED - - //ClassControllCamera - #define FLASH_GPIO GPIO_NUM_4 // PIN for flashlight LED - #define USE_PWM_LEDFLASH // if __LEDGLOBAL is defined, a global variable is used for LED control, otherwise locally and each time a new +#define CAM_PIN_VSYNC GPIO_NUM_25 +#define CAM_PIN_HREF GPIO_NUM_23 +#define CAM_PIN_PCLK GPIO_NUM_22 +#define CAM_XCLK_TIMER LEDC_TIMER_0 +#define CAM_XCLK_CHANNEL LEDC_CHANNEL_0 -#elif defined(BOARD_ESP32CAM_AITHINKER) // ESP32Cam (AiThinker) PIN Map - // SD card (operated with SDMMC peripheral) - //------------------------------------------------- - #define GPIO_SDCARD_CLK GPIO_NUM_14 - #define GPIO_SDCARD_CMD GPIO_NUM_15 - #define GPIO_SDCARD_D0 GPIO_NUM_2 - #ifndef __SD_USE_ONE_LINE_MODE__ - #define GPIO_SDCARD_D1 GPIO_NUM_4 - #define GPIO_SDCARD_D2 GPIO_NUM_12 - #define GPIO_SDCARD_D3 GPIO_NUM_13 - #else - #define GPIO_SDCARD_D1 GPIO_NUM_NC - #define GPIO_SDCARD_D2 GPIO_NUM_NC - #define GPIO_SDCARD_D3 GPIO_NUM_13 - #endif +// GpioHandler +#define GPIO_IO1 GPIO_NUM_4 +#define GPIO_IO2 GPIO_NUM_12 +#define GPIO_IO3 GPIO_NUM_13 +#define GPIO_IO4 GPIO_NUM_NC - #define CAM_PIN_PWDN GPIO_NUM_32 - #define CAM_PIN_RESET GPIO_NUM_NC //software reset will be performed - #define CAM_PIN_XCLK GPIO_NUM_0 - #define CAM_PIN_SIOD GPIO_NUM_26 - #define CAM_PIN_SIOC GPIO_NUM_27 +// Statusled + ClassControllCamera +#define BLINK_GPIO GPIO_NUM_33 // PIN for red board LED +#define BLINK_GPIO_INVERT - #define CAM_PIN_D7 GPIO_NUM_35 - #define CAM_PIN_D6 GPIO_NUM_34 - #define CAM_PIN_D5 GPIO_NUM_39 - #define CAM_PIN_D4 GPIO_NUM_36 - #define CAM_PIN_D3 GPIO_NUM_21 - #define CAM_PIN_D2 GPIO_NUM_19 - #define CAM_PIN_D1 GPIO_NUM_18 - #define CAM_PIN_D0 GPIO_NUM_5 - #define CAM_PIN_VSYNC GPIO_NUM_25 - #define CAM_PIN_HREF GPIO_NUM_23 - #define CAM_PIN_PCLK GPIO_NUM_22 +// ClassControllCamera +#define FLASH_GPIO GPIO_NUM_4 // PIN for flashlight LED +#define FLASH_MODE GPIO_PIN_MODE_BUILTIN_FLASH_PWM - //Statusled + ClassControllCamera - #define BLINK_GPIO GPIO_NUM_33 // PIN for red board LED - - //ClassControllCamera - #define FLASH_GPIO GPIO_NUM_4 // PIN for flashlight LED - #define USE_PWM_LEDFLASH // if __LEDGLOBAL is defined, a global variable is used for LED control, otherwise locally and each time a new +// ******* Board type WROVER_KIT_ESP32 +#elif defined(BOARD_WROVER_KIT_ESP32) // WROVER-KIT PIN Map +#define BoardType "WROVER-KIT" + +// Uart +//------------------------------------------------- +#define UART_PORT_NUM UART_NUM_0 +#define UART_TX_GPIO_NUM GPIO_NUM_1 +#define UART_RX_GPIO_NUM GPIO_NUM_3 +#define UART_RTS_GPIO_NUM GPIO_NUM_NC +#define UART_CTS_GPIO_NUM GPIO_NUM_NC +#define UART_BUFFER_SIZE 2048 + +// SD card (operated with SDMMC peripheral) +//------------------------------------------------- +#define GPIO_SDCARD_CLK GPIO_NUM_14 +#define GPIO_SDCARD_CMD GPIO_NUM_15 +#define GPIO_SDCARD_D0 GPIO_NUM_2 +#ifndef __SD_USE_ONE_LINE_MODE__ +#error "Board not suppotred SD_FOUR_LINE_MODE" +// #define GPIO_SDCARD_D1 GPIO_NUM_4 +// #define GPIO_SDCARD_D2 GPIO_NUM_12 +// #define GPIO_SDCARD_D3 GPIO_NUM_13 +#else +#define GPIO_SDCARD_D1 GPIO_NUM_NC +#define GPIO_SDCARD_D2 GPIO_NUM_NC +#define GPIO_SDCARD_D3 GPIO_NUM_13 +#endif + +#define CAM_PIN_PWDN GPIO_NUM_NC // power down is not used +#define CAM_PIN_RESET GPIO_NUM_NC // software reset will be performed + +#define CAM_PIN_XCLK GPIO_NUM_21 +#define CAM_PIN_SIOD GPIO_NUM_26 +#define CAM_PIN_SIOC GPIO_NUM_27 + +#define CAM_PIN_D7 GPIO_NUM_35 +#define CAM_PIN_D6 GPIO_NUM_34 +#define CAM_PIN_D5 GPIO_NUM_39 +#define CAM_PIN_D4 GPIO_NUM_36 +#define CAM_PIN_D3 GPIO_NUM_19 +#define CAM_PIN_D2 GPIO_NUM_18 +#define CAM_PIN_D1 GPIO_NUM_5 +#define CAM_PIN_D0 GPIO_NUM_4 + +#define CAM_PIN_VSYNC GPIO_NUM_25 +#define CAM_PIN_HREF GPIO_NUM_23 +#define CAM_PIN_PCLK GPIO_NUM_22 + +#define CAM_XCLK_TIMER LEDC_TIMER_0 +#define CAM_XCLK_CHANNEL LEDC_CHANNEL_0 + +// GpioHandler +#define GPIO_IO1 GPIO_NUM_12 +#define GPIO_IO2 GPIO_NUM_13 +#define GPIO_IO3 GPIO_NUM_33 +#define GPIO_IO4 GPIO_NUM_NC + +// Statusled + ClassControllCamera +#define BLINK_GPIO GPIO_NUM_NC // PIN for red board LED, On the board the LED is on the IO2, but it is used for the SD + +// ClassControllCamera +#define FLASH_GPIO GPIO_NUM_12 // PIN for flashlight LED +#define FLASH_MODE GPIO_PIN_MODE_BUILTIN_FLASH_PWM + +// ******* Board type FREENOVE_ESP32S3 +#elif defined(BOARD_FREENOVE_ESP32S3) +#define BoardType "ESP32-S3-CAM" + +// Uart +//------------------------------------------------- +#define UART_PORT_NUM UART_NUM_0 +#define UART_TX_GPIO_NUM GPIO_NUM_43 +#define UART_RX_GPIO_NUM GPIO_NUM_44 +#define UART_RTS_GPIO_NUM GPIO_NUM_NC +#define UART_CTS_GPIO_NUM GPIO_NUM_NC +#define UART_BUFFER_SIZE 2048 + +// SD card (operated with SDMMC peripheral) +//------------------------------------------------- +#define GPIO_SDCARD_CLK GPIO_NUM_39 // MTCK, GPIO39, CLK_OUT3, SUBSPICS1 +#define GPIO_SDCARD_CMD GPIO_NUM_38 // GPIO38, FSPIWP, SUBSPIWP +#define GPIO_SDCARD_D0 GPIO_NUM_40 // MTDO, GPIO40, CLK_OUT2 + +#ifndef __SD_USE_ONE_LINE_MODE__ +#define GPIO_SDCARD_D1 GPIO_NUM_41 // MTDI, GPIO41, CLK_OUT1 +#define GPIO_SDCARD_D2 GPIO_NUM_14 // RTC_GPIO14, GPIO14, TOUCH14, ADC2_CH3, FSPIWP, FSPIDQS, SUBSPIWP +#define GPIO_SDCARD_D3 GPIO_NUM_47 // SPICLK_P_DIFF, GPIO47, SUBSPICLK_P_DIFF +#else +#define GPIO_SDCARD_D1 GPIO_NUM_NC +#define GPIO_SDCARD_D2 GPIO_NUM_NC +#define GPIO_SDCARD_D3 GPIO_NUM_47 +#endif + +// Camera (suppotred OV2640 or OV5640) +//------------------------------------------------- +#define CAM_PIN_PWDN GPIO_NUM_NC +#define CAM_PIN_RESET GPIO_NUM_NC // software reset will be performed + +#define CAM_PIN_XCLK GPIO_NUM_15 +#define CAM_PIN_SIOD GPIO_NUM_4 +#define CAM_PIN_SIOC GPIO_NUM_5 + +#define CAM_PIN_D0 GPIO_NUM_11 // Y2_GPIO_NUM +#define CAM_PIN_D1 GPIO_NUM_9 // Y3_GPIO_NUM +#define CAM_PIN_D2 GPIO_NUM_8 // Y4_GPIO_NUM +#define CAM_PIN_D3 GPIO_NUM_10 // Y5_GPIO_NUM +#define CAM_PIN_D4 GPIO_NUM_12 // Y6_GPIO_NUM +#define CAM_PIN_D5 GPIO_NUM_18 // Y7_GPIO_NUM +#define CAM_PIN_D6 GPIO_NUM_17 // Y8_GPIO_NUM +#define CAM_PIN_D7 GPIO_NUM_16 // Y9_GPIO_NUM + +#define CAM_PIN_VSYNC GPIO_NUM_6 +#define CAM_PIN_HREF GPIO_NUM_7 +#define CAM_PIN_PCLK GPIO_NUM_13 + +#define CAM_XCLK_TIMER LEDC_TIMER_0 +#define CAM_XCLK_CHANNEL LEDC_CHANNEL_0 + +// GpioHandler +#define GPIO_IO1 GPIO_NUM_2 +#define GPIO_IO2 GPIO_NUM_21 +#define GPIO_IO3 GPIO_NUM_48 +#define GPIO_IO4 GPIO_NUM_NC + +// Statusled + ClassControllCamera +#define BLINK_GPIO GPIO_NUM_2 // PIN for red board LED +// #define BLINK_GPIO_INVERT + +// ClassControllCamera +#define FLASH_GPIO GPIO_NUM_48 // PIN for flashlight LED +#define FLASH_MODE GPIO_PIN_MODE_OUTPUT_WS281X +#define FLASH_SMARTLED_TYPE LED_WS2812 +#define FLASH_SMARTLED_COLOR Rgb{127, 127, 127} +#define FLASH_SMARTLED_QUANTITY 1 + +// ******* Board type ESP32_S3_ETH_V1 +#elif defined(BOARD_ESP32_S3_ETH_V1) +#define BoardType "ESP32-S3-ETH" + +// Uart +//------------------------------------------------- +#define UART_PORT_NUM UART_NUM_0 +#define UART_TX_GPIO_NUM GPIO_NUM_43 +#define UART_RX_GPIO_NUM GPIO_NUM_44 +#define UART_RTS_GPIO_NUM GPIO_NUM_NC +#define UART_CTS_GPIO_NUM GPIO_NUM_NC +#define UART_BUFFER_SIZE 2048 + +// SD card (operated with SDMMC peripheral) +//------------------------------------------------- +#define GPIO_SDCARD_CLK GPIO_NUM_40 +#define GPIO_SDCARD_CMD GPIO_NUM_42 +#define GPIO_SDCARD_D0 GPIO_NUM_41 + +#ifndef __SD_USE_ONE_LINE_MODE__ +#error "Board not suppotred SD_FOUR_LINE_MODE" +#else +#define GPIO_SDCARD_D1 GPIO_NUM_NC +#define GPIO_SDCARD_D2 GPIO_NUM_NC +#define GPIO_SDCARD_D3 GPIO_NUM_3 +#endif + +// SPI_ETHERNET_W5500 +//------------------------------------------------- +#define PER_ENABLE GPIO_NUM_46 +#define ETH_SPI_EN GPIO_NUM_45 + +#define ETH_SPI_NUM 1 + +#define ETH_SPI_HOST SPI2_HOST +#define ETH_SPI_CLOCK_MHZ 20 // SPI clock speed (MHz) (range 5 - 80, default 16) + +#define ETH_SPI_POLLING0_MS 10 // Set SPI Ethernet module polling period (default 10) +#define ETH_SPI_PHY_ADDR0 ESP_ETH_PHY_ADDR_AUTO // PHY address, set -1 to enable PHY address detection at initialization stage + +#define ETH_SPI_CS0_GPIO GPIO_NUM_39 +#define ETH_SPI_INT0_GPIO GPIO_NUM_38 +#define ETH_SPI_PHY_RST0_GPIO GPIO_NUM_NC + +#define ETH_SPI_MISO_GPIO GPIO_NUM_14 +#define ETH_SPI_MOSI_GPIO GPIO_NUM_1 +#define ETH_SPI_SCLK_GPIO GPIO_NUM_21 + +// Camera (suppotred OV2640 or OV5640) +//------------------------------------------------- +#define CAM_PIN_PWDN GPIO_NUM_NC +#define CAM_PIN_RESET GPIO_NUM_NC // software reset will be performed + +#define CAM_PIN_XCLK GPIO_NUM_15 +#define CAM_PIN_SIOD GPIO_NUM_4 +#define CAM_PIN_SIOC GPIO_NUM_5 + +#define CAM_PIN_D0 GPIO_NUM_11 // Y2_GPIO_NUM +#define CAM_PIN_D1 GPIO_NUM_9 // Y3_GPIO_NUM +#define CAM_PIN_D2 GPIO_NUM_8 // Y4_GPIO_NUM +#define CAM_PIN_D3 GPIO_NUM_10 // Y5_GPIO_NUM +#define CAM_PIN_D4 GPIO_NUM_12 // Y6_GPIO_NUM +#define CAM_PIN_D5 GPIO_NUM_18 // Y7_GPIO_NUM +#define CAM_PIN_D6 GPIO_NUM_17 // Y8_GPIO_NUM +#define CAM_PIN_D7 GPIO_NUM_16 // Y9_GPIO_NUM + +#define CAM_PIN_VSYNC GPIO_NUM_6 +#define CAM_PIN_HREF GPIO_NUM_7 +#define CAM_PIN_PCLK GPIO_NUM_13 + +#define CAM_XCLK_TIMER LEDC_TIMER_0 +#define CAM_XCLK_CHANNEL LEDC_CHANNEL_0 + +// GpioHandler +#define GPIO_IO1 GPIO_NUM_33 +#define GPIO_IO2 GPIO_NUM_34 +#define GPIO_IO3 GPIO_NUM_35 +#define GPIO_IO4 GPIO_NUM_36 + +// Statusled + ClassControllCamera +#define BLINK_GPIO GPIO_NUM_NC // PIN for red board LED + +// ClassControllCamera +#define FLASH_GPIO GPIO_NUM_47 // PIN for flashlight LED +#define FLASH_MODE GPIO_PIN_MODE_OUTPUT_WS281X +#define FLASH_SMARTLED_TYPE LED_WS2812 +#define FLASH_SMARTLED_COLOR Rgb{127, 127, 127} +#define FLASH_SMARTLED_QUANTITY 4 + +// ******* Board type ESP32_S3_ETH_V2 +#elif defined(BOARD_ESP32_S3_ETH_V2) +#define BoardType "ESP32-S3-ETH" + +// Uart +//------------------------------------------------- +#define UART_PORT_NUM UART_NUM_0 +#define UART_TX_GPIO_NUM GPIO_NUM_43 +#define UART_RX_GPIO_NUM GPIO_NUM_44 +#define UART_RTS_GPIO_NUM GPIO_NUM_NC +#define UART_CTS_GPIO_NUM GPIO_NUM_NC +#define UART_BUFFER_SIZE 2048 + +// SD card (operated with SDMMC peripheral) +//------------------------------------------------- +#define GPIO_SDCARD_CLK GPIO_NUM_40 +#define GPIO_SDCARD_CMD GPIO_NUM_42 +#define GPIO_SDCARD_D0 GPIO_NUM_41 + +#ifndef __SD_USE_ONE_LINE_MODE__ +#error "Board not suppotred SD_FOUR_LINE_MODE" +#else +#define GPIO_SDCARD_D1 GPIO_NUM_NC +#define GPIO_SDCARD_D2 GPIO_NUM_NC +#define GPIO_SDCARD_D3 GPIO_NUM_3 +#endif + +// SPI_ETHERNET_W5500 +//------------------------------------------------- +#define PER_ENABLE GPIO_NUM_46 +#define ETH_SPI_EN GPIO_NUM_45 + +#define ETH_SPI_NUM 1 + +#define ETH_SPI_HOST SPI2_HOST +#define ETH_SPI_CLOCK_MHZ 20 // SPI clock speed (MHz) (range 5 - 80, default 16) + +#define ETH_SPI_POLLING0_MS 10 // Set SPI Ethernet module polling period (default 10) +#define ETH_SPI_PHY_ADDR0 ESP_ETH_PHY_ADDR_AUTO // PHY address, set -1 to enable PHY address detection at initialization stage + +#define ETH_SPI_CS0_GPIO GPIO_NUM_39 +#define ETH_SPI_INT0_GPIO GPIO_NUM_38 +#define ETH_SPI_PHY_RST0_GPIO GPIO_NUM_NC + +#define ETH_SPI_MISO_GPIO GPIO_NUM_14 +#define ETH_SPI_MOSI_GPIO GPIO_NUM_1 +#define ETH_SPI_SCLK_GPIO GPIO_NUM_21 + +// Camera (suppotred OV2640 or OV5640) +//------------------------------------------------- +#define CAM_PIN_PWDN GPIO_NUM_NC +#define CAM_PIN_RESET GPIO_NUM_NC // software reset will be performed + +#define CAM_PIN_XCLK GPIO_NUM_15 +#define CAM_PIN_SIOD GPIO_NUM_4 +#define CAM_PIN_SIOC GPIO_NUM_5 + +#define CAM_PIN_D0 GPIO_NUM_11 // Y2_GPIO_NUM +#define CAM_PIN_D1 GPIO_NUM_9 // Y3_GPIO_NUM +#define CAM_PIN_D2 GPIO_NUM_8 // Y4_GPIO_NUM +#define CAM_PIN_D3 GPIO_NUM_10 // Y5_GPIO_NUM +#define CAM_PIN_D4 GPIO_NUM_47 // Y6_GPIO_NUM +#define CAM_PIN_D5 GPIO_NUM_18 // Y7_GPIO_NUM +#define CAM_PIN_D6 GPIO_NUM_17 // Y8_GPIO_NUM +#define CAM_PIN_D7 GPIO_NUM_16 // Y9_GPIO_NUM + +#define CAM_PIN_VSYNC GPIO_NUM_6 +#define CAM_PIN_HREF GPIO_NUM_7 +#define CAM_PIN_PCLK GPIO_NUM_13 + +#define CAM_XCLK_TIMER LEDC_TIMER_0 +#define CAM_XCLK_CHANNEL LEDC_CHANNEL_0 + +// GpioHandler +#define GPIO_IO1 GPIO_NUM_33 +#define GPIO_IO2 GPIO_NUM_34 +#define GPIO_IO3 GPIO_NUM_35 +#define GPIO_IO4 GPIO_NUM_36 + +// Statusled + ClassControllCamera +#define BLINK_GPIO GPIO_NUM_48 // PIN for red board LED + +// ClassControllCamera +#define FLASH_GPIO GPIO_NUM_12 // PIN for flashlight LED +#define FLASH_MODE GPIO_PIN_MODE_OUTPUT_WS281X +#define FLASH_SMARTLED_TYPE LED_WS2812 +#define FLASH_SMARTLED_COLOR Rgb{127, 127, 127} +#define FLASH_SMARTLED_QUANTITY 4 #else - #error "Board not selected" -#endif //Board PIN Map - +#error "Board not selected" +#endif // Board PIN Map // ******* LED definition -#ifdef USE_PWM_LEDFLASH - //// PWM für Flash-LED - #define LEDC_TIMER LEDC_TIMER_1 // LEDC_TIMER_0 - #define LEDC_MODE LEDC_LOW_SPEED_MODE - #define LEDC_OUTPUT_IO FLASH_GPIO // Define the output GPIO - #define LEDC_CHANNEL LEDC_CHANNEL_1 - #define LEDC_DUTY_RES LEDC_TIMER_13_BIT // Set duty resolution to 13 bits - //#define LEDC_DUTY (195) // Set duty to 50%. ((2 ** 13) - 1) * 50% = 4095 - #define LEDC_FREQUENCY (5000) // Frequency in Hertz. Set frequency at 5 kHz +//// PWM für Flash-LED +#define LEDC_TIMER LEDC_TIMER_1 +#define LEDC_CHANNEL LEDC_CHANNEL_1 +#define LEDC_MODE LEDC_LOW_SPEED_MODE +#define LEDC_DUTY_RES LEDC_TIMER_13_BIT // Set duty resolution to 13 bits +#define LEDC_FREQUENCY (5000) // Frequency in Hertz. Set frequency at 5 kHz +#define LEDC_OUTPUT_INVERT (0) // Enable (1) or disable (0) gpio output invert -#endif //USE_PWM_LEDFLASH - - -//softAP -#ifdef ENABLE_SOFTAP - #define EXAMPLE_ESP_WIFI_SSID "AI-on-the-Edge" - #define EXAMPLE_ESP_WIFI_PASS "" - #define EXAMPLE_ESP_WIFI_CHANNEL 11 - #define EXAMPLE_MAX_STA_CONN 1 -#endif // ENABLE_SOFTAP +// softAP +#define ESP_WIFI_AP_SSID "AI-on-the-Edge" +#define ESP_WIFI_AP_IP "192.168.4.1" +#define ESP_WIFI_AP_PASS "" +#define ESP_WIFI_AP_CHANNEL 11 +#define ESP_WIFI_AP_MAX_STA_CONN 1 #endif // ifndef defines_h diff --git a/code/main/CMakeLists.txt b/code/main/CMakeLists.txt index 98f1088a..8281e9f3 100644 --- a/code/main/CMakeLists.txt +++ b/code/main/CMakeLists.txt @@ -59,11 +59,8 @@ endif() ####################################################################### ####################################################################### - FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/main/*.*) -# idf_component_register(SRCS ${app_sources}) - idf_component_register(SRCS ${app_sources} - INCLUDE_DIRS "." + INCLUDE_DIRS "." "../include" REQUIRES esp_psram) \ No newline at end of file diff --git a/code/main/main.cpp b/code/main/main.cpp index d3ca2674..17717499 100644 --- a/code/main/main.cpp +++ b/code/main/main.cpp @@ -1,3 +1,5 @@ +#include "sdkconfig.h" + #include #include #include @@ -5,78 +7,32 @@ #include "esp_psram.h" #include "esp_pm.h" - #include "psram.h" #include "esp_chip_info.h" -// SD-Card //////////////////// #include "esp_vfs_fat.h" #include "ffconf.h" #include "driver/sdmmc_host.h" -#if (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(5, 1, 2)) -#include "sdcard_init.h" -#endif -/////////////////////////////// +#include "defines.h" +#include "Helper.h" +#include "read_network_config.h" +#include "network_init.h" +#include "server_ota.h" + +#include "MainFlowControl.h" #include "ClassLogFile.h" -#include "connect_wlan.h" -#include "read_wlanini.h" - -#include "server_main.h" -#include "MainFlowControl.h" -#include "server_file.h" -#include "server_ota.h" #include "time_sntp.h" -#include "configFile.h" -#include "server_main.h" -#include "server_camera.h" -#include "basic_auth.h" -#ifdef ENABLE_MQTT - #include "server_mqtt.h" -#endif //ENABLE_MQTT -#include "Helper.h" #include "statusled.h" #include "sdcard_check.h" -#include "../../include/defines.h" - -#ifdef ENABLE_SOFTAP - #include "softAP.h" -#endif //ENABLE_SOFTAP - #ifdef DISABLE_BROWNOUT_DETECTOR - #include "soc/soc.h" - #include "soc/rtc_cntl_reg.h" -#endif - -#ifdef DEBUG_ENABLE_SYSINFO -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 ) - #include "esp_sys.h" -#endif -#endif //DEBUG_ENABLE_SYSINFO - -// define `gpio_pad_select_gpip` for newer versions of IDF -#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)) -#include "esp_rom_gpio.h" -#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio -#endif - -#ifdef USE_HIMEM_IF_AVAILABLE - #include "esp32/himem.h" - #ifdef DEBUG_HIMEM_MEMORY_CHECK - #include "himem_memory_check.h" - #endif -#endif - -//#ifdef CONFIG_HEAP_TRACING_STANDALONE -#if defined HEAP_TRACING_MAIN_WIFI || defined HEAP_TRACING_MAIN_START - #include - #define NUM_RECORDS 300 - static heap_trace_record_t trace_record[NUM_RECORDS]; // This buffer must be in internal RAM +#include "soc/soc.h" +#include "soc/rtc_cntl_reg.h" #endif extern const char *GIT_TAG; @@ -88,19 +44,16 @@ extern std::string getFwVersion(void); extern std::string getHTMLversion(void); extern std::string getHTMLcommit(void); -std::vector splitString(const std::string& str); -void migrateConfiguration(void); bool setCpuFrequency(void); static const char *TAG = "MAIN"; -#define MOUNT_POINT "/sdcard" - -bool Init_NVS_SDCard() +bool init_nvs_sd_card() { esp_err_t ret = nvs_flash_init(); - - if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) + { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } @@ -161,84 +114,233 @@ bool Init_NVS_SDCard() slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; // Der PullUp des GPIO13 wird durch slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; - // nicht gesetzt, da er eigentlich nicht benötigt wird, + // nicht gesetzt, da er eigentlich nicht benötigt wird, // dies führt jedoch bei schlechten Kopien des AI_THINKER Boards // zu Problemen mit der SD Initialisierung und eventuell sogar zur reboot-loops. // Um diese Probleme zu kompensieren, wird der PullUp manuel gesetzt. - gpio_set_pull_mode(GPIO_SDCARD_D3, GPIO_PULLUP_ONLY); // HS2_D3 + gpio_set_pull_mode(GPIO_SDCARD_D3, GPIO_PULLUP_ONLY); // HS2_D3 // Options for mounting the filesystem. // If format_if_mount_failed is set to true, SD card will be partitioned and // formatted in case when mounting fails. esp_vfs_fat_sdmmc_mount_config_t mount_config = { .format_if_mount_failed = false, - .max_files = 12, // previously -> 2022-09-21: 5, 2023-01-02: 7 - .allocation_unit_size = 0, // 0 = auto + .max_files = 12, // previously -> 2022-09-21: 5, 2023-01-02: 7 + .allocation_unit_size = 0, // 0 = auto .disk_status_check_enable = 0, }; - sdmmc_card_t* card; + sdmmc_card_t *card; const char mount_point[] = MOUNT_POINT; // Use settings defined above to initialize SD card and mount FAT filesystem. // Note: esp_vfs_fat_sdmmc_mount is an all-in-one convenience function. - // Please check its source code and implement error recovery when developing - // production applications. -#if (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(5, 1, 2)) - ret = esp_vfs_fat_sdmmc_mount_mh(mount_point, &host, &slot_config, &mount_config, &card); -#else + // Please check its source code and implement error recovery when developing production applications. ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card); -#endif - if (ret != ESP_OK) { - if (ret == ESP_FAIL) { + if (ret != ESP_OK) + { + if (ret == ESP_FAIL) + { ESP_LOGE(TAG, "Failed to mount FAT filesystem on SD card. Check SD card filesystem (only FAT supported) or try another card"); - StatusLED(SDCARD_INIT, 1, true); - } - else if (ret == 263) { // Error code: 0x107 --> usually: SD not found - ESP_LOGE(TAG, "SD card init failed. Check if SD card is properly inserted into SD card slot or try another card"); - StatusLED(SDCARD_INIT, 2, true); + set_status_led(SDCARD_INIT, 1, true); } - else { + else if (ret == 263) + { + // Error code: 0x107 --> usually: SD not found + ESP_LOGE(TAG, "SD card init failed. Check if SD card is properly inserted into SD card slot or try another card"); + set_status_led(SDCARD_INIT, 2, true); + } + else + { ESP_LOGE(TAG, "SD card init failed. Check error code or try another card"); - StatusLED(SDCARD_INIT, 3, true); + set_status_led(SDCARD_INIT, 3, true); } return false; } - //sdmmc_card_print_info(stdout, card); // With activated CONFIG_NEWLIB_NANO_FORMAT --> capacity not printed correctly anymore - SaveSDCardInfo(card); + // sdmmc_card_print_info(stdout, card); // With activated CONFIG_NEWLIB_NANO_FORMAT --> capacity not printed correctly anymore + save_sd_card_info(card); return true; } +esp_err_t init_psram(void) +{ + // Init external PSRAM + // ******************************************** + esp_err_t result = esp_psram_init(); + if (result == ESP_FAIL) + { + // ESP_FAIL -> Failed to init PSRAM + ESP_LOGE(TAG, "PSRAM init failed (%s)! PSRAM not found or defective", std::to_string(result).c_str()); + set_system_statusflag(SYSTEM_STATUS_PSRAM_BAD); + set_status_led(PSRAM_INIT, 1, true); + return ESP_FAIL; + } + else + { + // ESP_OK -> PSRAM init OK --> continue to check PSRAM size + size_t psram_size = esp_psram_get_size(); + ESP_LOGI(TAG, "PSRAM size: %s byte (%s MB / %s MBit)", std::to_string(psram_size).c_str(), std::to_string(psram_size / 1024 / 1024).c_str(), std::to_string(psram_size / 1024 / 1024 * 8).c_str()); + + // Check PSRAM size + // ******************************************** + if (psram_size < (4 * 1024 * 1024)) + { + // PSRAM is below 4 MBytes (32Mbit) + ESP_LOGE(TAG, "PSRAM size >= 4MB (32Mbit) is mandatory to run this application"); + set_system_statusflag(SYSTEM_STATUS_PSRAM_BAD); + set_status_led(PSRAM_INIT, 2, true); + return ESP_FAIL; + } + else + { + // PSRAM size OK --> continue to check heap size + size_t _hsize = get_heapsize(); + ESP_LOGI(TAG, "Total heap: %s byte", std::to_string(_hsize).c_str()); + + // Check heap memory + // ******************************************** + if (_hsize < 4000000) + { + // Check available Heap memory for a bit less than 4 MB (a test on a good device showed 4187558 bytes to be available) + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Total heap >= 4000000 byte is mandatory to run this application"); + set_system_statusflag(SYSTEM_STATUS_HEAP_TOO_SMALL); + set_status_led(PSRAM_INIT, 3, true); + return ESP_FAIL; + } + else + { + // HEAP size OK --> continue to reserve shared memory block and check camera init + // Allocate static PSRAM memory regions + if (reserve_psram_shared_region() == false) + { + ESP_LOGI(TAG, "Allocate static PSRAM memory regions failed, heap too small!"); + set_system_statusflag(SYSTEM_STATUS_HEAP_TOO_SMALL); + set_status_led(PSRAM_INIT, 3, true); + return ESP_FAIL; + } + else + { + ESP_LOGI(TAG, "Allocate static PSRAM memory regions ok"); + return ESP_OK; + } + } + } + } + + return ESP_OK; +} + +esp_err_t init_camera(void) +{ + Camera.set_flash_light_on_off(false); + Camera.power_reset_camera(); + esp_err_t result = Camera.init_camera(); + + // Check camera init + // ******************************************** + if (result != ESP_OK) + { + // Camera init failed, retry to init + char camStatusHex[33]; + sprintf(camStatusHex, "0x%02x", result); + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Camera init failed (" + std::string(camStatusHex) + "), retrying..."); + + Camera.power_reset_camera(); + result = Camera.init_camera(); + Camera.set_flash_light_on_off(false); + + if (result != ESP_OK) + { + // Camera init failed again + sprintf(camStatusHex, "0x%02x", result); + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Camera init failed (" + std::string(camStatusHex) + ")! Check camera module and/or proper electrical connection"); + set_system_statusflag(SYSTEM_STATUS_CAM_BAD); + Camera.set_flash_light_on_off(false); // make sure flashlight is off + set_status_led(CAM_INIT, 1, true); + return ESP_FAIL; + } + } + + Camera.set_flash_light_on_off(false); // make sure flashlight is off before start of flow + + // Print camera infos + // ******************************************** + char caminfo[50]; + sensor_t *sensor = esp_camera_sensor_get(); + sprintf(caminfo, "PID: 0x%02x, VER: 0x%02x, MIDL: 0x%02x, MIDH: 0x%02x", sensor->id.PID, sensor->id.VER, sensor->id.MIDH, sensor->id.MIDL); + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Camera info: " + std::string(caminfo)); + + return ESP_OK; +} + extern "C" void app_main(void) { - //#ifdef CONFIG_HEAP_TRACING_STANDALONE - #if defined HEAP_TRACING_MAIN_WIFI || defined HEAP_TRACING_MAIN_START - //register a buffer to record the memory trace - ESP_ERROR_CHECK( heap_trace_init_standalone(trace_record, NUM_RECORDS) ); - #endif - - TickType_t xDelay; - - #ifdef DISABLE_BROWNOUT_DETECTOR - WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector - #endif +#ifdef DISABLE_BROWNOUT_DETECTOR + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disable brownout detector +#endif - #ifdef HEAP_TRACING_MAIN_START - ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) ); - #endif +#if (defined(BOARD_ESP32_S3_ETH_V1) || defined(BOARD_ESP32_S3_ETH_V2)) + // Configure IO Pad as General Purpose IO, + // so that it can be connected to internal Matrix, + // then combined with one or more peripheral signals. + gpio_pad_select_gpio(PER_ENABLE); + + gpio_set_direction(PER_ENABLE, GPIO_MODE_OUTPUT); + gpio_set_level(PER_ENABLE, 1); +#endif // ******************************************** - // Highlight start of app_main + // Highlight start of app_main // ******************************************** ESP_LOGI(TAG, "\n\n\n\n================ Start app_main ================="); - + // Init SD card // ******************************************** - if (!Init_NVS_SDCard()) + if (!init_nvs_sd_card()) { - ESP_LOGE(TAG, "Device init aborted!"); + ESP_LOGE(TAG, "!!! Device init aborted at step: init_nvs_sd_card() !!!"); + set_system_statusflag(SYSTEM_STATUS_SDCARD_CHECK_BAD); + vTaskDelay(2000 / portTICK_PERIOD_MS); + return; // No way to continue without working SD card! + } + + // Init external PSRAM + // ******************************************** + if (init_psram() != ESP_OK) + { + ESP_LOGE(TAG, "!!! Device init aborted at step: init_psram() !!!"); + set_system_statusflag(SYSTEM_STATUS_PSRAM_BAD); + vTaskDelay(2000 / portTICK_PERIOD_MS); + return; // No way to continue without working PSRAM! + } + + // SD card: basic R/W check + // ******************************************** + int iSDCardStatus = SDCardCheckRW(); + if (iSDCardStatus < 0) + { + if (iSDCardStatus <= -1 && iSDCardStatus >= -2) + { + // write error + set_status_led(SDCARD_CHECK, 1, true); + } + else if (iSDCardStatus <= -3 && iSDCardStatus >= -5) + { + // read error + set_status_led(SDCARD_CHECK, 2, true); + } + else if (iSDCardStatus == -6) + { + // delete error + set_status_led(SDCARD_CHECK, 3, true); + } + + ESP_LOGE(TAG, "!!! Device init aborted at step: SDCardCheckRW() !!!"); + set_system_statusflag(SYSTEM_STATUS_SDCARD_CHECK_BAD); + vTaskDelay(2000 / portTICK_PERIOD_MS); + return; // No way to continue without working SD card! } @@ -246,6 +348,22 @@ extern "C" void app_main(void) // ******************************************** LogFile.CreateLogDirectories(); // mandatory for logging + image saving + // SD card: Create further mandatory directories (if not already existing) + // Correct creation of these folders will be checked with function "SDCardCheckFolderFilePresence" + // ******************************************** + make_dir("/sdcard/firmware"); // mandatory for OTA firmware update + make_dir("/sdcard/img_tmp"); // mandatory for setting up alignment marks + make_dir("/sdcard/demo"); // mandatory for demo mode + make_dir("/sdcard/config/certs"); // mandatory for mqtt certificates + + // SD card: Check presence of some mandatory folders / files + // ******************************************** + if (!SDCardCheckFolderFilePresence()) + { + set_status_led(SDCARD_CHECK, 4, true); + set_system_statusflag(SYSTEM_STATUS_FOLDER_CHECK_BAD); // reduced web interface going to be loaded + } + // ******************************************** // Highlight start of logfile logging // Default Log Level: INFO -> Everything which needs to be logged during boot should be have level INFO, WARN OR ERROR @@ -254,752 +372,224 @@ extern "C" void app_main(void) LogFile.WriteToFile(ESP_LOG_INFO, TAG, "==================== Start ======================"); LogFile.WriteToFile(ESP_LOG_INFO, TAG, "================================================="); - // SD card: basic R/W check - // ******************************************** - int iSDCardStatus = SDCardCheckRW(); - if (iSDCardStatus < 0) { - if (iSDCardStatus <= -1 && iSDCardStatus >= -2) { // write error - StatusLED(SDCARD_CHECK, 1, true); - } - else if (iSDCardStatus <= -3 && iSDCardStatus >= -5) { // read error - StatusLED(SDCARD_CHECK, 2, true); - } - else if (iSDCardStatus == -6) { // delete error - StatusLED(SDCARD_CHECK, 3, true); - } - setSystemStatusFlag(SYSTEM_STATUS_SDCARD_CHECK_BAD); // reduced web interface going to be loaded - } - - // SD card: Create further mandatory directories (if not already existing) - // Correct creation of these folders will be checked with function "SDCardCheckFolderFilePresence" - // ******************************************** - MakeDir("/sdcard/firmware"); // mandatory for OTA firmware update - MakeDir("/sdcard/img_tmp"); // mandatory for setting up alignment marks - MakeDir("/sdcard/demo"); // mandatory for demo mode - MakeDir("/sdcard/config/certs"); // mandatory for mqtt certificates - // Check for updates // ******************************************** - CheckOTAUpdate(); - CheckUpdate(); - - // Init external PSRAM - // ******************************************** - esp_err_t PSRAMStatus = esp_psram_init(); - if (PSRAMStatus == ESP_FAIL) { // ESP_FAIL -> Failed to init PSRAM - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "PSRAM init failed (" + std::to_string(PSRAMStatus) + ")! PSRAM not found or defective"); - setSystemStatusFlag(SYSTEM_STATUS_PSRAM_BAD); - StatusLED(PSRAM_INIT, 1, true); - } - else { // ESP_OK -> PSRAM init OK --> continue to check PSRAM size - size_t psram_size = esp_psram_get_size(); // size_t psram_size = esp_psram_get_size(); // comming in IDF 5.0 - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "PSRAM size: " + std::to_string(psram_size) + " byte (" + std::to_string(psram_size/1024/1024) + - "MB / " + std::to_string(psram_size/1024/1024*8) + "MBit)"); - - // Check PSRAM size - // ******************************************** - if (psram_size < (4*1024*1024)) { // PSRAM is below 4 MBytes (32Mbit) - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "PSRAM size >= 4MB (32Mbit) is mandatory to run this application"); - setSystemStatusFlag(SYSTEM_STATUS_PSRAM_BAD); - StatusLED(PSRAM_INIT, 2, true); - } - else { // PSRAM size OK --> continue to check heap size - size_t _hsize = getESPHeapSize(); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Total heap: " + std::to_string(_hsize) + " byte"); - - // Check heap memory - // ******************************************** - if (_hsize < 4000000) { // Check available Heap memory for a bit less than 4 MB (a test on a good device showed 4187558 bytes to be available) - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Total heap >= 4000000 byte is mandatory to run this application"); - setSystemStatusFlag(SYSTEM_STATUS_HEAP_TOO_SMALL); - StatusLED(PSRAM_INIT, 3, true); - } - else { // HEAP size OK --> continue to reserve shared memory block and check camera init - /* Allocate static PSRAM memory regions */ - if (! reserve_psram_shared_region()) { - setSystemStatusFlag(SYSTEM_STATUS_HEAP_TOO_SMALL); - StatusLED(PSRAM_INIT, 3, true); - } - else { // PSRAM OK - // Init camera - // ******************************************** - PowerResetCamera(); - esp_err_t camStatus = Camera.InitCam(); - Camera.LightOnOff(false); - - xDelay = 2000 / portTICK_PERIOD_MS; - ESP_LOGD(TAG, "After camera initialization: sleep for: %ldms", (long) xDelay * CONFIG_FREERTOS_HZ/portTICK_PERIOD_MS); - vTaskDelay( xDelay ); - - // Check camera init - // ******************************************** - if (camStatus != ESP_OK) { // Camera init failed, retry to init - char camStatusHex[33]; - sprintf(camStatusHex,"0x%02x", camStatus); - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Camera init failed (" + std::string(camStatusHex) + "), retrying..."); - - PowerResetCamera(); - camStatus = Camera.InitCam(); - Camera.LightOnOff(false); - - xDelay = 2000 / portTICK_PERIOD_MS; - ESP_LOGD(TAG, "After camera initialization: sleep for: %ldms", (long) xDelay * CONFIG_FREERTOS_HZ/portTICK_PERIOD_MS); - vTaskDelay( xDelay ); - - if (camStatus != ESP_OK) { // Camera init failed again - sprintf(camStatusHex,"0x%02x", camStatus); - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Camera init failed (" + std::string(camStatusHex) + - ")! Check camera module and/or proper electrical connection"); - setSystemStatusFlag(SYSTEM_STATUS_CAM_BAD); - Camera.LightOnOff(false); // make sure flashlight is off - StatusLED(CAM_INIT, 1, true); - } - } - - if (camStatus == ESP_OK) { // ESP_OK -> Camera init OK --> continue to perform camera framebuffer check - // Camera framebuffer check - // ******************************************** - if (!Camera.testCamera()) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Camera framebuffer check failed"); - // Easiest would be to simply restart here and try again, - // how ever there seem to be systems where it fails at startup but still work correctly later. - // Therefore we treat it still as successed! */ - setSystemStatusFlag(SYSTEM_STATUS_CAM_FB_BAD); - StatusLED(CAM_INIT, 2, false); - } - Camera.LightOnOff(false); // make sure flashlight is off before start of flow - - // Print camera infos - // ******************************************** - char caminfo[50]; - sensor_t * s = esp_camera_sensor_get(); - sprintf(caminfo, "PID: 0x%02x, VER: 0x%02x, MIDL: 0x%02x, MIDH: 0x%02x", s->id.PID, s->id.VER, s->id.MIDH, s->id.MIDL); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Camera info: " + std::string(caminfo)); - } - } - } - } - } - - // Migrate parameter in config.ini to new naming (firmware 15.0 and newer) - // ******************************************** - migrateConfiguration(); - - // Init time (as early as possible, but SD card needs to be initialized) - // ******************************************** - setupTime(); // NTP time service: Status of time synchronization will be checked after every round (server_tflite.cpp) - - // Set CPU Frequency - // ******************************************** - setCpuFrequency(); - - // Start SoftAP for initial remote setup - // Note: Start AP if no wlan.ini and/or config.ini available, e.g. SD card empty; function does not exit anymore until reboot - // ******************************************** - #ifdef ENABLE_SOFTAP - CheckStartAPMode(); - #endif - - // SD card: Check presence of some mandatory folders / files - // ******************************************** - if (!SDCardCheckFolderFilePresence()) { - StatusLED(SDCARD_CHECK, 4, true); - setSystemStatusFlag(SYSTEM_STATUS_FOLDER_CHECK_BAD); // reduced web interface going to be loaded - } - - // Check version information - // ******************************************** - std::string versionFormated = getFwVersion() + ", Date/Time: " + std::string(BUILD_TIME) + \ - ", Web UI: " + getHTMLversion(); - - if (std::string(GIT_TAG) != "") { // We are on a tag, add it as prefix - versionFormated = "Tag: '" + std::string(GIT_TAG) + "', " + versionFormated; - } - LogFile.WriteToFile(ESP_LOG_INFO, TAG, versionFormated); - - if (getHTMLcommit().substr(0, 7) == "?") - LogFile.WriteToFile(ESP_LOG_WARN, TAG, std::string("Failed to read file html/version.txt to parse Web UI version")); - - if (getHTMLcommit().substr(0, 7) != std::string(GIT_REV).substr(0, 7)) { // Compare the first 7 characters of both hashes - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Web UI version (" + getHTMLcommit() + ") does not match firmware version (" + std::string(GIT_REV) + ")"); - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Recommendation: Repeat installation using AI-on-the-edge-device__update__*.zip"); - } - - // Check reboot reason - // ******************************************** - CheckIsPlannedReboot(); - if (!getIsPlannedReboot() && (esp_reset_reason() == ESP_RST_PANIC)) { // If system reboot was not triggered by user and reboot was caused by execption - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reset reason: " + getResetReason()); - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Device was rebooted due to a software exception! Log level is set to DEBUG until the next reboot. " - "Flow init is delayed by 5 minutes to check the logs or do an OTA update"); - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Keep device running until crash occurs again and check logs after device is up again"); - LogFile.setLogLevel(ESP_LOG_DEBUG); - } - else { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Reset reason: " + getResetReason()); - } - - #ifdef HEAP_TRACING_MAIN_START - ESP_ERROR_CHECK( heap_trace_stop() ); - heap_trace_dump(); - #endif - - #ifdef HEAP_TRACING_MAIN_WIFI - ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) ); - #endif - - // Read WLAN parameter and start WIFI - // ******************************************** - int iWLANStatus = LoadWlanFromFile(WLAN_CONFIG_FILE); - if (iWLANStatus == 0) { - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "WLAN config loaded, init WIFI..."); - if (wifi_init_sta() != ESP_OK) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "WIFI init failed. Device init aborted!"); - StatusLED(WLAN_INIT, 3, true); - return; - } - - init_basic_auth(); - } - else if (iWLANStatus == -1) { // wlan.ini not available, potentially empty or content not readable - StatusLED(WLAN_INIT, 1, true); - return; // No way to continue without reading the wlan.ini - } - else if (iWLANStatus == -2) { // SSID or password not configured - StatusLED(WLAN_INIT, 2, true); - return; // No way to continue with empty SSID or password! - } - - xDelay = 2000 / portTICK_PERIOD_MS; - ESP_LOGD(TAG, "main: sleep for: %ldms", (long) xDelay * CONFIG_FREERTOS_HZ/portTICK_PERIOD_MS); - vTaskDelay( xDelay ); - - // manual reset the time - // ******************************************** - if (!time_manual_reset_sync()) + ESP_LOGI(TAG, "check for updates"); + CheckOTAUpdateStatus(); + if (!CheckOTAUpdateAvailability()) { - LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Manual Time Sync failed during startup" ); - } - - // Set log level for wifi component to WARN level (default: INFO; only relevant for serial console) - // ******************************************** - esp_log_level_set("wifi", ESP_LOG_WARN); - - #ifdef HEAP_TRACING_MAIN_WIFI - ESP_ERROR_CHECK( heap_trace_stop() ); - heap_trace_dump(); - #endif - - #ifdef DEBUG_ENABLE_SYSINFO - #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 ) - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Device Info : " + get_device_info() ); - ESP_LOGD(TAG, "Device infos %s", get_device_info().c_str()); - #endif - #endif //DEBUG_ENABLE_SYSINFO - - #ifdef USE_HIMEM_IF_AVAILABLE - #ifdef DEBUG_HIMEM_MEMORY_CHECK - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Himem mem check : " + himem_memory_check() ); - ESP_LOGD(TAG, "Himem mem check %s", himem_memory_check().c_str()); - #endif - #endif - - // Print Device info - // ******************************************** - esp_chip_info_t chipInfo; - esp_chip_info(&chipInfo); - - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Device info: CPU cores: " + std::to_string(chipInfo.cores) + - ", Chip revision: " + std::to_string(chipInfo.revision)); - - // Print SD-Card info - // ******************************************** - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "SD card info: Name: " + getSDCardName() + ", Capacity: " + - getSDCardCapacity() + "MB, Free: " + getSDCardFreePartitionSpace() + "MB"); - - xDelay = 2000 / portTICK_PERIOD_MS; - ESP_LOGD(TAG, "main: sleep for: %ldms", (long) xDelay * CONFIG_FREERTOS_HZ/portTICK_PERIOD_MS); - vTaskDelay( xDelay ); - - // Start webserver + register handler - // ******************************************** - ESP_LOGD(TAG, "starting servers"); - - server = start_webserver(); - register_server_camera_uri(server); - register_server_main_flow_task_uri(server); - register_server_file_uri(server, "/sdcard"); - register_server_ota_sdcard_uri(server); - #ifdef ENABLE_MQTT - register_server_mqtt_uri(server); - #endif //ENABLE_MQTT - - gpio_handler_create(server); - - ESP_LOGD(TAG, "Before reg server main"); - register_server_main_uri(server, "/sdcard"); - - // Only for testing purpose - //setSystemStatusFlag(SYSTEM_STATUS_CAM_FB_BAD); - //setSystemStatusFlag(SYSTEM_STATUS_PSRAM_BAD); - - // Check main init + start TFlite task - // ******************************************** - if (getSystemStatus() == 0) { // No error flag is set - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Initialization completed successfully"); - InitializeFlowTask(); - } - else if (isSetSystemStatusFlag(SYSTEM_STATUS_CAM_FB_BAD) || // Non critical errors occured, we try to continue... - isSetSystemStatusFlag(SYSTEM_STATUS_NTP_BAD)) { - LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Initialization completed with non-critical errors!"); - InitializeFlowTask(); - } - else { // Any other error is critical and makes running the flow impossible. Init is going to abort. - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Initialization failed. Flow task start aborted. Loading reduced web interface..."); - } -} - -void migrateConfiguration(void) { - std::vector splitted; - bool migrated = false; - - bool CamZoom_found = false; - int CamZoom_lines = 0; - bool CamZoom_value = false; - int CamZoomSize_lines = 0; - int CamZoomSize_value = 0; - int CamZoomOffsetX_lines = 0; - int CamZoomOffsetX_value = 0; - int CamZoomOffsetY_lines = 0; - int CamZoomOffsetY_value = 0; - - if (!FileExists(CONFIG_FILE)) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Config file seems to be missing!"); - return; - } - - std::string section = ""; - std::ifstream ifs(CONFIG_FILE); - std::string content((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); - - /* Split config file it array of lines */ - std::vector configLines = splitString(content); - - /* Process each line */ - for (int i = 0; i < configLines.size(); i++) { - // ESP_LOGI(TAG, "Line %d: %s", i, configLines[i].c_str()); - - if (configLines[i].find("[") != std::string::npos) { - // Start of new section - section = configLines[i]; - replaceString(section, ";", "", false); // Remove possible semicolon (just for the string comparison) - // ESP_LOGI(TAG, "New section: %s", section.c_str()); + // Check reboot reason + // ******************************************** + ESP_LOGI(TAG, "check reboot reason"); + CheckIsPlannedReboot(); + if (!getIsPlannedReboot() && (esp_reset_reason() == ESP_RST_PANIC)) + { + // If system reboot was not triggered by user and reboot was caused by execption + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reset reason: " + get_reset_reason()); + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Device was rebooted due to a software exception! Log level is set to DEBUG until the next reboot. " + "Flow init is delayed by 5 minutes to check the logs or do an OTA update"); + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Keep device running until crash occurs again and check logs after device is up again"); + LogFile.setLogLevel(ESP_LOG_DEBUG); } - else { - splitted = ZerlegeZeile(configLines[i]); + else + { + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Reset reason: " + get_reset_reason()); } - /* Migrate parameters as needed - * For the boolean parameters, we make them enabled all the time now: - * 1. If they where disabled, set them to their default value - * 2. Enable them - * Notes: - * The migration has some simplifications: - * - Case Sensitiveness must be like in the initial config.ini - * - No Whitespace after a semicollon - * - Only one whitespace before/after the equal sign - */ - if (section == "[MakeImage]") { - migrated = migrated | replaceString(configLines[i], "[MakeImage]", "[TakeImage]"); // Rename the section itself + // Set CPU Frequency + // ******************************************** + ESP_LOGI(TAG, "set CPU Frequency"); + setCpuFrequency(); + +#ifdef CONFIG_SOC_TEMP_SENSOR_SUPPORTED + ESP_LOGI(TAG, "init Tempsensor"); + init_tempsensor(); +#endif + + // Init camera + // ******************************************** + ESP_LOGI(TAG, "init camera"); + if (init_camera() != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "!!! Error at step: init_camera() !!!"); } - if (section == "[MakeImage]" || section == "[TakeImage]") { - if ((isInString(configLines[i], "Brightness")) && (!isInString(configLines[i], "CamBrightness"))) { - migrated = migrated | replaceString(configLines[i], "Brightness", "CamBrightness"); - } - else if ((isInString(configLines[i], "Contrast")) && (!isInString(configLines[i], "CamContrast"))) { - migrated = migrated | replaceString(configLines[i], "Contrast", "CamContrast"); - } - else if ((isInString(configLines[i], "Saturation")) && (!isInString(configLines[i], "CamSaturation"))) { - migrated = migrated | replaceString(configLines[i], "Saturation", "CamSaturation"); - } - else if ((isInString(configLines[i], "Sharpness")) && (!isInString(configLines[i], "CamSharpness")) && (!isInString(configLines[i], "CamAutoSharpness"))) { - migrated = migrated | replaceString(configLines[i], "Sharpness", "CamSharpness"); - } - else if ((isInString(configLines[i], "Aec2")) && (!isInString(configLines[i], "CamAec")) && (!isInString(configLines[i], "CamAec2"))) { - migrated = migrated | replaceString(configLines[i], "Aec2", "CamAec2"); - } - else if ((isInString(configLines[i], "Zoom")) && (!isInString(configLines[i], "CamZoom")) && (!isInString(configLines[i], "ZoomMode")) && (!isInString(configLines[i], "ZoomOffsetX")) && (!isInString(configLines[i], "ZoomOffsetY"))) { - CamZoom_lines = i; - if (splitted.size() < 2) { - CamZoom_value = false; - } - else { - // ESP_LOGI(TAG, "splitted[1]: %s", splitted[1].c_str()); - CamZoom_value = alphanumericToBoolean(splitted[1]); - } - CamZoom_found = true; - } - else if ((isInString(configLines[i], "ZoomMode")) && (!isInString(configLines[i], "CamZoom"))) { - CamZoomSize_lines = i; - if (splitted.size() < 2) { - CamZoomSize_value = 0; - } - else { - if (isStringNumeric(splitted[1])) { - CamZoomSize_value = std::stof(splitted[1]); - } - else { - CamZoomSize_value = 0; - } - } - CamZoom_found = true; - } - else if ((isInString(configLines[i], "ZoomOffsetX")) && (!isInString(configLines[i], "CamZoom")) && (!isInString(configLines[i], "ZoomOffsetY"))) { - CamZoomOffsetX_lines = i; - if (splitted.size() < 2) { - CamZoomOffsetX_value = 0; - } - else { - if (isStringNumeric(splitted[1])) { - CamZoomOffsetX_value = std::stof(splitted[1]); - } - else { - CamZoomOffsetX_value = 0; - } - } - CamZoom_found = true; - } - else if ((isInString(configLines[i], "ZoomOffsetY")) && (!isInString(configLines[i], "CamZoom")) && (!isInString(configLines[i], "ZoomOffsetX"))) { - CamZoomOffsetY_lines = i; - if (splitted.size() < 2) { - CamZoomOffsetY_value = 0; - } - else { - if (isStringNumeric(splitted[1])) { - CamZoomOffsetY_value = std::stof(splitted[1]); - } - else { - CamZoomOffsetY_value = 0; - } - } - CamZoom_found = true; - } - else { - migrated = migrated | replaceString(configLines[i], "LogImageLocation", "RawImagesLocation"); - migrated = migrated | replaceString(configLines[i], "LogfileRetentionInDays", "RawImagesRetention"); + // Check version information + // ******************************************** + ESP_LOGI(TAG, "check version information"); + std::string versionFormated = getFwVersion() + ", Date/Time: " + std::string(BUILD_TIME) + ", Web UI: " + getHTMLversion(); - migrated = migrated | replaceString(configLines[i], ";Demo = true", ";Demo = false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";Demo", "Demo"); // Enable it + if (std::string(GIT_TAG) != "") + { + // We are on a tag, add it as prefix + versionFormated = "Tag: '" + std::string(GIT_TAG) + "', " + versionFormated; + } + LogFile.WriteToFile(ESP_LOG_INFO, TAG, versionFormated); - migrated = migrated | replaceString(configLines[i], "ImageQuality", "CamQuality"); - migrated = migrated | replaceString(configLines[i], "AutoExposureLevel", "CamAeLevel"); - migrated = migrated | replaceString(configLines[i], "FixedExposure", "CamAec"); - - migrated = migrated | replaceString(configLines[i], "ImageSize", ";UNUSED_PARAMETER"); // This parameter is no longer used - migrated = migrated | replaceString(configLines[i], "Grayscale", ";UNUSED_PARAMETER"); // This parameter is no longer used - migrated = migrated | replaceString(configLines[i], "Negative", ";UNUSED_PARAMETER"); // This parameter is no longer used - } - } - else if (section == "[Alignment]") { - migrated = migrated | replaceString(configLines[i], "InitialMirror", ";UNUSED_PARAMETER"); // This parameter is no longer used - migrated = migrated | replaceString(configLines[i], ";InitialMirror", ";UNUSED_PARAMETER"); // This parameter is no longer used - migrated = migrated | replaceString(configLines[i], "FlipImageSize", ";UNUSED_PARAMETER"); // This parameter is no longer used - migrated = migrated | replaceString(configLines[i], ";FlipImageSize", ";UNUSED_PARAMETER"); // This parameter is no longer used - } - else if (section == "[Digits]") { - migrated = migrated | replaceString(configLines[i], "LogImageLocation", "ROIImagesLocation"); - migrated = migrated | replaceString(configLines[i], "LogfileRetentionInDays", "ROIImagesRetention"); - } - else if (section == "[Analog]") { - migrated = migrated | replaceString(configLines[i], "LogImageLocation", "ROIImagesLocation"); - migrated = migrated | replaceString(configLines[i], "LogfileRetentionInDays", "ROIImagesRetention"); - migrated = migrated | replaceString(configLines[i], "ExtendedResolution", ";UNUSED_PARAMETER"); // This parameter is no longer used - } - else if (section == "[PostProcessing]") { - /* AllowNegativeRates has a as prefix! */ - if (isInString(configLines[i], "AllowNegativeRates") && isInString(configLines[i], ";")) { - // It is the parameter "AllowNegativeRates" and it is commented out - migrated = migrated | replaceString(configLines[i], "true", "false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it - } - /* IgnoreLeadingNaN has a as prefix! */ - else if (isInString(configLines[i], "IgnoreLeadingNaN") && isInString(configLines[i], ";")) { - // It is the parameter "IgnoreLeadingNaN" and it is commented out - migrated = migrated | replaceString(configLines[i], "true", "false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it - } - /* ExtendedResolution has a as prefix! */ - else if (isInString(configLines[i], "ExtendedResolution") && isInString(configLines[i], ";")) { - // It is the parameter "ExtendedResolution" and it is commented out - migrated = migrated | replaceString(configLines[i], "true", "false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";", ""); // Enable it - } - else { - migrated = migrated | replaceString(configLines[i], ";PreValueUse = true", ";PreValueUse = false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";PreValueUse", "PreValueUse"); // Enable it - - migrated = migrated | replaceString(configLines[i], ";ErrorMessage = true", ";ErrorMessage = false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";ErrorMessage", "ErrorMessage"); // Enable it - - migrated = migrated | replaceString(configLines[i], ";CheckDigitIncreaseConsistency = true", ";CheckDigitIncreaseConsistency = false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";CheckDigitIncreaseConsistency", "CheckDigitIncreaseConsistency"); // Enable it - } - } - else if (section == "[MQTT]") { - migrated = migrated | replaceString(configLines[i], "SetRetainFlag", "RetainMessages"); // First rename it, enable it with its default value - migrated = migrated | replaceString(configLines[i], ";RetainMessages = true", ";RetainMessages = false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";RetainMessages", "RetainMessages"); // Enable it - - migrated = migrated | replaceString(configLines[i], ";HomeassistantDiscovery = true", ";HomeassistantDiscovery = false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";HomeassistantDiscovery", "HomeassistantDiscovery"); // Enable it - - // only if string starts with "Topic" (Was the naming in very old version) - if (configLines[i].rfind("Topic", 0) != std::string::npos) { - migrated = migrated | replaceString(configLines[i], "Topic", "MainTopic"); - } - } - else if (section == "[InfluxDB]") { - /* Fieldname has a as prefix! */ - if (isInString(configLines[i], "Fieldname")) { - // It is the parameter "Fieldname" - migrated = migrated | replaceString(configLines[i], "Fieldname", "Field"); // Rename it to Field - } - } - else if (section == "[InfluxDBv2]") { - /* Fieldname has a as prefix! */ - if (isInString(configLines[i], "Fieldname")) { - // It is the parameter "Fieldname" - migrated = migrated | replaceString(configLines[i], "Fieldname", "Field"); // Rename it to Field - } - /* Database got renamed to Bucket! */ - else if (isInString(configLines[i], "Database")) { - // It is the parameter "Database" - migrated = migrated | replaceString(configLines[i], "Database", "Bucket"); // Rename it to Bucket - } - } - else if (section == "[GPIO]") { - } - else if (section == "[DataLogging]") { - migrated = migrated | replaceString(configLines[i], "DataLogRetentionInDays", "DataFilesRetention"); - /* DataLogActive is true by default! */ - migrated = migrated | replaceString(configLines[i], ";DataLogActive = false", ";DataLogActive = true"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";DataLogActive", "DataLogActive"); // Enable it - } - else if (section == "[AutoTimer]") { - migrated = migrated | replaceString(configLines[i], "Intervall", "Interval"); - migrated = migrated | replaceString(configLines[i], "Autostart", ";UNUSED_PARAMETER"); // This parameter is no longer used - } - else if (section == "[Debug]") { - migrated = migrated | replaceString(configLines[i], "Logfile ", "LogLevel "); // Whitespace needed so it does not match `LogfileRetentionInDays` - /* LogLevel (resp. LogFile) was originally a boolean, but we switched it to an int - * For both cases (true/false), we set it to level 2 (WARNING) */ - migrated = migrated | replaceString(configLines[i], "LogLevel = true", "LogLevel = 2"); - migrated = migrated | replaceString(configLines[i], "LogLevel = false", "LogLevel = 2"); - migrated = migrated | replaceString(configLines[i], "LogfileRetentionInDays", "LogfilesRetention"); - } - else if (section == "[System]") { - migrated = migrated | replaceString(configLines[i], "RSSIThreashold", "RSSIThreshold"); - migrated = migrated | replaceString(configLines[i], "AutoAdjustSummertime", ";UNUSED_PARAMETER"); // This parameter is no longer used - - migrated = migrated | replaceString(configLines[i], ";SetupMode = true", ";SetupMode = false"); // Set it to its default value - migrated = migrated | replaceString(configLines[i], ";SetupMode", "SetupMode"); // Enable it - } - } - - if (CamZoom_found == true) { - if (CamZoomSize_value == 0) { - // mode0 - // 1600 - 640 = 960 / 2 = max-Offset (+/-) 480 - // 1200 - 480 = 720 / 2 = max-Offset (+/-) 360 - - if (CamZoomOffsetX_value > 960) { - CamZoomOffsetX_value = 960; - } - else { - CamZoomOffsetX_value = (floor((CamZoomOffsetX_value - 480) / 8) * 8); - } - - if (CamZoomOffsetY_value > 720) { - CamZoomOffsetY_value = 720; - } - else { - CamZoomOffsetY_value = (floor((CamZoomOffsetY_value - 360) / 8) * 8); - } - - CamZoomSize_value = 29; - } - else { - // mode1 - // 800 - 640 = 160 / 2 = max-Offset (+/-) 80 - // 600 - 480 = 120 / 2 = max-Offset (+/-) 60 - - if (CamZoomOffsetX_value > 160) { - CamZoomOffsetX_value = 160; - } - else { - CamZoomOffsetX_value = (floor(((CamZoomOffsetX_value - 80) * 2) / 8) * 8); - } - - if (CamZoomOffsetY_value > 120) { - CamZoomOffsetY_value = 120; - } - else { - CamZoomOffsetY_value = (floor(((CamZoomOffsetY_value - 60) * 2) / 8) * 8); - } - - CamZoomSize_value = 9; + if (getHTMLcommit().substr(0, 7) == "?") + { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, std::string("Failed to read file html/version.txt to parse Web UI version")); } - if (CamZoom_lines > 0) { - if (CamZoom_value) { - configLines[CamZoom_lines] = ("CamZoom = true"); - } - else { - configLines[CamZoom_lines] = ("CamZoom = false"); - } - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Migrated Configfile line 'Zoom' to 'CamZoom'"); - migrated = true; + if (getHTMLcommit().substr(0, 7) != std::string(GIT_REV).substr(0, 7)) + { + // Compare the first 7 characters of both hashes + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Web UI version (" + getHTMLcommit() + ") does not match firmware version (" + std::string(GIT_REV) + ")"); + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Recommendation: Repeat installation using AI-on-the-edge-device__update__*.zip"); } - if (CamZoomSize_lines > 0) { - configLines[CamZoomSize_lines] = ("CamZoomSize = " + std::to_string(CamZoomSize_value)); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Migrated Configfile line 'ZoomMode' to 'CamZoomSize'"); - migrated = true; - } - if (CamZoomOffsetX_lines > 0) { - configLines[CamZoomOffsetX_lines] = ("CamZoomOffsetX = " + std::to_string(CamZoomOffsetX_value)); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Migrated Configfile line 'ZoomOffsetX' to 'CamZoomOffsetX'"); - migrated = true; - } - if (CamZoomOffsetY_lines > 0) { - configLines[CamZoomOffsetY_lines] = ("CamZoomOffsetY = " + std::to_string(CamZoomOffsetY_value)); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Migrated Configfile line 'ZoomOffsetY' to 'CamZoomOffsetY'"); - migrated = true; - } - } - if (migrated) { - // At least one replacement happened - if (!RenameFile(CONFIG_FILE, CONFIG_FILE_BACKUP)) { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to create backup of Config file!"); + // Start webserver + register handler + // ******************************************** + ESP_LOGI(TAG, "starting Webserver"); + if (init_network() != ESP_OK) + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Webserver init failed. Device init aborted!"); + set_status_led(WLAN_INIT, 3, true); return; } - FILE *pfile = fopen(CONFIG_FILE, "w"); + // Set log level for wifi component to WARN level (default: INFO; only relevant for serial console) + // ******************************************** + esp_log_level_set("wifi", ESP_LOG_WARN); - for (int i = 0; i < configLines.size(); i++) { - if (!isInString(configLines[i], ";UNUSED_PARAMETER")) { - fwrite(configLines[i].c_str(), configLines[i].length(), 1, pfile); - fwrite("\n", 1, 1, pfile); + // Init time (as early as possible, but SD card needs to be initialized) + // ******************************************** + if (network_config.connection_type != NETWORK_CONNECTION_DISCONNECT) + { + ESP_LOGI(TAG, "setup Time"); + setupTime(); // NTP time service: Status of time synchronization will be checked after every round (server_tflite.cpp) + } + + TickType_t xDelay = 2000 / portTICK_PERIOD_MS; + ESP_LOGD(TAG, "main: sleep for: %ldms", (long)xDelay * CONFIG_FREERTOS_HZ / portTICK_PERIOD_MS); + vTaskDelay(xDelay); + + // manual reset the time + // ******************************************** + if (network_config.connection_type != NETWORK_CONNECTION_DISCONNECT) + { + ESP_LOGI(TAG, "manual reset the time"); + if (!time_manual_reset_sync()) + { + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Manual Time Sync failed during startup"); } } - fclose(pfile); - LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Config file migrated. Saved backup to " + string(CONFIG_FILE_BACKUP)); - } -} + // Print Device info + // ******************************************** + ESP_LOGI(TAG, "print Device info"); + esp_chip_info_t chipInfo; + esp_chip_info(&chipInfo); -std::vector splitString(const std::string& str) { - std::vector tokens; - - std::stringstream ss(str); - std::string token; + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Device info: CPU cores: " + std::to_string(chipInfo.cores) + ", Chip revision: " + std::to_string(chipInfo.revision)); - while (std::getline(ss, token, '\n')) { - tokens.push_back(token); - } - - return tokens; -} + // Print SD-Card info + // ******************************************** + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "SD card info: Name: " + get_sd_card_name() + ", Capacity: " + get_sd_card_capacity() + "MB, Free: " + get_sd_card_free_partition_space() + "MB"); -/*bool replace_all(std::string& s, std::string const& toReplace, std::string const& replaceWith) { - std::string buf; - std::size_t pos = 0; - std::size_t prevPos; - bool found = false; + xDelay = 2000 / portTICK_PERIOD_MS; + ESP_LOGD(TAG, "main: sleep for: %ldms", (long)xDelay * CONFIG_FREERTOS_HZ / portTICK_PERIOD_MS); + vTaskDelay(xDelay); - // Reserves rough estimate of final size of string. - buf.reserve(s.size()); - - while (true) { - prevPos = pos; - pos = s.find(toReplace, pos); - if (pos == std::string::npos) { - break; + // Check main init + start TFlite task + // ******************************************** + ESP_LOGI(TAG, "check main init"); + if (get_system_status() == 0) + { + // No error flag is set + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Initialization completed successfully"); + InitializeFlowTask(); + } + else if (is_set_system_statusflag(SYSTEM_STATUS_CAM_FB_BAD) || is_set_system_statusflag(SYSTEM_STATUS_NTP_BAD)) + { + // Non critical errors occured, we try to continue... + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Initialization completed with non-critical errors!"); + InitializeFlowTask(); + } + else + { + // Any other error is critical and makes running the flow impossible. Init is going to abort. + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Initialization failed. Flow task start aborted. Loading reduced web interface..."); } - found = true; - buf.append(s, prevPos, pos - prevPos); - buf += replaceWith; - pos += toReplace.size(); } +} - buf.append(s, prevPos, s.size() - prevPos); - s.swap(buf); - - return found; -}*/ - -bool setCpuFrequency(void) { - ConfigFile configFile = ConfigFile(CONFIG_FILE); - string cpuFrequency = "160"; - esp_pm_config_t pm_config; - - if (!configFile.ConfigFileExists()){ +bool setCpuFrequency(void) +{ + FILE *pFile = fopen(CONFIG_FILE, "r"); + if (pFile == NULL) + { LogFile.WriteToFile(ESP_LOG_WARN, TAG, "No ConfigFile defined - exit setCpuFrequency()!"); return false; } - std::vector splitted; - std::string line = ""; - bool disabledLine = false; - bool eof = false; - - /* Load config from config file */ - while ((!configFile.GetNextParagraph(line, disabledLine, eof) || - (line.compare("[System]") != 0)) && !eof) {} - - if (eof || disabledLine) { - return false; - } - - while (configFile.getNextLine(&line, disabledLine, eof) && - !configFile.isNewParagraph(line)) { - splitted = ZerlegeZeile(line); - - if (toUpper(splitted[0]) == "CPUFREQUENCY") { - if (splitted.size() < 2) { - cpuFrequency = "160"; - } - else { - cpuFrequency = splitted[1]; - } + ClassFlow classFlow; + std::string aktparamgraph = ""; + while (classFlow.GetNextParagraph(pFile, aktparamgraph)) + { + if ((to_upper(aktparamgraph).compare("[SYSTEM]") == 0) || (to_upper(aktparamgraph).compare(";[SYSTEM]") == 0)) + { break; } } - if (esp_pm_get_configuration(&pm_config) != ESP_OK) { + if ((to_upper(aktparamgraph).compare("[SYSTEM]") != 0) && (to_upper(aktparamgraph).compare(";[SYSTEM]") != 0)) + { + fclose(pFile); + return false; + } + + string cpuFrequency = "160"; + esp_pm_config_t pm_config; + + std::vector splitted; + + while (classFlow.getNextLine(pFile, &aktparamgraph) && !classFlow.isNewParagraph(aktparamgraph)) + { + splitted = split_line(aktparamgraph); + + if (splitted.size() > 1) + { + std::string _param = to_upper(splitted[0]); + + if (_param == "CPUFREQUENCY") + { + cpuFrequency = splitted[1]; + } + } + } + fclose(pFile); + + if (esp_pm_get_configuration(&pm_config) != ESP_OK) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to read CPU Frequency!"); return false; } - if (cpuFrequency == "160") { // 160 is the default + if (cpuFrequency == "160") + { + // 160 is the default // No change needed } - else if (cpuFrequency == "240") { + else if (cpuFrequency == "240") + { pm_config.max_freq_mhz = 240; pm_config.min_freq_mhz = pm_config.max_freq_mhz; - if (esp_pm_configure(&pm_config) != ESP_OK) { + + if (esp_pm_configure(&pm_config) != ESP_OK) + { LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to set new CPU frequency!"); return false; } } - else { - LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Unknown CPU frequency: " + cpuFrequency + "! " - "It must be 160 or 240!"); + else + { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Unknown CPU frequency: " + cpuFrequency + "! It must be 160 or 240!"); return false; } - if (esp_pm_get_configuration(&pm_config) == ESP_OK) { + if (esp_pm_get_configuration(&pm_config) == ESP_OK) + { LogFile.WriteToFile(ESP_LOG_INFO, TAG, string("CPU frequency: ") + to_string(pm_config.max_freq_mhz) + " MHz"); } diff --git a/code/main/version.h b/code/main/version.h index 405defdf..abea7563 100644 --- a/code/main/version.h +++ b/code/main/version.h @@ -55,7 +55,7 @@ std::string getFwVersion(void) { std::string getHTMLversion(void){ char buf[100]="?\0"; FILE* pFile; - string fn = FormatFileName("/sdcard/html/version.txt"); + string fn = format_filename("/sdcard/html/version.txt"); pFile = fopen(fn.c_str(), "r"); if (pFile == NULL) @@ -73,7 +73,7 @@ std::string getHTMLversion(void){ std::string getHTMLcommit(void){ char buf[100]="?\0"; FILE* pFile; - string fn = FormatFileName("/sdcard/html/version.txt"); + string fn = format_filename("/sdcard/html/version.txt"); pFile = fopen(fn.c_str(), "r"); if (pFile == NULL) diff --git a/code/partitions.csv b/code/partitions.csv deleted file mode 100644 index b0c6981b..00000000 --- a/code/partitions.csv +++ /dev/null @@ -1,8 +0,0 @@ -# Name, Type, SubType, Offset, Size, Flags -# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap -nvs, data, nvs, , 0x4000, -otadata, data, ota, , 0x2000, -phy_init, data, phy, , 0x1000, -# factory, app, factory, , 1600k, -ota_0, app, ota_0, , 1900k, -ota_1, app, ota_1, , 1900k, \ No newline at end of file diff --git a/code/partitions_custom_16mb.csv b/code/partitions_custom_16mb.csv new file mode 100644 index 00000000..39e914e1 --- /dev/null +++ b/code/partitions_custom_16mb.csv @@ -0,0 +1,9 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x6000, +otadata, data, ota, , 0x2000, +phy_init, data, phy, , 0x1000, +ota_0, app, ota_0, , 0x3E0000, +ota_1, app, ota_1, , 0x3E0000, +www, data, littlefs, , 0x5E0000, +nvs_key, data, nvs_keys, , 0x1000, +coredump, data, coredump, , 192k, diff --git a/code/partitions_custom_4mb.csv b/code/partitions_custom_4mb.csv new file mode 100644 index 00000000..460c7ff5 --- /dev/null +++ b/code/partitions_custom_4mb.csv @@ -0,0 +1,8 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x6000, +otadata, data, ota, , 0x2000, +phy_init, data, phy, , 0x1000, +ota_0, app, ota_0, , 0x1E0000, +ota_1, app, ota_1, , 0x1E0000, +nvs_key, data, nvs_keys, , 0x1000, +coredump, data, coredump, , 0x1E000, diff --git a/code/partitions_custom_8mb.csv b/code/partitions_custom_8mb.csv new file mode 100644 index 00000000..557ab0cc --- /dev/null +++ b/code/partitions_custom_8mb.csv @@ -0,0 +1,9 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x6000, +otadata, data, ota, , 0x2000, +phy_init, data, phy, , 0x1000, +ota_0, app, ota_0, , 0x1E0000, +ota_1, app, ota_1, , 0x1E0000, +www, data, littlefs, , 0x2E0000, +nvs_key, data, nvs_keys, , 0x1000, +coredump, data, coredump, , 192k, diff --git a/code/platformio.ini b/code/platformio.ini index d622f420..a1941a1e 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -15,12 +15,14 @@ [common:idf] build_flags = -DUSE_ESP_IDF - lib_deps = + lib_deps = + #https://github.com/joltwallet/esp_littlefs.git + #https://github.com/espressif/esp-modbus.git [common:esp32-idf] extends = common:idf ; PlatformIO releases, see https://github.com/platformio/platform-espressif32/releases - platform = platformio/espressif32 @ 6.9.0 + platform = platformio/espressif32 @ 6.11.0 framework = espidf lib_deps = ${common:idf.lib_deps} @@ -32,6 +34,7 @@ [flags:runtime] build_flags = + -Wno-missing-field-initializers -Wno-nonnull-compare -Wno-sign-compare -Wno-unused-but-set-variable @@ -47,7 +50,10 @@ -fno-exceptions +;############################################################################# ; The main env - default +; Board: ESP32CAM_AITHINKER +;############################################################################# [env:esp32cam] extends = common:esp32-idf board = esp32cam @@ -58,167 +64,119 @@ build_flags = ${flags:runtime.build_flags} ; ### Sofware options : (can be set in defines.h) -D BOARD_ESP32CAM_AITHINKER - -D ENABLE_MQTT + -D BOARD_HAS_PSRAM ;-D MQTT_PROTOCOL_311 - -D MQTT_ENABLE_SSL + ;-D MQTT_ENABLE_SSL ;-D MQTT_ENABLE_WS ;-D MQTT_ENABLE_WSS - -D MQTT_SUPPORTED_FEATURE_SKIP_CRT_CMN_NAME_CHECK + ;-D MQTT_SUPPORTED_FEATURE_SKIP_CRT_CMN_NAME_CHECK ;-D MQTT_SUPPORTED_FEATURE_CRT_CMN_NAME ;-D MQTT_SUPPORTED_FEATURE_CLIENT_KEY_PASSWORD - -D ENABLE_INFLUXDB - -D ENABLE_WEBHOOK - -D ENABLE_SOFTAP -board_build.partitions = partitions.csv +board_build.partitions = partitions_custom_4mb.csv monitor_speed = 115200 -; full standalone dev mode -; As sample, the board is nod32s instead of esp32cam (do not change nothing in fact :) -; You can test newer platform_packages -; or flash mode (board_build.flash_mode = qio) -[env:esp32cam-dev] +;############################################################################# +; Board: WROVER_KIT_ESP32 +;############################################################################# +[env:wrover-kit-esp32] extends = common:esp32-idf -board = esp32cam ; node32s -;board_build.flash_mode = qio ;generate SPI_FAST_FLASH_BOOT boot loop +board = esp32cam +framework = espidf build_flags = ; ### common imported : ${common:esp32-idf.build_flags} - ${flags:clangtidy.build_flags} + ${flags:runtime.build_flags} ; ### Sofware options : (can be set in defines.h) - -D BOARD_ESP32CAM_AITHINKER - -D ENABLE_MQTT - -D ENABLE_INFLUXDB - -D ENABLE_WEBHOOK - ;-D ENABLE_SOFTAP - ; ### Debug options : - ;-D DEBUG_DETAIL_ON - ;-D DEBUG_DISABLE_BROWNOUT_DETECTOR - ;-D DEBUG_ENABLE_SYSINFO - ;-D DEBUG_ENABLE_PERFMON - ;-D DEBUG_HIMEM_MEMORY_CHECK - ;### test options - -D CONFIG_ESP_TASK_WDT - ;-D CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL - -D CONFIG_ESP_TASK_WDT_TIMEOUT_S ; fix for CONFIG_ESP_INT_WDT_TIMEOUT_MS - ;-D USE_HIMEM_IF_AVAILABLE -framework = espidf -lib_ldf_mode = deep+ -platform = platformio/espressif32 @ 5.2.0 -platform_packages = - ;platformio/framework-espidf @ 3.40402.0 (4.4.2) - ;platformio/framework-espidf@^3.50000.0 - ;platformio/tool-cmake @ 3.16.4 - ;platformio/tool-cmake@^3.21.3 - ;platformio/tool-esptoolpy @ 1.40201.0 (4.2.1) -;platformio/tool-esptoolpy@^1.40400.0 - ;platformio/tool-idf @ 1.0.1 - ;platformio/tool-mconf @ 1.4060000.20190628 (406.0.0) - ;platformio/tool-ninja @ 1.9.0 - ;platformio/tool-ninja @ 1.10.2 - ;platformio/toolchain-esp32ulp @ 1.22851.191205 (2.28.51) -;espressif/toolchain-esp32ulp @ 2.35.0-20220830 - ;platformio/toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5 - ;platformio/toolchain-xtensa-esp32 @ 11.2.0+2022r1 - -; platformio/espressif32 @ 5.3.0 dependencies : - ;platformio/framework-espidf @ 3.40403.0 - ;platformio/tool-cmake @ 3.16.4 - ;platformio/tool-esptoolpy@^1.40400.0 - ;platformio/tool-idf @ 1.0.1 - ;platformio/tool-mconf @ 1.4060000.20190628 - ;platformio/tool-ninja @ 1.9.0 - ;espressif/toolchain-esp32ulp @ 2.35.0-20220830 - ;;;;espressif/toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5 -board_build.partitions = partitions.csv + -D BOARD_WROVER_KIT_ESP32 + -D BOARD_HAS_PSRAM + ;-D MQTT_PROTOCOL_311 + ;-D MQTT_ENABLE_SSL + ;-D MQTT_ENABLE_WS + ;-D MQTT_ENABLE_WSS + ;-D MQTT_SUPPORTED_FEATURE_SKIP_CRT_CMN_NAME_CHECK + ;-D MQTT_SUPPORTED_FEATURE_CRT_CMN_NAME + ;-D MQTT_SUPPORTED_FEATURE_CLIENT_KEY_PASSWORD +board_build.partitions = partitions_custom_4mb.csv monitor_speed = 115200 -; Activate all debug mode -; Cannot be used alone, but must be added at the end of extends = env:esp32cam-dev, esp32cam-debug -;If multiple items specified in the extends field then only values from the latter one will be used in the final configuration -;https://docs.platformio.org/en/stable/projectconf/section_env_advanced.html -[env:esp32cam-debug] ; activate all debug -;extends nothing, only apply sdkconfig.esp32-debug.defaults, enable debug options and clangtidy -build_flags = - ; ### clangtidy build flags: - ${flags:clangtidy.build_flags} - ; ### Debug options : - -D DEBUG_DETAIL_ON - ;-D DEBUG_DISABLE_BROWNOUT_DETECTOR - -D DEBUG_ENABLE_SYSINFO - -D DEBUG_ENABLE_PERFMON - ;-D DEBUG_HIMEM_MEMORY_CHECK - ;-D USE_HIMEM_IF_AVAILABLE -lib_ldf_mode = deep+ - -; Power management enabled -;https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/power_management.html -;https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html#config-pm-enable -[env:esp32cam-power-management] -build_flags = - -D TCONFIG_PM_ENABLE - -D CONFIG_PM_DFS_INIT_AUTO - -D CONFIG_FREERTOS_USE_TICKLESS_IDLE - ;-D FREERTOS_IDLE_TIME_BEFORE_SLEEP=3 - -;********************** -; next section use modified version CMakeLists.txt of to use sdkconfig..defaults + sdkconfig.defaults -; https://github.com/platformio/platform-espressif32/issues/638 - - -; set board to rev3 -[env:esp32cam-board-rev3] -extends = env:esp32cam-dev, esp32cam-debug - -; set CPU frequency to 240 instead of 160 default -[env:esp32cam-cpu-freq-240] -extends = env:esp32cam-dev, esp32cam-debug -; sdkconfig.esp32cam-board-rev3.defaults override some sdkconfig.defaults - -; set board to rev3 + CPU frequency to 240 -; look at the extends : it takes esp32cam-dev and add env:esp32cam-board-rev3, env:esp32cam-cpu-freq-240 , esp32cam-debug parameters -[env:esp32cam-board-rev3-cpu-freq-240] -extends = env:esp32cam-dev, env:esp32cam-board-rev3, env:esp32cam-cpu-freq-240 , esp32cam-debug - -; set board to rev3 + CPU frequency to 240 + power management -[env:esp32cam-board-rev3-cpu-freq-240-pow] -extends = env:esp32cam-dev, env:esp32cam-board-rev3, env:esp32cam-cpu-freq-240 , env:esp32cam-power-management, esp32cam-debug - -; Enable use of 8 MB PSRAM boards -;https://github.com/espressif/esp-idf/blob/master/examples/system/himem/README.md -[env:esp32cam-dev-himem] -extends = env:esp32cam-dev, esp32cam-debug -; sdkconfig.esp32cam-dev-himem.defaults override some sdkconfig.defaults -build_flags = - -DBOARD_HAS_PSRAM - ;-D DEBUG_HIMEM_MEMORY_CHECK - ;-D USE_HIMEM_IF_AVAILABLE - -; set options for task analysis (PR #1751) -[env:esp32cam-dev-task-analysis] -extends = env:esp32cam-dev, esp32cam-debug -; sdkconfig.esp32cam-dev-task-analysis.defaults override some sdkconfig.defaults -build_flags = - ;-D DEBUG_DETAIL_ON ; if esp32cam-debug not in extends - -D TASK_ANALYSIS_ON - ;please use only one HEAP tracing at time. - -D HEAP_TRACING_MAIN_WIFI - ;-D HEAP_TRACING_MAIN_START - ;-D HEAP_TRACING_CLASS_FLOW_CNN_GENERAL_DO_ALING_AND_CUT - - -; Overwrite espcam build_flags to not include ENABLE_SOFTAP -; Nor the -U ENABLE_SOFTAP nor -D ENABLE_SOFTAP=0 works to unset defines actually -; Set CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n in sdkconfig.esp32cam-no-softap.defaults to disable softap in the esp-idf compilation -[env:esp32cam-no-softap] ;CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n saves 28k of flash -extends = env:esp32cam -build_flags = - ; ### common imported : +;############################################################################# +; Board: FREENOVE ESP32S3-WROOM-1-N16R8 (and compatible) +;############################################################################# +[env:freenove-esp32s3] +extends = common:esp32-idf +board = 4d_systems_esp32s3_gen4_r8n16 +framework = espidf +build_flags = + ; ### common imported : ${common:esp32-idf.build_flags} - ${flags:clangtidy.build_flags} - ; ### Sofware options : - -D ENABLE_MQTT - -D ENABLE_INFLUXDB - -D ENABLE_WEBHOOK - ;-D ENABLE_SOFTAP ; disabled + ${flags:runtime.build_flags} + ; ### Sofware options : (can be set in defines.h) + -D BOARD_FREENOVE_ESP32S3 + ;-D BOARD_SUPPORTS_LITTLEFS + -D BOARD_HAS_PSRAM + ;-D MQTT_PROTOCOL_311 + ;-D MQTT_ENABLE_SSL + ;-D MQTT_ENABLE_WS + ;-D MQTT_ENABLE_WSS + ;-D MQTT_SUPPORTED_FEATURE_SKIP_CRT_CMN_NAME_CHECK + ;-D MQTT_SUPPORTED_FEATURE_CRT_CMN_NAME + ;-D MQTT_SUPPORTED_FEATURE_CLIENT_KEY_PASSWORD +board_build.filesystem = littlefs +board_build.partitions = partitions_custom_16mb.csv +monitor_speed = 115200 + + +;############################################################################# +; Board: BOARD_ESP32_S3_ETH_V1 ESP32S3-WROOM-1-N16R8 (and compatible) +;############################################################################# +[env:esp32s3-eth-v1] +extends = common:esp32-idf +board = 4d_systems_esp32s3_gen4_r8n16 +framework = espidf +build_flags = + ; ### common imported : + ${common:esp32-idf.build_flags} + ${flags:runtime.build_flags} + ; ### Sofware options : (can be set in defines.h) + -D BOARD_ESP32_S3_ETH_V1 + ;-D BOARD_SUPPORTS_LITTLEFS + -D BOARD_HAS_PSRAM + ;-D MQTT_PROTOCOL_311 + ;-D MQTT_ENABLE_SSL + ;-D MQTT_ENABLE_WS + ;-D MQTT_ENABLE_WSS + ;-D MQTT_SUPPORTED_FEATURE_SKIP_CRT_CMN_NAME_CHECK + ;-D MQTT_SUPPORTED_FEATURE_CRT_CMN_NAME + ;-D MQTT_SUPPORTED_FEATURE_CLIENT_KEY_PASSWORD +board_build.filesystem = littlefs +board_build.partitions = partitions_custom_16mb.csv +monitor_speed = 115200 + + +;############################################################################# +; Board: BOARD_ESP32_S3_ETH_V2 ESP32S3-WROOM-1-N16R8 (and compatible) +;############################################################################# +[env:esp32s3-eth-v2] +extends = common:esp32-idf +board = 4d_systems_esp32s3_gen4_r8n16 +framework = espidf +build_flags = + ; ### common imported : + ${common:esp32-idf.build_flags} + ${flags:runtime.build_flags} + ; ### Sofware options : (can be set in defines.h) + -D BOARD_ESP32_S3_ETH_V2 + ;-D BOARD_SUPPORTS_LITTLEFS + -D BOARD_HAS_PSRAM + ;-D MQTT_PROTOCOL_311 + ;-D MQTT_ENABLE_SSL + ;-D MQTT_ENABLE_WS + ;-D MQTT_ENABLE_WSS + ;-D MQTT_SUPPORTED_FEATURE_SKIP_CRT_CMN_NAME_CHECK + ;-D MQTT_SUPPORTED_FEATURE_CRT_CMN_NAME + ;-D MQTT_SUPPORTED_FEATURE_CLIENT_KEY_PASSWORD +board_build.filesystem = littlefs +board_build.partitions = partitions_custom_16mb.csv +monitor_speed = 115200 diff --git a/code/sdkconfig.defaults b/code/sdkconfig.defaults index e62a5470..2664a28f 100644 --- a/code/sdkconfig.defaults +++ b/code/sdkconfig.defaults @@ -18,6 +18,9 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y ###### safe optimizations CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +#CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y +#CONFIG-BOOTLOADER-APP-ANTI-ROLLBACK=y +#CONFIG_APP_ROLLBACK_ENABLE=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y #disable bootloader logging @@ -60,7 +63,6 @@ CONFIG_LWIP_IPV6=n #Newlib format CONFIG_NEWLIB_NANO_FORMAT=y - # ESP-NN # # CONFIG_NN_ANSI_C is not set @@ -68,57 +70,63 @@ CONFIG_NN_OPTIMIZED=y CONFIG_NN_OPTIMIZATIONS=1 # end of ESP-NN -# ESP HTTP client -# -# CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS is not set - ###### end safe optimizations -CONFIG_ESP32_REV_MIN_0=y - CONFIG_ESP32_DPORT_WORKAROUND=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_ESPTOOLPY_FLASHSIZE="4MB" -CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y - -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" -CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" -CONFIG_PARTITION_TABLE_OFFSET=0x8000 -CONFIG_PARTITION_TABLE_MD5=y - +# +# ESP-SPIRAM +# CONFIG_ESP32_SPIRAM_SUPPORT=y - -CONFIG_SPIRAM_SIZE=-1 -CONFIG_SPIRAM_SPEED_40M=y -CONFIG_SPIRAM=y CONFIG_SPIRAM_BOOT_INIT=y CONFIG_SPIRAM_USE_MALLOC=y -#CONFIG_SPIRAM_USE_MEMMAP=y => Does not work: "cam_dma_config(306): frame buffer malloc failed" CONFIG_SPIRAM_MEMTEST=y CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384 -CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=40960 +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=131072 CONFIG_SPIRAM_CACHE_WORKAROUND=y CONFIG_SPIRAM_IGNORE_NOTFOUND=y - +CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 +CONFIG_FMB_TIMER_PORT_ENABLED=y -CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 +# +# HTTP Server +# +CONFIG_HTTPD_MAX_REQ_HDR_LEN=2048 +CONFIG_HTTPD_MAX_URI_LEN=512 +# CONFIG_HTTPD_ERR_RESP_NO_DELAY=y CONFIG_HTTPD_PURGE_BUF_LEN=16 +# CONFIG_HTTPD_LOG_PURGE_DATA is not set +# CONFIG_HTTPD_WS_SUPPORT=y +# CONFIG_HTTPD_QUEUE_WORK_BLOCKING is not set +# end of HTTP Server +# +# Wi-Fi +# CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=16 CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=16 +#only necessary for WIFI mesh roaming (include/defines.h -> WLAN_USE_MESH_ROAMING) +#CONFIG_WPA_11KV_SUPPORT=y +#CONFIG_WPA_SCAN_CACHE=n +#CONFIG_WPA_MBO_SUPPORT=n +#CONFIG_WPA_11R_SUPPORT=n // Will be supported with ESP-IDF v5.0 +#CONFIG_WPA_DEBUG_PRINT=n + +# +# ESP-FATFS +# CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_MAX_LFN=255 CONFIG_FATFS_API_ENCODING_ANSI_OEM=y -CONFIG_FMB_TIMER_PORT_ENABLED=y - +# +# ESP-MQTT +# CONFIG_MQTT_MSG_ID_INCREMENTAL=y CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED=y CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED=y @@ -153,39 +161,32 @@ CONFIG_RMT_RECV_FUNC_IN_IRAM=y # CONFIG_RMT_ENABLE_DEBUG_LOG is not set # end of ESP-Driver:RMT Configurations -CONFIG_CAMERA_CORE0=n -CONFIG_CAMERA_CORE1=y -CONFIG_OV7670_SUPPORT=n -CONFIG_OV7725_SUPPORT=n -CONFIG_NT99141_SUPPORT=n -CONFIG_OV3660_SUPPORT=y -CONFIG_OV2640_SUPPORT=y -CONFIG_OV5640_SUPPORT=y -CONFIG_GC2145_SUPPORT=n -CONFIG_GC032A_SUPPORT=n -CONFIG_GC0308_SUPPORT=n -CONFIG_BF3005_SUPPORT=n - -CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=4864 - -#only necessary for WIFI mesh roaming (include/defines.h -> WLAN_USE_MESH_ROAMING) -#CONFIG_WPA_11KV_SUPPORT=y -#CONFIG_WPA_SCAN_CACHE=n -#CONFIG_WPA_MBO_SUPPORT=n -#CONFIG_WPA_11R_SUPPORT=n // Will be supported with ESP-IDF v5.0 -#CONFIG_WPA_DEBUG_PRINT=n - -#only necessary for task analysis (include/defines.h -> TASK_ANALYSIS_ON) -#set in [env:esp32cam-dev-task-analysis] +# +# Kernel +# +# CONFIG_FREERTOS_SMP is not set +# CONFIG_FREERTOS_UNICORE is not set +CONFIG_FREERTOS_HZ=1000 #CONFIG_FREERTOS_USE_TRACE_FACILITY=1 #CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y #CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=n -#force disable HIMEM as not used in default config, can be enabled with [env:esp32cam-dev-himem] -#free 256kb of internal memory : -#I (2112) esp_himem: Initialized. Using last 8 32KB address blocks for bank switching on 4352 KB of physical memory. -CONFIG_SPIRAM_BANKSWITCH_ENABLE=n -#CONFIG_SPIRAM_BANKSWITCH_RESERVE is not set +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=4864 CONFIG_PM_ENABLE=y + +# MAC Config +# +CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_STA=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_AP=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_BT=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_ETH=y +CONFIG_ESP_MAC_UNIVERSAL_MAC_ADDRESSES_FOUR=y +CONFIG_ESP_MAC_UNIVERSAL_MAC_ADDRESSES=4 +# CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_TWO is not set +CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_FOUR=y +CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES=4 +CONFIG_ESP_MAC_IGNORE_MAC_CRC_ERROR=y +#CONFIG_ESP_MAC_USE_CUSTOM_MAC_AS_BASE_MAC=y +# end of MAC Config diff --git a/code/sdkconfig.esp32cam-board-rev3.defaults b/code/sdkconfig.esp32cam-board-rev3.defaults deleted file mode 100644 index dc70635c..00000000 --- a/code/sdkconfig.esp32cam-board-rev3.defaults +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG_ESP32_REV_MIN_0=n - -ESP32_REV_MIN_3=y -CONFIG_ESP32_REV_MIN_3=y - -#https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/kconfig.html#config-spiram-cache-workaround -CONFIG_ESP32_DPORT_WORKAROUND=n \ No newline at end of file diff --git a/code/sdkconfig.esp32cam-cpu-freq-240.defaults b/code/sdkconfig.esp32cam-cpu-freq-240.defaults deleted file mode 100644 index 6ea8e9c4..00000000 --- a/code/sdkconfig.esp32cam-cpu-freq-240.defaults +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_HZ=1000 -CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y -CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 diff --git a/code/sdkconfig.esp32cam-debug.defaults b/code/sdkconfig.esp32cam-debug.defaults deleted file mode 100644 index 10fcbb1e..00000000 --- a/code/sdkconfig.esp32cam-debug.defaults +++ /dev/null @@ -1,33 +0,0 @@ -#enable bootloader logging -CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=n -CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 -CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=n -CONFIG_FREERTOS_ASSERT_DISABLE=yn -CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=n -#CONFIG_LOG_DEFAULT_LEVEL_NONE=y -#CONFIG_LOG_DEFAULT_LEVEL=0 -CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y -CONFIG_LWIP_ESP_LWIP_ASSERT=n -CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED=n -CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 -# CONFIG_LOG_COLORS is not set - -#set default loggin to -CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y -# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set -# CONFIG_BOOTLOADER_LOG_LEVEL_INFO is not set -# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set -# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set -CONFIG_BOOTLOADER_LOG_LEVEL=2 - -#disable lookup function -CONFIG_ESP_ERR_TO_NAME_LOOKUP=y -# CONFIG_ESP_ERR_TO_NAME_LOOKUP is not set - -#no panic message -ESP_SYSTEM_PANIC_SILENT_REBOOT=n - -#disable ADC calibration (needed for external sensors) -CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y -CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y -CONFIG_ADC_CAL_LUT_ENABLE=needed \ No newline at end of file diff --git a/code/sdkconfig.esp32cam-dev-himem.defaults b/code/sdkconfig.esp32cam-dev-himem.defaults deleted file mode 100644 index 36ec86c9..00000000 --- a/code/sdkconfig.esp32cam-dev-himem.defaults +++ /dev/null @@ -1,34 +0,0 @@ -#### https://github.com/espressif/esp-idf/blob/master/examples/system/himem/sdkconfig.defaults -CONFIG_SPIRAM=y -CONFIG_SPIRAM_SUPPORT=y -CONFIG_SPIRAM_BOOT_INIT=y -CONFIG_SPIRAM_IGNORE_NOTFOUND=n -CONFIG_SPIRAM_USE_MALLOC=y -CONFIG_SPIRAM_TYPE_AUTO=y -CONFIG_SPIRAM_SIZE=-1 -CONFIG_SPIRAM_SPEED_40M=y -CONFIG_SPIRAM_MEMTEST=y -CONFIG_SPIRAM_CACHE_WORKAROUND=y -CONFIG_SPIRAM_BANKSWITCH_ENABLE=y -CONFIG_SPIRAM_BANKSWITCH_RESERVE=4 - -CONFIG_SPIRAM_USE_CAPS_ALLOC=y - -/* some references for testing - -# https://github.com/platformio/platform-espressif32/issues/185 - -#define CONFIG_SPIRAM_SUPPORT 1 -#define CONFIG_PICO_PSRAM_CS_IO 10 -#define CONFIG_ESP32_SPIRAM_SUPPORT 1 -#define CONFIG_SPIRAM_BOOT_INIT 1 -#define CONFIG_SPIRAM_IGNORE_NOTFOUND 0 -#define CONFIG_SPIRAM_TYPE_AUTO 1 -#define CONFIG_SPIRAM_SIZE -1 -#define CONFIG_SPIRAM_USE_MEMMAP 1 -#define CONFIG_SPIRAM_SPEED_40M 1 -#define SPIRAM_USE_MALLOC 1 -#define CONFIG_SPIRAM_CACHE_WORKAROUND 1 -#define CONFIG_SPIRAM_BANKSWITCH_ENABLE 1 -#define CONFIG_SPIRAM_BANKSWITCH_RESERVE 8 -*/ \ No newline at end of file diff --git a/code/sdkconfig.esp32cam-dev-task-analysis.defaults b/code/sdkconfig.esp32cam-dev-task-analysis.defaults deleted file mode 100644 index da1afdc4..00000000 --- a/code/sdkconfig.esp32cam-dev-task-analysis.defaults +++ /dev/null @@ -1,18 +0,0 @@ -CONFIG_FREERTOS_USE_TRACE_FACILITY=1 -CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y -CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y - -CONFIG_HEAP_TRACING_STANDALONE=y -CONFIG_HEAP_POISONING_LIGHT=y -CONFIG_HEAP_TASK_TRACKING=y - - -# General options for additional checks -CONFIG_HEAP_POISONING_COMPREHENSIVE=y -CONFIG_COMPILER_WARN_WRITE_STRINGS=y -CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y -CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y -CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y -CONFIG_COMPILER_STACK_CHECK=y - -CONFIG_ESP_TASK_WDT=n diff --git a/code/sdkconfig.esp32cam-dev.defaults b/code/sdkconfig.esp32cam-dev.defaults deleted file mode 100644 index 52477e48..00000000 --- a/code/sdkconfig.esp32cam-dev.defaults +++ /dev/null @@ -1,36 +0,0 @@ -### nothing actually - - -/* #disabled - -#### recommended optimizations to test -#https://docs.espressif.com/projects/esp-at/en/latest/esp32/Compile_and_Develop/How_to_optimize_throughput.html - -# System -CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096 -CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_HZ=1000 -#CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y -#CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 -#CONFIG_ESPTOOLPY_FLASHMODE_QIO=y -#CONFIG_ESPTOOLPY_FLASHFREQ_80M=y - -# LWIP -CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65534 -CONFIG_LWIP_TCP_WND_DEFAULT=65534 -CONFIG_LWIP_TCP_RECVMBOX_SIZE=12 -CONFIG_LWIP_UDP_RECVMBOX_SIZE=12 -CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64 - -# Wi-Fi -#CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=16 -#CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=64 -#CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=64 -#CONFIG_ESP32_WIFI_TX_BA_WIN=32 -#CONFIG_ESP32_WIFI_RX_BA_WIN=32 -#CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y -#CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y - -## end of recommended optimizations - -*/ diff --git a/code/sdkconfig.esp32cam.defaults b/code/sdkconfig.esp32cam.defaults new file mode 100644 index 00000000..918cd5ae --- /dev/null +++ b/code/sdkconfig.esp32cam.defaults @@ -0,0 +1,175 @@ +################################################## +# Application and board specific configuration +# Edit this file instead of sdkconfig.{board_type}! +# After editing make sure to explicitly delete +# sdkconfig.{board_type} to apply your changes! +################################################## + +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET_ARCH="xtensa" +CONFIG_IDF_TARGET_ESP32=y +CONFIG_IDF_TARGET="esp32" + +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT=y +# CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +CONFIG_ESPTOOLPY_FLASHMODE="qio" +# CONFIG_ESPTOOLPY_FLASHMODE="dout" +# CONFIG_ESPTOOLPY_FLASHMODE="dio" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M_DEFAULT=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE="8MB" +# CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE="16MB" +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y +CONFIG_ESP_ROM_HAS_SPI_FLASH=y +CONFIG_SOC_SPI_FLASH_SUPPORTED=y +CONFIG_SOC_SPIRAM_SUPPORTED=y +CONFIG_ESP32_SPIRAM_SUPPORT=y + +# +# ESP PSRAM +# +CONFIG_SPIRAM=y + +# +# ESP-Driver:SPI Configurations +# +CONFIG_SPI_MASTER_IN_IRAM=y +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +CONFIG_SPI_SLAVE_IN_IRAM=y +CONFIG_SPI_SLAVE_ISR_IN_IRAM=y +# end of ESP-Driver:SPI Configurations + +# +# SPI RAM config +# +CONFIG_SPIRAM_MODE_QUAD=y +CONFIG_SPIRAM_TYPE_AUTO=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_SPEED=80 +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384 +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=131072 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y + +# +# Partition Table +# +# CONFIG_PARTITION_TABLE_SINGLE_APP is not set +# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set +# CONFIG_PARTITION_TABLE_TWO_OTA=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_custom_4mb.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_custom_4mb.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y + +# +# ESP-TLS +# +# CONFIG_ESP_TLS_USING_MBEDTLS=y +# CONFIG_ESP_TLS_USE_DS_PERIPHERAL=n +# CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK is not set +# CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set +# CONFIG_ESP_TLS_PSK_VERIFICATION is not set +# CONFIG_ESP_TLS_INSECURE=y +# CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y +# end of ESP-TLS + +# +# ESP-Driver:LEDC Configurations +# +CONFIG_LEDC_CTRL_FUNC_IN_IRAM=y +# end of ESP-Driver:LEDC Configurations + +# +# Legacy RMT Driver Configurations +# +CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y +# end of Legacy RMT Driver Configurations + +# +# ESP-Driver:RMT Configurations +# +CONFIG_RMT_ISR_IRAM_SAFE=y +CONFIG_RMT_RECV_FUNC_IN_IRAM=y +# CONFIG_RMT_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:RMT Configurations + +# +# Camera +# +CONFIG_OV7670_SUPPORT=n +CONFIG_OV7725_SUPPORT=n +CONFIG_NT99141_SUPPORT=n +CONFIG_OV3660_SUPPORT=y +CONFIG_OV2640_SUPPORT=y +CONFIG_OV5640_SUPPORT=y +CONFIG_GC2145_SUPPORT=n +CONFIG_GC032A_SUPPORT=n +CONFIG_GC0308_SUPPORT=n +CONFIG_BF20A6_SUPPORT=n +CONFIG_BF3005_SUPPORT=n +CONFIG_SC030IOT_SUPPORT=n +CONFIG_MEGA_CCM_SUPPORT=n +CONFIG_HM1055_SUPPORT=n +CONFIG_HM0360_SUPPORT=n +# CONFIG_GC_SENSOR_WINDOWING_MODE=y +# CONFIG_GC_SENSOR_SUBSAMPLE_MODE=y +CONFIG_CAMERA_TASK_STACK_SIZE=2048 +# CONFIG_CAMERA_CORE0 is not set +CONFIG_CAMERA_CORE1=y +# CONFIG_CAMERA_NO_AFFINITY is not set +CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX=32768 +CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_AUTO=y +# CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_CUSTOM is not set +# end of Camera configuration + +# +# Modbus configuration +# +# CONFIG_FMB_COMM_MODE_TCP_EN=y +CONFIG_FMB_TCP_PORT_DEFAULT=502 +CONFIG_FMB_TCP_PORT_MAX_CONN=5 +CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20 +# CONFIG_FMB_TCP_UID_ENABLED is not set +CONFIG_FMB_COMM_MODE_RTU_EN=y +# CONFIG_FMB_COMM_MODE_ASCII_EN=y +CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000 +CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 +CONFIG_FMB_QUEUE_LENGTH=20 +CONFIG_FMB_PORT_TASK_STACK_SIZE=4096 +CONFIG_FMB_SERIAL_BUF_SIZE=256 +CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB=8 +CONFIG_FMB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS=0 +CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS=1000 +CONFIG_FMB_PORT_TASK_PRIO=10 +# CONFIG_FMB_PORT_TASK_AFFINITY_NO_AFFINITY is not set +# CONFIG_FMB_PORT_TASK_AFFINITY_CPU0=y +# CONFIG_FMB_PORT_TASK_AFFINITY_CPU1=y +CONFIG_FMB_PORT_TASK_AFFINITY=0x0 +CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT=y +CONFIG_FMB_CONTROLLER_SLAVE_ID=0x00112233 +CONFIG_FMB_CONTROLLER_SLAVE_ID_MAX_SIZE=32 +CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20 +CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 +CONFIG_FMB_CONTROLLER_STACK_SIZE=4096 +CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20 +CONFIG_FMB_TIMER_PORT_ENABLED=y +# CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD is not set +# CONFIG_FMB_EXT_TYPE_SUPPORT is not set +# end of Modbus configuration diff --git a/code/sdkconfig.esp32s3-eth-v1.defaults b/code/sdkconfig.esp32s3-eth-v1.defaults new file mode 100644 index 00000000..f1d8e1c7 --- /dev/null +++ b/code/sdkconfig.esp32s3-eth-v1.defaults @@ -0,0 +1,248 @@ +################################################## +# Application and board specific configuration +# Edit this file instead of sdkconfig.{board_type}! +# After editing make sure to explicitly delete +# sdkconfig.{board_type} to apply your changes! +################################################## + +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET_ARCH="xtensa" +CONFIG_IDF_TARGET_ESP32S3=y +CONFIG_IDF_TARGET="esp32s3" + +# +# SDMMC +# +CONFIG_SOC_SDMMC_USE_GPIO_MATRIX=y + +# Enable lwIP IRAM optimization +CONFIG_LWIP_IRAM_OPTIMIZATION=y + +# Enable Ethernet IRAM optimization +CONFIG_ETH_IRAM_OPTIMIZATION=y + +# +# Serial flasher config +# +# CONFIG_ESPTOOLPY_NO_STUB is not set +# CONFIG_ESPTOOLPY_OCT_FLASH is not set +CONFIG_ESPTOOLPY_FLASH_MODE_AUTO_DETECT=y +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_QOUT=y +# CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT=y +CONFIG_ESPTOOLPY_FLASHMODE="qio" +# CONFIG_ESPTOOLPY_FLASHFREQ_40M=y +# CONFIG_ESPTOOLPY_FLASHFREQ_40M_DEFAULT=y +# CONFIG_ESPTOOLPY_FLASHFREQ="40m" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M_DEFAULT=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +# CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE="4MB" +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE="8MB" +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="16MB" +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y + +CONFIG_SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_20M_SUPPORTED=y +CONFIG_SOC_SPIRAM_SUPPORTED=y + +# +# Partition Table +# +# CONFIG_PARTITION_TABLE_SINGLE_APP is not set +# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set +# CONFIG_PARTITION_TABLE_TWO_OTA=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_custom_16mb.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_custom_16mb.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y + +# +# ESP PSRAM +# +CONFIG_SPIRAM=y + +# +# SPI configuration +# +CONFIG_SPI_MASTER_IN_IRAM=y +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +CONFIG_SPI_SLAVE_IN_IRAM=y +CONFIG_SPI_SLAVE_ISR_IN_IRAM=y + +# +# SPI RAM config +# +# CONFIG_SPIRAM_MODE_QUAD is not set +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_TYPE_AUTO=y +CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y +# CONFIG_SPIRAM_SPEED_40M=y +# CONFIG_SPIRAM_SPEED=40 +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_SPEED=80 +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=131072 +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y + +CONFIG_ESP_ROM_HAS_SPI_FLASH=y +CONFIG_SOC_SPI_FLASH_SUPPORTED=y +CONFIG_ESP32S3_SPIRAM_SUPPORT=y +CONFIG_SOC_SPI_MEM_SUPPORT_OPI_MODE=y + +# +# Cache config +# +CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y +# CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE=0x4000 +CONFIG_ESP32S3_INSTRUCTION_CACHE_8WAYS=y +CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS=8 +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32 +CONFIG_ESP32S3_DATA_CACHE_64KB=y +# CONFIG_ESP32S3_DATA_CACHE_SIZE=0x8000 +CONFIG_ESP32S3_DATA_CACHE_8WAYS=y +CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8 +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=64 + +# +# ESP-TLS +# +# CONFIG_ESP_TLS_USING_MBEDTLS=y +# CONFIG_ESP_TLS_USE_DS_PERIPHERAL=n +# CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK is not set +# CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set +# CONFIG_ESP_TLS_PSK_VERIFICATION is not set +# CONFIG_ESP_TLS_INSECURE=y +# CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y +# end of ESP-TLS + +# +# Ethernet +# +CONFIG_ETH_ENABLED=y +CONFIG_ETH_USE_SPI_ETHERNET=y +# CONFIG_ETH_SPI_ETHERNET_DM9051 is not set +CONFIG_ETH_SPI_ETHERNET_W5500=y +# CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL is not set +# CONFIG_ETH_USE_OPENETH is not set +# CONFIG_ETH_TRANSMIT_MUTEX is not set +# end of Ethernet + +# +# USB Serial/JTAG +# +CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=n +CONFIG_USB_OTG_SUPPORTED=n + +# +# LEDC +# +CONFIG_LEDC_CTRL_FUNC_IN_IRAM=y +CONFIG_SOC_LEDC_SUPPORTED=y +CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y +CONFIG_SOC_LEDC_SUPPORT_XTAL_CLOCK=y +CONFIG_SOC_LEDC_CHANNEL_NUM=8 +CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=14 +CONFIG_SOC_LEDC_SUPPORT_FADE_STOP=y + +# +# RMT +# +CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y +CONFIG_RMT_ISR_IRAM_SAFE=y +CONFIG_RMT_RECV_FUNC_IN_IRAM=y +ONFIG_SOC_RMT_GROUPS=1 +CONFIG_SOC_RMT_TX_CANDIDATES_PER_GROUP=4 +CONFIG_SOC_RMT_RX_CANDIDATES_PER_GROUP=4 +CONFIG_SOC_RMT_CHANNELS_PER_GROUP=8 +CONFIG_SOC_RMT_MEM_WORDS_PER_CHANNEL=48 +CONFIG_SOC_RMT_SUPPORT_RX_PINGPONG=y +CONFIG_SOC_RMT_SUPPORT_RX_DEMODULATION=y +CONFIG_SOC_RMT_SUPPORT_TX_ASYNC_STOP=y +CONFIG_SOC_RMT_SUPPORT_TX_LOOP_COUNT=y +CONFIG_SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP=y +CONFIG_SOC_RMT_SUPPORT_TX_SYNCHRO=y +CONFIG_SOC_RMT_SUPPORT_TX_CARRIER_DATA_ONLY=y +CONFIG_SOC_RMT_SUPPORT_XTAL=y +CONFIG_SOC_RMT_SUPPORT_RC_FAST=y +CONFIG_SOC_RMT_SUPPORT_APB=y +CONFIG_SOC_RMT_SUPPORT_DMA=y + +# +# Camera +# +CONFIG_OV7670_SUPPORT=n +CONFIG_OV7725_SUPPORT=n +CONFIG_NT99141_SUPPORT=n +CONFIG_OV3660_SUPPORT=y +CONFIG_OV2640_SUPPORT=y +CONFIG_OV5640_SUPPORT=y +CONFIG_GC2145_SUPPORT=n +CONFIG_GC032A_SUPPORT=n +CONFIG_GC0308_SUPPORT=n +CONFIG_BF20A6_SUPPORT=n +CONFIG_BF3005_SUPPORT=n +CONFIG_SC030IOT_SUPPORT=n +CONFIG_MEGA_CCM_SUPPORT=n +CONFIG_HM1055_SUPPORT=n +CONFIG_HM0360_SUPPORT=n +# CONFIG_GC_SENSOR_WINDOWING_MODE=y +CONFIG_GC_SENSOR_SUBSAMPLE_MODE=y +CONFIG_CAMERA_TASK_STACK_SIZE=2048 +# CONFIG_CAMERA_CORE0 is not set +CONFIG_CAMERA_CORE1=y +# CONFIG_CAMERA_NO_AFFINITY is not set +CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX=32768 +CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_AUTO=y +# CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_CUSTOM is not set +# end of Camera configuration + +# +# Modbus configuration +# +# CONFIG_FMB_COMM_MODE_TCP_EN=y +CONFIG_FMB_TCP_PORT_DEFAULT=502 +CONFIG_FMB_TCP_PORT_MAX_CONN=5 +CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20 +# CONFIG_FMB_TCP_UID_ENABLED is not set +CONFIG_FMB_COMM_MODE_RTU_EN=y +# CONFIG_FMB_COMM_MODE_ASCII_EN=y +CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000 +CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 +CONFIG_FMB_QUEUE_LENGTH=20 +CONFIG_FMB_PORT_TASK_STACK_SIZE=4096 +CONFIG_FMB_SERIAL_BUF_SIZE=256 +CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB=8 +CONFIG_FMB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS=0 +CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS=1000 +CONFIG_FMB_PORT_TASK_PRIO=10 +# CONFIG_FMB_PORT_TASK_AFFINITY_NO_AFFINITY is not set +# CONFIG_FMB_PORT_TASK_AFFINITY_CPU0=y +# CONFIG_FMB_PORT_TASK_AFFINITY_CPU1=y +CONFIG_FMB_PORT_TASK_AFFINITY=0x0 +CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT=y +CONFIG_FMB_CONTROLLER_SLAVE_ID=0x00112233 +CONFIG_FMB_CONTROLLER_SLAVE_ID_MAX_SIZE=32 +CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20 +CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 +CONFIG_FMB_CONTROLLER_STACK_SIZE=4096 +CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20 +CONFIG_FMB_TIMER_PORT_ENABLED=y +# CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD is not set +# CONFIG_FMB_EXT_TYPE_SUPPORT is not set +# end of Modbus configuration diff --git a/code/sdkconfig.esp32s3-eth-v2.defaults b/code/sdkconfig.esp32s3-eth-v2.defaults new file mode 100644 index 00000000..f1d8e1c7 --- /dev/null +++ b/code/sdkconfig.esp32s3-eth-v2.defaults @@ -0,0 +1,248 @@ +################################################## +# Application and board specific configuration +# Edit this file instead of sdkconfig.{board_type}! +# After editing make sure to explicitly delete +# sdkconfig.{board_type} to apply your changes! +################################################## + +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET_ARCH="xtensa" +CONFIG_IDF_TARGET_ESP32S3=y +CONFIG_IDF_TARGET="esp32s3" + +# +# SDMMC +# +CONFIG_SOC_SDMMC_USE_GPIO_MATRIX=y + +# Enable lwIP IRAM optimization +CONFIG_LWIP_IRAM_OPTIMIZATION=y + +# Enable Ethernet IRAM optimization +CONFIG_ETH_IRAM_OPTIMIZATION=y + +# +# Serial flasher config +# +# CONFIG_ESPTOOLPY_NO_STUB is not set +# CONFIG_ESPTOOLPY_OCT_FLASH is not set +CONFIG_ESPTOOLPY_FLASH_MODE_AUTO_DETECT=y +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_QOUT=y +# CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT=y +CONFIG_ESPTOOLPY_FLASHMODE="qio" +# CONFIG_ESPTOOLPY_FLASHFREQ_40M=y +# CONFIG_ESPTOOLPY_FLASHFREQ_40M_DEFAULT=y +# CONFIG_ESPTOOLPY_FLASHFREQ="40m" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M_DEFAULT=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +# CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE="4MB" +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE="8MB" +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="16MB" +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y + +CONFIG_SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_20M_SUPPORTED=y +CONFIG_SOC_SPIRAM_SUPPORTED=y + +# +# Partition Table +# +# CONFIG_PARTITION_TABLE_SINGLE_APP is not set +# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set +# CONFIG_PARTITION_TABLE_TWO_OTA=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_custom_16mb.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_custom_16mb.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y + +# +# ESP PSRAM +# +CONFIG_SPIRAM=y + +# +# SPI configuration +# +CONFIG_SPI_MASTER_IN_IRAM=y +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +CONFIG_SPI_SLAVE_IN_IRAM=y +CONFIG_SPI_SLAVE_ISR_IN_IRAM=y + +# +# SPI RAM config +# +# CONFIG_SPIRAM_MODE_QUAD is not set +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_TYPE_AUTO=y +CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y +# CONFIG_SPIRAM_SPEED_40M=y +# CONFIG_SPIRAM_SPEED=40 +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_SPEED=80 +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=131072 +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y + +CONFIG_ESP_ROM_HAS_SPI_FLASH=y +CONFIG_SOC_SPI_FLASH_SUPPORTED=y +CONFIG_ESP32S3_SPIRAM_SUPPORT=y +CONFIG_SOC_SPI_MEM_SUPPORT_OPI_MODE=y + +# +# Cache config +# +CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y +# CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE=0x4000 +CONFIG_ESP32S3_INSTRUCTION_CACHE_8WAYS=y +CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS=8 +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32 +CONFIG_ESP32S3_DATA_CACHE_64KB=y +# CONFIG_ESP32S3_DATA_CACHE_SIZE=0x8000 +CONFIG_ESP32S3_DATA_CACHE_8WAYS=y +CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8 +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=64 + +# +# ESP-TLS +# +# CONFIG_ESP_TLS_USING_MBEDTLS=y +# CONFIG_ESP_TLS_USE_DS_PERIPHERAL=n +# CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK is not set +# CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set +# CONFIG_ESP_TLS_PSK_VERIFICATION is not set +# CONFIG_ESP_TLS_INSECURE=y +# CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y +# end of ESP-TLS + +# +# Ethernet +# +CONFIG_ETH_ENABLED=y +CONFIG_ETH_USE_SPI_ETHERNET=y +# CONFIG_ETH_SPI_ETHERNET_DM9051 is not set +CONFIG_ETH_SPI_ETHERNET_W5500=y +# CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL is not set +# CONFIG_ETH_USE_OPENETH is not set +# CONFIG_ETH_TRANSMIT_MUTEX is not set +# end of Ethernet + +# +# USB Serial/JTAG +# +CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=n +CONFIG_USB_OTG_SUPPORTED=n + +# +# LEDC +# +CONFIG_LEDC_CTRL_FUNC_IN_IRAM=y +CONFIG_SOC_LEDC_SUPPORTED=y +CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y +CONFIG_SOC_LEDC_SUPPORT_XTAL_CLOCK=y +CONFIG_SOC_LEDC_CHANNEL_NUM=8 +CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=14 +CONFIG_SOC_LEDC_SUPPORT_FADE_STOP=y + +# +# RMT +# +CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y +CONFIG_RMT_ISR_IRAM_SAFE=y +CONFIG_RMT_RECV_FUNC_IN_IRAM=y +ONFIG_SOC_RMT_GROUPS=1 +CONFIG_SOC_RMT_TX_CANDIDATES_PER_GROUP=4 +CONFIG_SOC_RMT_RX_CANDIDATES_PER_GROUP=4 +CONFIG_SOC_RMT_CHANNELS_PER_GROUP=8 +CONFIG_SOC_RMT_MEM_WORDS_PER_CHANNEL=48 +CONFIG_SOC_RMT_SUPPORT_RX_PINGPONG=y +CONFIG_SOC_RMT_SUPPORT_RX_DEMODULATION=y +CONFIG_SOC_RMT_SUPPORT_TX_ASYNC_STOP=y +CONFIG_SOC_RMT_SUPPORT_TX_LOOP_COUNT=y +CONFIG_SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP=y +CONFIG_SOC_RMT_SUPPORT_TX_SYNCHRO=y +CONFIG_SOC_RMT_SUPPORT_TX_CARRIER_DATA_ONLY=y +CONFIG_SOC_RMT_SUPPORT_XTAL=y +CONFIG_SOC_RMT_SUPPORT_RC_FAST=y +CONFIG_SOC_RMT_SUPPORT_APB=y +CONFIG_SOC_RMT_SUPPORT_DMA=y + +# +# Camera +# +CONFIG_OV7670_SUPPORT=n +CONFIG_OV7725_SUPPORT=n +CONFIG_NT99141_SUPPORT=n +CONFIG_OV3660_SUPPORT=y +CONFIG_OV2640_SUPPORT=y +CONFIG_OV5640_SUPPORT=y +CONFIG_GC2145_SUPPORT=n +CONFIG_GC032A_SUPPORT=n +CONFIG_GC0308_SUPPORT=n +CONFIG_BF20A6_SUPPORT=n +CONFIG_BF3005_SUPPORT=n +CONFIG_SC030IOT_SUPPORT=n +CONFIG_MEGA_CCM_SUPPORT=n +CONFIG_HM1055_SUPPORT=n +CONFIG_HM0360_SUPPORT=n +# CONFIG_GC_SENSOR_WINDOWING_MODE=y +CONFIG_GC_SENSOR_SUBSAMPLE_MODE=y +CONFIG_CAMERA_TASK_STACK_SIZE=2048 +# CONFIG_CAMERA_CORE0 is not set +CONFIG_CAMERA_CORE1=y +# CONFIG_CAMERA_NO_AFFINITY is not set +CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX=32768 +CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_AUTO=y +# CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_CUSTOM is not set +# end of Camera configuration + +# +# Modbus configuration +# +# CONFIG_FMB_COMM_MODE_TCP_EN=y +CONFIG_FMB_TCP_PORT_DEFAULT=502 +CONFIG_FMB_TCP_PORT_MAX_CONN=5 +CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20 +# CONFIG_FMB_TCP_UID_ENABLED is not set +CONFIG_FMB_COMM_MODE_RTU_EN=y +# CONFIG_FMB_COMM_MODE_ASCII_EN=y +CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000 +CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 +CONFIG_FMB_QUEUE_LENGTH=20 +CONFIG_FMB_PORT_TASK_STACK_SIZE=4096 +CONFIG_FMB_SERIAL_BUF_SIZE=256 +CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB=8 +CONFIG_FMB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS=0 +CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS=1000 +CONFIG_FMB_PORT_TASK_PRIO=10 +# CONFIG_FMB_PORT_TASK_AFFINITY_NO_AFFINITY is not set +# CONFIG_FMB_PORT_TASK_AFFINITY_CPU0=y +# CONFIG_FMB_PORT_TASK_AFFINITY_CPU1=y +CONFIG_FMB_PORT_TASK_AFFINITY=0x0 +CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT=y +CONFIG_FMB_CONTROLLER_SLAVE_ID=0x00112233 +CONFIG_FMB_CONTROLLER_SLAVE_ID_MAX_SIZE=32 +CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20 +CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 +CONFIG_FMB_CONTROLLER_STACK_SIZE=4096 +CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20 +CONFIG_FMB_TIMER_PORT_ENABLED=y +# CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD is not set +# CONFIG_FMB_EXT_TYPE_SUPPORT is not set +# end of Modbus configuration diff --git a/code/sdkconfig.freenove-esp32s3.defaults b/code/sdkconfig.freenove-esp32s3.defaults new file mode 100644 index 00000000..b27fe4f3 --- /dev/null +++ b/code/sdkconfig.freenove-esp32s3.defaults @@ -0,0 +1,230 @@ +################################################## +# Application and board specific configuration +# Edit this file instead of sdkconfig.{board_type}! +# After editing make sure to explicitly delete +# sdkconfig.{board_type} to apply your changes! +################################################## + +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET_ARCH="xtensa" +CONFIG_IDF_TARGET_ESP32S3=y +CONFIG_IDF_TARGET="esp32s3" + +# +# SDMMC +# +CONFIG_SOC_SDMMC_USE_GPIO_MATRIX=y + +# +# Serial flasher config +# +# CONFIG_ESPTOOLPY_NO_STUB is not set +# CONFIG_ESPTOOLPY_OCT_FLASH is not set +CONFIG_ESPTOOLPY_FLASH_MODE_AUTO_DETECT=y +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_QOUT=y +# CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT=y +CONFIG_ESPTOOLPY_FLASHMODE="qio" +# CONFIG_ESPTOOLPY_FLASHFREQ_40M=y +# CONFIG_ESPTOOLPY_FLASHFREQ_40M_DEFAULT=y +# CONFIG_ESPTOOLPY_FLASHFREQ="40m" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M_DEFAULT=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +# CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE="4MB" +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE="8MB" +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="16MB" +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y + +CONFIG_SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_20M_SUPPORTED=y +CONFIG_SOC_SPIRAM_SUPPORTED=y + +# +# Partition Table +# +# CONFIG_PARTITION_TABLE_SINGLE_APP is not set +# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set +# CONFIG_PARTITION_TABLE_TWO_OTA=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_custom_16mb.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_custom_16mb.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y + +# +# ESP PSRAM +# +CONFIG_SPIRAM=y + +# +# SPI configuration +# +CONFIG_SPI_MASTER_IN_IRAM=y +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +CONFIG_SPI_SLAVE_IN_IRAM=y +CONFIG_SPI_SLAVE_ISR_IN_IRAM=y + +# +# SPI RAM config +# +# CONFIG_SPIRAM_MODE_QUAD is not set +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_TYPE_AUTO=y +CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y +# CONFIG_SPIRAM_SPEED_40M=y +# CONFIG_SPIRAM_SPEED=40 +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_SPEED=80 +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=131072 +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y + +CONFIG_ESP_ROM_HAS_SPI_FLASH=y +CONFIG_SOC_SPI_FLASH_SUPPORTED=y +CONFIG_ESP32S3_SPIRAM_SUPPORT=y +CONFIG_SOC_SPI_MEM_SUPPORT_OPI_MODE=y + +# +# Cache config +# +CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y +# CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE=0x4000 +CONFIG_ESP32S3_INSTRUCTION_CACHE_8WAYS=y +CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS=8 +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32 +CONFIG_ESP32S3_DATA_CACHE_64KB=y +# CONFIG_ESP32S3_DATA_CACHE_SIZE=0x8000 +CONFIG_ESP32S3_DATA_CACHE_8WAYS=y +CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8 +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=64 + +# +# ESP-TLS +# +# CONFIG_ESP_TLS_USING_MBEDTLS=y +# CONFIG_ESP_TLS_USE_DS_PERIPHERAL=n +# CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK is not set +# CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set +# CONFIG_ESP_TLS_PSK_VERIFICATION is not set +# CONFIG_ESP_TLS_INSECURE=y +# CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y +# end of ESP-TLS + +# +# USB Serial/JTAG +# +CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=n +CONFIG_USB_OTG_SUPPORTED=n + +# +# LEDC +# +CONFIG_LEDC_CTRL_FUNC_IN_IRAM=y +CONFIG_SOC_LEDC_SUPPORTED=y +CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y +CONFIG_SOC_LEDC_SUPPORT_XTAL_CLOCK=y +CONFIG_SOC_LEDC_CHANNEL_NUM=8 +CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=14 +CONFIG_SOC_LEDC_SUPPORT_FADE_STOP=y + +# +# RMT +# +CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y +CONFIG_RMT_ISR_IRAM_SAFE=y +CONFIG_RMT_RECV_FUNC_IN_IRAM=y +ONFIG_SOC_RMT_GROUPS=1 +CONFIG_SOC_RMT_TX_CANDIDATES_PER_GROUP=4 +CONFIG_SOC_RMT_RX_CANDIDATES_PER_GROUP=4 +CONFIG_SOC_RMT_CHANNELS_PER_GROUP=8 +CONFIG_SOC_RMT_MEM_WORDS_PER_CHANNEL=48 +CONFIG_SOC_RMT_SUPPORT_RX_PINGPONG=y +CONFIG_SOC_RMT_SUPPORT_RX_DEMODULATION=y +CONFIG_SOC_RMT_SUPPORT_TX_ASYNC_STOP=y +CONFIG_SOC_RMT_SUPPORT_TX_LOOP_COUNT=y +CONFIG_SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP=y +CONFIG_SOC_RMT_SUPPORT_TX_SYNCHRO=y +CONFIG_SOC_RMT_SUPPORT_TX_CARRIER_DATA_ONLY=y +CONFIG_SOC_RMT_SUPPORT_XTAL=y +CONFIG_SOC_RMT_SUPPORT_RC_FAST=y +CONFIG_SOC_RMT_SUPPORT_APB=y +CONFIG_SOC_RMT_SUPPORT_DMA=y + +# +# Camera +# +CONFIG_OV7670_SUPPORT=n +CONFIG_OV7725_SUPPORT=n +CONFIG_NT99141_SUPPORT=n +CONFIG_OV3660_SUPPORT=y +CONFIG_OV2640_SUPPORT=y +CONFIG_OV5640_SUPPORT=y +CONFIG_GC2145_SUPPORT=y +CONFIG_GC032A_SUPPORT=n +CONFIG_GC0308_SUPPORT=n +CONFIG_BF20A6_SUPPORT=n +CONFIG_BF3005_SUPPORT=n +CONFIG_SC030IOT_SUPPORT=n +CONFIG_MEGA_CCM_SUPPORT=n +CONFIG_HM1055_SUPPORT=n +CONFIG_HM0360_SUPPORT=n +# CONFIG_GC_SENSOR_WINDOWING_MODE=y +CONFIG_GC_SENSOR_SUBSAMPLE_MODE=y +CONFIG_CAMERA_TASK_STACK_SIZE=2048 +# CONFIG_CAMERA_CORE0 is not set +CONFIG_CAMERA_CORE1=y +# CONFIG_CAMERA_NO_AFFINITY is not set +CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX=32768 +CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_AUTO=y +# CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_CUSTOM is not set +# end of Camera configuration + +# +# Modbus configuration +# +# CONFIG_FMB_COMM_MODE_TCP_EN=y +CONFIG_FMB_TCP_PORT_DEFAULT=502 +CONFIG_FMB_TCP_PORT_MAX_CONN=5 +CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20 +# CONFIG_FMB_TCP_UID_ENABLED is not set +CONFIG_FMB_COMM_MODE_RTU_EN=y +# CONFIG_FMB_COMM_MODE_ASCII_EN=y +CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000 +CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 +CONFIG_FMB_QUEUE_LENGTH=20 +CONFIG_FMB_PORT_TASK_STACK_SIZE=4096 +CONFIG_FMB_SERIAL_BUF_SIZE=256 +CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB=8 +CONFIG_FMB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS=0 +CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS=1000 +CONFIG_FMB_PORT_TASK_PRIO=10 +# CONFIG_FMB_PORT_TASK_AFFINITY_NO_AFFINITY is not set +# CONFIG_FMB_PORT_TASK_AFFINITY_CPU0=y +# CONFIG_FMB_PORT_TASK_AFFINITY_CPU1=y +CONFIG_FMB_PORT_TASK_AFFINITY=0x0 +CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT=y +CONFIG_FMB_CONTROLLER_SLAVE_ID=0x00112233 +CONFIG_FMB_CONTROLLER_SLAVE_ID_MAX_SIZE=32 +CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20 +CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 +CONFIG_FMB_CONTROLLER_STACK_SIZE=4096 +CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20 +CONFIG_FMB_TIMER_PORT_ENABLED=y +# CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD is not set +# CONFIG_FMB_EXT_TYPE_SUPPORT is not set +# end of Modbus configuration diff --git a/code/sdkconfig.wrover-kit-esp32.defaults b/code/sdkconfig.wrover-kit-esp32.defaults new file mode 100644 index 00000000..5cea03b7 --- /dev/null +++ b/code/sdkconfig.wrover-kit-esp32.defaults @@ -0,0 +1,177 @@ +################################################## +# Application and board specific configuration +# Edit this file instead of sdkconfig.{board_type}! +# After editing make sure to explicitly delete +# sdkconfig.{board_type} to apply your changes! +################################################## + +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET_ARCH="xtensa" +CONFIG_IDF_TARGET_ESP32=y +CONFIG_IDF_TARGET="esp32" + +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT=y +# CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +CONFIG_ESPTOOLPY_FLASHMODE="qio" +# CONFIG_ESPTOOLPY_FLASHMODE="dout" +# CONFIG_ESPTOOLPY_FLASHMODE="dio" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M_DEFAULT=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE="8MB" +# CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE="16MB" +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y +CONFIG_ESP_ROM_HAS_SPI_FLASH=y +CONFIG_SOC_SPI_FLASH_SUPPORTED=y +CONFIG_SOC_SPIRAM_SUPPORTED=y +CONFIG_ESP32_SPIRAM_SUPPORT=y + +# +# ESP PSRAM +# +CONFIG_SPIRAM=y + +# +# ESP-Driver:SPI Configurations +# +CONFIG_SPI_MASTER_IN_IRAM=y +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +CONFIG_SPI_SLAVE_IN_IRAM=y +CONFIG_SPI_SLAVE_ISR_IN_IRAM=y +# end of ESP-Driver:SPI Configurations + +# +# SPI RAM config +# +CONFIG_SPIRAM_MODE_QUAD=y +CONFIG_SPIRAM_TYPE_AUTO=y +# CONFIG_SPIRAM_SPEED_40M=y +# CONFIG_SPIRAM_SPEED=40 +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_SPEED=80 +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384 +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=131072 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y + +# +# Partition Table +# +# CONFIG_PARTITION_TABLE_SINGLE_APP is not set +# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set +# CONFIG_PARTITION_TABLE_TWO_OTA=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_custom_4mb.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_custom_4mb.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y + +# +# ESP-TLS +# +# CONFIG_ESP_TLS_USING_MBEDTLS=y +# CONFIG_ESP_TLS_USE_DS_PERIPHERAL=n +# CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK is not set +# CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set +# CONFIG_ESP_TLS_PSK_VERIFICATION is not set +# CONFIG_ESP_TLS_INSECURE=y +# CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y +# end of ESP-TLS + +# +# ESP-Driver:LEDC Configurations +# +CONFIG_LEDC_CTRL_FUNC_IN_IRAM=y +# end of ESP-Driver:LEDC Configurations + +# +# Legacy RMT Driver Configurations +# +CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y +# end of Legacy RMT Driver Configurations + +# +# ESP-Driver:RMT Configurations +# +CONFIG_RMT_ISR_IRAM_SAFE=y +CONFIG_RMT_RECV_FUNC_IN_IRAM=y +# CONFIG_RMT_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:RMT Configurations + +# +# Camera +# +CONFIG_OV7670_SUPPORT=n +CONFIG_OV7725_SUPPORT=n +CONFIG_NT99141_SUPPORT=n +CONFIG_OV3660_SUPPORT=y +CONFIG_OV2640_SUPPORT=y +CONFIG_OV5640_SUPPORT=y +CONFIG_GC2145_SUPPORT=n +CONFIG_GC032A_SUPPORT=n +CONFIG_GC0308_SUPPORT=n +CONFIG_BF20A6_SUPPORT=n +CONFIG_BF3005_SUPPORT=n +CONFIG_SC030IOT_SUPPORT=n +CONFIG_MEGA_CCM_SUPPORT=n +CONFIG_HM1055_SUPPORT=n +CONFIG_HM0360_SUPPORT=n +# CONFIG_GC_SENSOR_WINDOWING_MODE=y +# CONFIG_GC_SENSOR_SUBSAMPLE_MODE=y +CONFIG_CAMERA_TASK_STACK_SIZE=2048 +# CONFIG_CAMERA_CORE0 is not set +CONFIG_CAMERA_CORE1=y +# CONFIG_CAMERA_NO_AFFINITY is not set +CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX=32768 +CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_AUTO=y +# CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_CUSTOM is not set +# end of Camera configuration + +# +# Modbus configuration +# +# CONFIG_FMB_COMM_MODE_TCP_EN=y +CONFIG_FMB_TCP_PORT_DEFAULT=502 +CONFIG_FMB_TCP_PORT_MAX_CONN=5 +CONFIG_FMB_TCP_CONNECTION_TOUT_SEC=20 +# CONFIG_FMB_TCP_UID_ENABLED is not set +CONFIG_FMB_COMM_MODE_RTU_EN=y +# CONFIG_FMB_COMM_MODE_ASCII_EN=y +CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=3000 +CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 +CONFIG_FMB_QUEUE_LENGTH=20 +CONFIG_FMB_PORT_TASK_STACK_SIZE=4096 +CONFIG_FMB_SERIAL_BUF_SIZE=256 +CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB=8 +CONFIG_FMB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS=0 +CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS=1000 +CONFIG_FMB_PORT_TASK_PRIO=10 +# CONFIG_FMB_PORT_TASK_AFFINITY_NO_AFFINITY is not set +# CONFIG_FMB_PORT_TASK_AFFINITY_CPU0=y +# CONFIG_FMB_PORT_TASK_AFFINITY_CPU1=y +CONFIG_FMB_PORT_TASK_AFFINITY=0x0 +CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT=y +CONFIG_FMB_CONTROLLER_SLAVE_ID=0x00112233 +CONFIG_FMB_CONTROLLER_SLAVE_ID_MAX_SIZE=32 +CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20 +CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 +CONFIG_FMB_CONTROLLER_STACK_SIZE=4096 +CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20 +CONFIG_FMB_TIMER_PORT_ENABLED=y +# CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD is not set +# CONFIG_FMB_EXT_TYPE_SUPPORT is not set +# end of Modbus configuration diff --git a/param-docs/expert-params.txt b/param-docs/expert-params.txt index f5317096..88d8bd78 100644 --- a/param-docs/expert-params.txt +++ b/param-docs/expert-params.txt @@ -1,5 +1,6 @@ +SaveAllFiles WaitBeforeTakingPicture -CamFrameSize +CamXclkFreqMhz CamGainceiling CamQuality CamAutoSharpness @@ -24,24 +25,28 @@ CamZoom CamZoomSize CamZoomOffsetX CamZoomOffsetY -demo +Demo SearchFieldX SearchFieldY +SearchMaxAngle +Antialiasing AlignmentAlgo CNNGoodThreshold PreValueAgeStartup ErrorMessage +MaxFlowRate +ProcessAlgoNew +CACert +ClientCert +ClientKey +ValidateServerCert IO0 IO1 IO3 IO4 IO12 IO13 -AutoStart +TimeServer Hostname RSSIThreshold -TimeServer -CACert -ValidateServerCert -ClientCert -ClientKey +CPUFrequency diff --git a/param-docs/generate-template-param-doc-pages.py b/param-docs/generate-template-param-doc-pages.py index 73daa01f..6b5a4833 100644 --- a/param-docs/generate-template-param-doc-pages.py +++ b/param-docs/generate-template-param-doc-pages.py @@ -8,7 +8,6 @@ import os import configparser import urllib.request - configFileUrl = "https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/rolling/sd-card/config/config.ini" parameterDocsFolder = "parameter-pages" @@ -16,7 +15,6 @@ parameterTemplateFile = "./templates/parameter.md" expertParameterListFile = "./expert-params.txt" hiddenInUiParameterListFile = "./hidden-in-ui.txt" - # Fetch default config file from URL print("Fetching %r..." % configFileUrl) with urllib.request.urlopen(configFileUrl) as response: @@ -39,7 +37,6 @@ with open(expertParameterListFile) as f: with open(hiddenInUiParameterListFile) as f: hiddenInUiParameters = f.read().splitlines() - config = configparser.ConfigParser(allow_no_value=True) config.optionxform = str # Make it case-insensitive config.read_string(content) @@ -51,7 +48,6 @@ if not os.path.exists(parameterDocsFolder): with open(parameterTemplateFile, 'r') as parameterTemplateFileHandle: parameterTemplate = parameterTemplateFileHandle.read() - print("For each section/parameter, check if there is already a documentation page in the folder %r..." % (os.getcwd() + "/" + parameterDocsFolder)) for section in config: if section != "DEFAULT": diff --git a/param-docs/parameter-pages/Alignment/SearchMaxAngle.md b/param-docs/parameter-pages/Alignment/SearchMaxAngle.md new file mode 100644 index 00000000..d496c673 --- /dev/null +++ b/param-docs/parameter-pages/Alignment/SearchMaxAngle.md @@ -0,0 +1,14 @@ +# Parameter `SearchMaxAngle` +Default Value: `45` + +Unit: Degrees + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +Angle, by which the reference is max rotated when searching (`0` .. `180`). + +!!! Note + Since the alignment is one of the steps using a lot of computation time, + the max angle should be as small as possible. + The calculation time goes quadratic with the angle. diff --git a/param-docs/parameter-pages/PostProcessing/NUMBER.CheckDigitIncreaseConsistency.md b/param-docs/parameter-pages/PostProcessing/NUMBER.CheckDigitIncreaseConsistency.md deleted file mode 100644 index 62c92b19..00000000 --- a/param-docs/parameter-pages/PostProcessing/NUMBER.CheckDigitIncreaseConsistency.md +++ /dev/null @@ -1,11 +0,0 @@ -# Parameter `CheckDigitIncreaseConsistency` -Default Value: `false` - -!!! Warning - This is an **Expert Parameter**! Only change it if you understand what it does! - -An additional consistency check. -It especially improves the zero crossing check between digits. - -!!! Note - If you edit the config file manually, you must prefix this parameter with `` followed by a dot (eg. `main.CheckDigitIncreaseConsistency`). The reason is that this parameter is specific for each `` (`` is the name of the number sequence defined in the ROI's). diff --git a/param-docs/parameter-pages/PostProcessing/NUMBER.MaxFlowRate.md b/param-docs/parameter-pages/PostProcessing/NUMBER.MaxFlowRate.md new file mode 100644 index 00000000..338fbe54 --- /dev/null +++ b/param-docs/parameter-pages/PostProcessing/NUMBER.MaxFlowRate.md @@ -0,0 +1,13 @@ +# Parameter `MaxFlowValue` +Default Value: `4,0` + +Only the relevant ROI's are evaluated (which change with `PreValue` +/- `MaxFlowValue`), the remaining ROI's retain the old value. + +!!! Warning + Experimental function, description will come later. + +!!! Warning + This is an **Expert Parameter**! Only change it if you understand what it does! + +!!! Note + If you edit the config file manually, you must prefix this parameter with `` followed by a dot (eg. `main.MaxFlowValue`). The reason is that this parameter is specific for each `` (`` is the name of the number sequence defined in the ROI's). diff --git a/param-docs/parameter-pages/TakeImage/CamXclkFreqMhz.md b/param-docs/parameter-pages/TakeImage/CamXclkFreqMhz.md new file mode 100644 index 00000000..62c148f0 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/CamXclkFreqMhz.md @@ -0,0 +1,13 @@ +# Parameter `CamXclkFreqMhz` + +**Camera xclk Frequenz in Mhz** + +- Set the **Camera xclk Frequenz in Mhz**. + +Default Value: `20` + +See [here](../datasheets/Camera.ov2640_ds_1.8_.pdf) for the ov2640 camera datasheet.
+See [here](../datasheets/OV5640_datasheet.pdf) for the ov5640 camera datasheet. + +!!! Warning + After changing this parameter you need to update your reference image and alignment markers! diff --git a/param-docs/parameter-pages/TakeImage/SaveAllFiles.md b/param-docs/parameter-pages/TakeImage/SaveAllFiles.md new file mode 100644 index 00000000..c191ffe7 --- /dev/null +++ b/param-docs/parameter-pages/TakeImage/SaveAllFiles.md @@ -0,0 +1,7 @@ +# Parameter `SaveAllFiles` + +**Save all files** + +- When **true**, all images that arise during processing are saved on the SD card. + +Default Value: `false` diff --git a/sd-card/config/config.ini b/sd-card/config/config.ini index 12a0548f..62604657 100644 --- a/sd-card/config/config.ini +++ b/sd-card/config/config.ini @@ -1,7 +1,9 @@ [TakeImage] ;RawImagesLocation = /log/source ;RawImagesRetention = 15 +SaveAllFiles = false WaitBeforeTakingPicture = 2 +CamXclkFreqMhz = 20 CamGainceiling = x8 CamQuality = 10 CamBrightness = 0 @@ -35,9 +37,10 @@ LEDIntensity = 50 Demo = false [Alignment] -InitialRotate = 0.0 SearchFieldX = 20 SearchFieldY = 20 +SearchMaxAngle = 15 +InitialRotate = 0.0 AlignmentAlgo = default /config/ref0.jpg 103 271 /config/ref1.jpg 442 142 @@ -62,18 +65,19 @@ main.ana3 283 374 92 92 false main.ana4 155 328 92 92 false [PostProcessing] -main.DecimalShift = 0 -main.AnalogDigitTransitionStart = 9.2 -main.ChangeRateThreshold = 2 PreValueUse = true PreValueAgeStartup = 720 +ErrorMessage = true main.AllowNegativeRates = false +main.DecimalShift = 0 +main.AnalogToDigitTransitionStart = 9.8 +;main.MaxFlowRate = 4.0 main.MaxRateValue = 0.05 -;main.MaxRateType = AbsoluteChange +main.MaxRateType = AbsoluteChange +main.ChangeRateThreshold = 2 main.ExtendedResolution = false main.IgnoreLeadingNaN = false -ErrorMessage = true -main.CheckDigitIncreaseConsistency = false +main.ProcessAlgoNew = false ;[MQTT] ;Uri = mqtt://IP-ADRESS:1883 @@ -81,8 +85,9 @@ main.CheckDigitIncreaseConsistency = false ;ClientID = watermeter ;user = USERNAME ;password = PASSWORD -RetainMessages = false -HomeassistantDiscovery = false +;RetainMessages = false +;HomeassistantDiscovery = false +;DiscoveryPrefix = 1 ;MeterType = other ;CACert = /config/certs/RootCA.pem ;ClientCert = /config/certs/client.pem.crt @@ -139,7 +144,7 @@ LogfilesRetention = 3 TimeZone = CET-1CEST,M3.5.0,M10.5.0/3 ;TimeServer = pool.ntp.org ;Hostname = undefined -RSSIThreshold = -75 +RSSIThreshold = 0 CPUFrequency = 160 Tooltip = true -SetupMode = true +SetupMode = true \ No newline at end of file diff --git a/sd-card/html/Flowstate_at_work.jpg b/sd-card/html/Flowstate_at_work.jpg new file mode 100644 index 0000000000000000000000000000000000000000..911ca849fea309d4bba683307653e725dd5278bc GIT binary patch literal 48431 zcmeFY2Ut^G)-ZZdktQG_h_oP}*eKF_UPVDf3?Pbv5Rnc7(tDz)AYCk25F&!2B1XE> zBGN=fq<2CnB25xXIFRJzZhYT)XYMyM-~4l*|9=0y&of6)vd+ocWv#u|+N*5XJ?vp< z^D$#nV~B%;13C}>L2SzAH%CG}Tp`HZ9NG&(5I4ldu?1QWAP(>k;t+>8f58xRfkWa? z*pp-TA2{m(&H)H~Lj2(N=TQ9xZw2t>SPy8!A8qe|@8{q?|Ht#MZxd5<3mH|V11d^t zDiEZqqN=8=qM@s*DgyuwZCw>j(Dh@Ezs?1;UAs0x$e;ZJGX4UC>2mx9hTe0m`_&H6 z!C}a;{*OL3f~Spt1B?6tuVMX3C&%Hx(#diBZ|Pk3Xa5}5>(>2cEJ5pEv*HGv;|9=q zaDz$x{>|=T_d`Yy_r{G}8#%eTxVU(DxOq2i<=@1|w`uzpVZp6Z;wWh;aY;!Tc_jrI z+5K{olDoC`>{mIUuC9(!&@s?f)mKteS6%CbgNKJ_6W=B=ett349g;g#|Lu$Y9une) zLZQd&Id(wnggDj^N|6qp+nTm#p7b)hnr4+;WEsYDBDBsq$(U{G)kzMYoA<7vH%{ zVfUWB>Kd9_+B&*MhmRPW95p?5()!eCn=@x^ot#}<-P}Dq0|JABLqfyCuU@+z6MG{r z{?_d~chm0OfAH{0cFxmhxzAs`EG#N6DJ^^ZuKYu7U4292$EHth?H!$6-95d1l#$V~ z@$VBqCZ}lg3yVwiWq4(EjV>TJzmxSRW&edPAwbu<4I9>P;98@LV_gWi*9&dn+;L#z zmV=gDj()9C z+_TGEG=+wJd#sLh?I(6fUpV7vK|c2c^|32Ir7^?p=-bWk=MT+WT|P37BwGvUa{3!R zS{B&7zKf|fGRs%rWjuYjL$>@>M~iCYK=IZJ{On-`@P3K<`Bq5;rc`E7T~5g!XmEzeZVgD(o0Dwex>5x*c7A8EPDq zg5)+{)>A){tZ~@l`D4ygVvxXxjNdp_&tzvR#Jjg09q@Jz`sn!M;Ph{j4o`=ts}wBE z18SD7&UkJvWkY*Vyo1ztqg>^LJIUHoQEn7M9USSk>0xjdZ?6|UdzlW)9ObBS;Yz*Q zi8%%>9{XGNzqT1cCCX9JSAUjV5cwImxzBj`=7Of*qhLr>KY457#8!@lxft))`+wp~ zUeA2*U>d!U8(ff8zAJ&MG?UzTVX)AzQF6i>o{V|QBCzfgycYC{%CoH#c=PH9Eht;A z^K9taB&nEG#D2cp zA(vm5E0MQ`%vK|ZzGW&)HmesjO2-}W5ZjA`AyaM1QK@OUI%7oYROFFo{moCk`aJXN z;$IQJAGuXgV|B2B`@G5Cuu-DHF{|9FYIB1s3C+AiPetz@E$*v+*%xA%LdC{=N4ik# z3_Sa}UF?eviwSSdjyVa>t95LS=wi8E*lNLX1p4(Mvd~?hM}m)4D|+CzkH0E@d+i>t z1?xLUzk&hpjc$WDb%@4>kgr$-C;P zLv3be_yf;`F3(n3Zt|l0ujg-mS9Vu>`1`Xr-HSYd=2|C8-(5fPSv&iU^RC0yW@#R| zTw)fjk9@dp9%4fRTu<20vP?qof)P5pSQ}+o;*}nCNJn(QSvx7wlWVZSUo11+dL}_3 zS>D9rW8axguT;5JrTN1!rxbA=Bzk-ASJ_c3o_b;k(XRC88CWh#A(k01BDnFh!m9e! z+0!*+UwKC_>lMlEK`(u+ntx3D5D)EsyzbXWwR_vOZn7xT<2go7uO>wCO%sArzmAHo zP zuYRK&{CIPY$cDHxNDsc_Jv{r_P!+wQW#UxJmB1q9hd#<*u9x{TOut42OxvE!x4O%E z^pND{7#%8nEcr;P0?Fp&U6;2qSEmob!pD-j4!;F2-m1KniPrmG5;}0AEKj*AuDC?W zF7iOp`FrcWkMw^}%>EpDs&KZ18``2eu)e|EqlQAsBHeEY%XxdS^t;zpt^3*XoEMHZ z$`3o``(s2LimYDs+e>(pDssuwPMI2GgX0oMW>%S<#SC&rvg^=@f!gSD%t5(l{{EeYqAso7Ygl5txn#|jzl%@WXyYbU59G*t|O*;MMl%}*~ys3s* z*Qq!;I|&O&P31h}tJIa+=jX?_61AH&b|&=={s?!?a?9p3l7EJJv-`NI^dM=sTasz7 z7{`sB)+)Rv8oY%;1`qX~-utS(Gx2uGxO2rMiJX-XusQmt`>8VSF&D0GE)kKlA4I&& zuPb!Sj;4lWUA#N`{k?*(en4WtwB<=kS%5Byvtn}jhx=67Jzclh(GIybr;d{CuOEb_ z_`mO8xSTcCzhL};w`WMLX5@)kSiYjb-B6+L<{CcOPn(}#x%1`}AIbkQuh1bGY_-3c zq??tM&#}Ipa7ItOwa55StKL_0<{g{w{U;FHApP-I{g&`7#V)X>XA6$B(42Dx9)|b^j6JvA}z;%9=;GIeOa$r&oWMI+q+v| z&s(VFINmz4DZT9Zb|uSo8+G1KCXYR=B)OG7j2QKcEO-%kJ@|d$MT_B$DAhTOsiOk| zxbr-=Awnml_KskSijySQjmIByy%?5MLRe3v=1E8J#!NLts8)L>y;6OhyO*4_0N(Sx9Z^|9&Jucz7(u!N)p88S|d@FcD-^VJnQO>}N=j&un zMg<$PC*_~;Eb)1|HQnA8DJi@6Y3|TcX~B5Tt|3~!X5q?BQ)&3vT#Bqg_HI%6pi|JM z>JvwsPgQkf9?dCB{yq|T+c#alN8%Hemq;Dsl;5<@_|R)R1^t&MX+u@j4pnFg;bz^0 zRH8%Ie3WlHt>9zi&SI=^B(_WcFce{~DkrH_=WXa0u0gh(va zD72-fg?_Zw^I@2lXr(_hl9tS~a(Ot!#i4`ado?d@FMQR+avwg|?(*&E0~M`}1xOhE zHOV|)XK7#Pc*BQLnPoaN++suDVz*OG`;;bKxC+VAx)pMGhE5xQzy3>{eE;VwZ=JU% zw|uy)>+H2hV}R8aBjck&n>4Rnv`EZQla;`KSH#9 zyyWJ7H5O%$_6}3~oWz`nn<|6XH{A3)$umE1^e#r2YgXm%0m-hh@%D<_$b*a*AKrKd z7{A;a`N~>pV9Vo7Rlg>ExA>7rzY(tXz@_$!A*L^}$duS;VZvu#HXr({!HkD! z%5en#B|Rx`rpNHd1p5QZQBL9$%}Xwp%SD4G^7A^cuzYY=qrK3I6`%J(fyh|>v4Fb> zb#%H>_Oc#vR&Kw4N#^6gytt6=`CDnx!I}bESE4`i3|=CcnZNgF-49p~cb4uh6=I3XpuF2kgF2jyOR zmSF76xjn8<1s_|qEskF)=()S`vH|)%nMdDXIL)m1G^z8qnyR;`0GajUxrbNh2T~tN z+C=?nw*I1F2cyC^X58G;~#5t1_;Aw|waH8~+o2?V0ocz|nvCFf-ljmB&5Z zphfSU`CnXhQk|U#a-V;?c_}IhvQDVAJuKcrFV=jvuU-CDaq6e3?Ky`{1|H9zKWs@o zxZPqf>=he=ZIErk=IY^A_4{8GQa+%hq73PyLoYcxdAS72TySyo@G+2`t02kBcsLu#+Gv=om|rq-arZbH z?(cFk{DierxR;ZTvn<+>pI1LrH`M!*w@aX-OsMxop8(xZ1KD4_>jHS~wX&?tFO)zp z1KBemb7AD`?;@kFq^_i*2r?+asa#((qyo(yFFm~BW%h*F4}lCQs; z@&O$k9c2|&WmQ#0fT0)=<`d``s^}9S_p6WJJ2~tU;N4#|yqefd;Z* z62H&R`;xi&pMC#BPI!B-P5BpgK;V&J(D=XOTYz=gB^PBYmjK@&eP_uCAe>sN<^Qtmveo zsj2Des;2Gis{WULOnm|Z9etc!*7^ZGD|vuUwA8dUwOq6`6rD7jL3=F?bwwR@RVPIa zCyfiL&W`G?;QC8@jK2pECdZ3^JD;_FoIyVqv{b+Z)KwJKE*!X^sP3$FKv7#m(^1jY zRYlE3>wt@^wz{*djI)!jv9G_kBUlFyZ$~#5WzS14Zn83K{L(#WVQL_&s-*JAqs2wX zKv#fkAbZ@yCn)rfx7HrsE++#W*En}TQ(Ik2M^#ftMO9N(MO*C;jMFau0YF~Xy84w> z`!#u8BYzjiKwp1rU*C%cvVSJseit{TODA_nA2%1EWMGMZf8+5FIIgCps-fcI>Z0hR-DQnp zkUmsY(NI)Xwf;SQctB;JimI-PimdWqh*Dltqd%$ASN?m-^?#xKjpqGQ>0d8_PJ;)~ z=r2|RJjwhIT>ly5-(vEA*!3TF{aYOPw~+rMUH@U%zr}%n3;93N_5U4q{WXYi@o_d# z2vI;AvPagvF*P#6T3BI>O^+X0D>p*x9$$3w4%h(7m)<^s{#GW3WI$a@hHC;8N<|?N zNC{GPbPBk1(8A*QU#e?=|4r}uRWO9w71#Ru```cDH~h{ZOaN6m8G!Yh(hxF0|F(+R6yfwiUoKZhr$` z_#4>C-QOGFr~@2%XKx?S|GG23!_I4PLmD<2e%|-0+~YQ zkOd?IU4Vih59lHk2q}VZAMhpsvI4&!`X}whf3>#&tuBC89*`4gaTxN2ydlS5?V+_b z065_KD{li_)l`4I;@ETuf;flR?1h~W#C02jSU1^hMi!gRdICY~2O#LZ&tKd7K87Ie zCGh^{U*E|SAV}~U1eMnP_1%Sg5L6ZmL1Mj^9Q_@Cjk6wnu5$&2_4!;-8$Sy{!rvf> z*Y0<|f#24ChqAjN$QtnKcrye&cnCqe+(6&w{t4Y{<>f#5_TS?C!M|U{=XD$$Yybba z)^o03yEcNtCnwiNP>}{VHxC~VH#aY+PxJEg^73s07Z;C!Apa(THTYNY8MIz&1^#d1 z=Hgz%`1h_~#b=J;b(hz3$o{AJ43+%t=gnUESVqEk?IGmDWxc6=^C|R9kmassLoRdC&qlgyRN(fJuTP~H zjoTL4&Cw##3yqsH_AN}0l9|#8wEjk`6|^8uifKUQd~mYb{E+F==Yke0 z`llR|;t3)5JtY$ElqBWcO;g8=cp@6O1NfTvgi@_WvpxAiEh!G_1tky7zdRI7%^GDT z(Jeo~Z0JgNGf9}_p?rfNv9V=)=t4uL(r*m%&jcbjwBL3dCn{<#~Vt}tcY~C8-eCVAxs9gRtve@$=qW38;U$G z!c+_uq?6%R>XR-BJP*?W&Z4A8;L~;o5=6*n8n-c?=)|CTS5t=XF(qplu1w*oB1fA4 zbp2_~>9QFIxpu`Mu?f`dCkqTA=MD$!iY4}I?hL3+a}|ZQnAw?O;>ny$jn~TeIUBMm zNmkxEqbZEf-sneCY-qMn@F^}9X<|dvK*CH41JmN`_B^zW4N-7KD(6daszr=V$0FF! zGdWXF^sxU<2$yVmrdp6znnV+>O)Iaynm757elI+oQq}(R*3?iC8*==1$M-`i$g%z( z)6JD!gcnF9rvYMYw&I(#@`V{4c2tDMm87_hpMvM#>nVN*!)mJb-POY!+!bi}fLKF< z_cSu46lkC61C^EEaHv81NC`M%hl`!P>@uRD8|{Xk+?G)CP1^8iu+vT3W6bwy!I{Rv zuCDmX=Ap}ITf7em)+?i5V`V9j+Hj(0-ANmnYJ2*((#=@eua7>Oi0U9z9H-zrKO_^# z#LvEQSW$g*8lgVGYeP9Qus zwA=KUc)bKHKVj34dEq3JI*yra9Kla>J7G%LtqO{4J%THW6k4hyc)uW?|#MBvt!-i#A3hNZL&4zJ$%$%gJ+GVrm(*Hn|Y#IDYX zhl-#F80QEKW0LLj$AV1~Bv@-=BwFETq~q{*iPoI*6gK2;qd~;i@F3dm&&h7-HQ_{7 zgy$`j3cxQTDO^+QZ>Ve4>VWK1k&#q=f71Xe`E+@_+VbM`edT5R`yCZge&npP~3BEiqgkyU1xhpjCv=0R*L%jEePqnBr^N;*_9-U=V7UoxHjEw~oYM{vtb zN6@o7QwVO`>3#COu0hi1Vnu5^L-!IJBD21JBqLna)yD8B8`{V)VN4TZrL|M>6PXz! zVqD0bS~di#P$Q^b0(}5}#}I?>2Blml3F)62PAHN|JKV0Eq3AQ<$M_-m({=S|vaZc^ z_slk+dEfZaYhIatopqyn z7w*gc<`FXY)M?GVwLVX|8K_rgIBZ~OQp>Qp`HgFtgCUBqF-3TRCLFAkW93l^ zjeS~iV=-ve(G%n`&(l&|bNSA7O-+n`&4})58!Cp{Jz{gCnjh}Q&=jsM&0=c}W!lCT zE-p#!U7GcoC3MOs#-7BMpr~dfSYNP?EGYi@1G>12pp~#_kDSA)w$8R0ocQMKG^^QE zpm2huA0a6?4Cb-ucqD9YW}0CNZ=>hKHjbm1Si`+-;OtI@#RUnLF8+YlCN2|HxX!k6 zLrI$~aBOF-;`cvvTb{#6z2cWod?D!+pe8N)#&8$YiaJ+=f;^G^wBV9lIHWdQq{!Y= zH)-)Re^t(&&cOc3ZaGCJwXGk1ZK z`obv;BfH*dY57xB!5Fudt_V1Ud@h%zRyv(*$V^8M2QAOd09D`cnkqPRm-BUA&<5Oo zS}|U4YWW-PC)jN#`%uGd2tU`|-oZg36)7`?b19ga*J<4jDC%Gpy~>vj9jLNl?FBf? z?eGpZbXnHsoh3Mz^-pdSgF<_|O4UZmn;+AqP^@!z=Y405!1z9}4db_+&B#?Ra3V-B zPJRVxeOimef`usaUPjLieOm2{hnNzqSLopk(iW04-@Bic2f;xok5KQNUeStY$ZC7_ z`q7E&tCxoH@AD>Bk^35a+(BkK`LDB*BfPHl{8D{|W$9{eRvBqH4u^toi%i@=wy^Hi z>dvh;shhBd(!t-hhiZ6F_*JFf7H7$DpVtbFemw;>#3KHcP1uIaa+Me&;Ku8j`Y2K$av2W*ceO35d63%riaPBzwlEI5}I&(r}XZ8N^+TPZ<& zP91v{UmWwoD>t;%Pb%~3XxJemk9^Z*tD)s5M(E#e&|uoWALuTjDe-)WD$ckCH<-p{(h}57s$+v;_{s2^(Xmq0!1ROiaNh~H8Lkh6YltL_ z8cxRuz;;JVTGj0~^zv7g1E1^}*c1)q*^tk%f%X~2MQ>dRy));FOUe#Mb30;`v|G_v zwmbu8@z&1c{p(v$mi3x+0lOaQb>KppX-86em+V4mxznOe$h}+=1MFdh8B=T`x>Rt+ zaTL|l*huI>c9kqF_UE&q-iK9hWV{{zLWt zw#S|7Q3nkJ@k^;BsxF$Ui^p9?KeA>00G2ZtJv=6nvdJFC4-3xn@8|9lU z?84Vb-JK?^xV1W^!v_jry><|FEU_+EnlXeTja=@!c2@3q3C>YDdwxeR5f&R9nZg{f zwnTff@PsaeAlZJhG1!U|#2P%Txewu6QAl!W1(6LY>0zzms~c(68)!QRYFP&?9MvoC zq37GhHtPUNr@*4!BQ39;g8qN=CIW*AYfm7&Gw9(5BycoG`I>K=aspvXnm77T@0ao; zZ&0KT^svMSU^UDo@qRznFO(qySwL%twdw^$J@G}#A2KfCKd3!9ah~?`g*$WVUO^f2 zs!$`d;7a2hiKNid@BBP_$ourXSzcVu!;uiB`J;@Cesoyg4-BlZZAR}ou8%A> zT2i@9w0hIG;KAv8@w&NGJ1|J*E&S*WDTW1C9==9_H2jc2ZPOhyG^Euhpw@ZZ#%G=V zq@uBM`}uOT+AjsZ9Mo1CU&1f&> z-VorRm5^&P%s%K&t>E2RbOJ7}+FW(qaI6WzhoK()hH811@$%dc+r(|B+s(a$e5Tb^ z>-wd&Fy)_y8rw)O=U~kRgzuCfb8EsdhACsH+mX$NuD1_Ms55-;_dC;eW#-6wa#szw zZ1JTJqEb|N5qm_Jxvd-RMv8yfa!udF(>uspz;`FSqJSMvMfU{i)hxe`r5mvFDjgX@ zxK`11wIwD2aNK)ZbEvrdr|qwHi<&8tr~kuU{M} zX}u_!=ppWW5%?#iixi#^xmPK2%fvoGSU&*aHAc8$KMJ{*SgSR3I%}j3vpKZllW&Ei z1RHwNM`8tLh&v60fzSs>%CLogAmWiaU53gaL10R4iJci*IMemhs;i50>gxn*xfw@C zK9P#>v7`&Ye$Cg`Y6{ym3J}&0O|2%9D;O?aE#%`Mhk^f5oV0Hg=;;pMnE>ExOkAHc zRQ4)O^eqXDKndo=A+LMCxxv=PFrcRXPV{JLZiLBcMPFd zM&=eku;89dpz2x$+w471QLunob7nZ7(C{gSGUgO)wC<(k?q-I;>zq}5zweS3!c>W> zGeQP@*w9kTPa9s`eq<0t2GtBy9eD`fQL+v{nt3Ej1Z*TKFlW{eG)Iu&4dbsvzCzQh ziCvZh<+%C2E~&Wj+-0q%Dl!5NwEI_2JvS{1vrfeBwL#0`DekyI?7|#v9~kc^=M5C0yL=pUk zOajMifJBenMtEBoMrOMJyUuPS!%Br9jMO++?aXjtKBuE#TU411lfgU&c7tF+M9qY$ zDA=EeL?Juz6g@6Bw4Df+F}48r4ZmomOrqMmfGx*s4g<@lFt>)(qe@O!<%jNTRJEP5 z{hsO^q-TG6_ZA-I9ScHIyAH2t`G$VA5UB3(Ux_Wneo9FPiH?6s+kHS=8E^>>N18Ks zW8Dn}srqT9(lTAM=@#W3uGbY9lG093E}E=pRpbyNQA;g(H4S+n=%v&RgKMoF{z+2_KvY@`i})8}T=jjy2C z(BV!j>`w02LZYRCK-Sn3*^tC*FcM}&r&5pco5pxS(zBE!g~#8vFiUS$ivbJOn~!fN zuvXX*2sne@ZoBT!6cs4-yWe&F+!3*^@ffKS2dw;0e-C=ne?n<>JIk>w$8yPrsqQ-q zxO-4g96$WJn++v(bfHjaZ~GGz$C7f|Y+W0EgRZ)u0uo0A5e6mL3%fW0)DgeBM7LpX z*;n5d+CqOpE%}m0?Q4?MdZUvNdKTS^oscDRy{}Yw;8@`QDPV7V-l3#p14XsZjgLJF zd+2}asn=}T{iq9Oq1ghX^c$>VMYquL3R<1N$-J&%s@FVzOgse<*P?^X^l@wwnrZ_= zf`;j6*4xMkzhP1zSA*Z#`irwcBj#qRpTzF;2C_)QK_ggdg$8jIC^QMp3h&n&mKJF8 z!}qSFxt%?2c`YY9Ch^HT%Jw{<3_16Xax1&i2_Om`EOT3+(|09aNy>kyC^l;;-Da;_95DN6@;^#>UzS;ZAZ^tKvF4Xj+F zYp;@FzA5FD=BO_3L(iv2+fU!hGk8QS{rr>oN-t+5<)IJS9o>JZYvia)CGCUxwvRtn z4TvDQchiGFZ6%fZvNq^@fFUZdh}Jpu?9ymL3pdg>q`?rcMN&uXE%fxm822y^K)v{i zY}rsQngJ|PH_=FU*ifLUU?0Z=*Py#Dy#BY6rB&nUcSbGcS%p5I%v-Sxo6#t`D z?`&2c4ViuSDvWRee`6aCOP()2igH5^5^tXz#TBFO7Wp!O`gRlBePLPYJXU43Ze^&| zc4fuDCVK8_F&o036HDfzyfQXRZdN__0g&#*qy07d*~5&Z?eN{RGviUEN1`0_M^YaB zTqFEg*El6@p^--&FPHs;UeOLX+=2hEgxkl~&vEv^bM#m6#I$wL3#R*)*WJ!C~x?RJYvpw$2{`r2jhh=|EBvx%6wn_oz4~K4hH5hV~tf zHYcoXEX7lfxT6=ri?y*AQOqq~<|S=7#mM7jB{p_;fu%`8u-Zk71bIP=yix0n7H?^Ft$I@}_B$xu95SwBhXSfO8v{7Ob!^@|3* z#gZjMAKwIWnQ>aCUlDwlK_VbWo&%d8Ilr$J?M#9dmE!^hZ)}D!qxOj&>znBw8rO>$ z{NuouwSPecd}c#_n{%;*vdQZ>JTM($hU?tR@Z5{28^QFOcJNh9r?6T117YWT!G&@& zWELw0)r2tbQq9#MVs1Ld@{85gXmDWn92R1DD+$< zdbScDg&y|ktjDQ#|LkUNi5=0Hs3Z0mylDE&zSPG97zhgsbRJZnsISgGMHK1WS&|q0quw=p%&jtIauT>n?zi+o{P!0AtzVz9>uN-Q*am23_iiR+7#OWwvmPxnie6d$Ne9%IOg zPv*^sAOcgYxAH7tjfXQz@dE03HC#6gH9Qr}F5Q|*RZsU-ifBa5k$STXU+5E#L5M%z zm(Vw1!zyP(7qK_6q8db1J>quHd3&Sv_|H#YUeI$#&pzVWf`7k{kA52FjAERKjG}f& zUkqv7RTd~Z5`>~nUL@$Z^BROL+0)aua&M-X$C*G@uEv6XgJCQia?p)M5~E2T>dHO+baB?5wTO!! zQI2$l4^oRTt$2#?x1F6Oc`4i=&1}B2xwDwBs!hzn#O>CJ3f+fHyMNHt*(ER?MTMEI z`17b3G!ITSw88|w;_F5+;Lz|D?D?_$@M-zEe%qS)W7Nhm-{ixL^wr$9=U+zAlydxV zB6@K@6S!TqXJ6vyMR2F_HKIz3gGLDNj?v!YQ%UM^i&Ex`V@K%|PG#N;+Z>h`FFLMrVNv~zTCl*HAqe+0 zMUdOec8PF&OVdrp_J=%=zw%>Fuc9OWz6gG}&_Sr9GK67K`}hYvz+q~2{PhDD7w>K4 zcbOigvI-24FUbcr95h&I8(brrdJxld=<|ryEI-9y{RldqZfEPcnnuiNd@V6}!uQcx zkJ12yqIvOVoJz8lR>&o7OphzNGR&?xbd=Ed0rSu}hFxO=SFk&Zp1~^un z3#@Cz>K(WioDg-cmauityMneQMLFXXquZDciaA)pj{U=|+Psz2dzYl>x~c{^@P{8Y za*Y1^Qy3LPEnwIm#l*Wz4@y6p2lQ4;+OGKO)amW|POpYa$t$x4DFg-Na7gxtT*gc* zzC!@0)ltb+lrB@b3f}h&G;j=@PU%C=xmTIZI zqD|h)hI;Dr@ROFrEM}4^9iE4BshPk#;l+ zYVy>x*2NZ4hrXYZjau}iS6pmp$Co9*G-^1K7lfXrQD4@VMJ1Ep^XcOU1Xh#!NJzpl zeK$6A>luD&f}agN?>Ll6#($3j(!vRLpJ)lBg$o?Nn9f z)r%JwNn1Q_#F%C?#3W?iC98~YaE}ca8`Rwj=ONr?z#YYac&Z0&3bv{x2Cx7dNkA2N z@XXC5ihXi6sxN=T!|~)M%$8afF=C!&7*(mO=;25;d+stq=CDsFq&`GEzGT3py`Fbr7#Ar8h@@5>m7Pszh+V|Z< zXW#kf6}#v?2#C3C{8XqX?ro(MZ91}@vXU)sJ*92sbEY>}r`~JNd~;6w>B2=r3Bcc1 zOucW2Fpf+R(l?;w?`k~qt+?q`TUWzZ)GltL-rr^mq6P;L4M1eg9`sld)0z#v4koWk zE=^&nwwM*uiF(ZHY7y{Pqw>RinOklnW@zwc!kZ0cwVXqE>P-aeQp6*@RAL>=wU*TP47hW1wkCnvN1g!ZRqrmdTdbh7U&uqN@K%k!&(tZPAnEa|1C>q0$yna2@#A8+Zl;FweH z<4L83Zy0x1!G69m7e}6E?7-I;&qngSA}f|%!)cmqi=9w4{5agvI=HwDj=6lC<6>1Y zY?{f`Gnn2g!}YlCzGqo3KHq-ev{D#8-EOOICxYug;mIw8NmZLKnc_6#tl<_RkG_W6 zVFocTvIiQ&+yeM6-WqZr(DxzC$}L#C;vVe>g_C9&z{I&e#4cuUCZm7!2P-F1bS}Ml(h1&QR}G)g@&yW!q*e z(HGN6!QCeap5>`g%U^)s)Blz@mC#tNRs@F)XL!}wUP};2f82|~2hXm#k}+B#=vy|_ zEZW4#P!k!WI0FB@Oxh362y6gIT$paLuQB8mPqDh`VR z^j>;$3)|!GYP}-Yz1HRY&f}#@_h>@C2zM?(-kuF)(8dC&r$H>e& zYSuSz2>jZPCPYQ~9S)Yl#1S6gsSeFNk(RWS;ja75W3RKMI!otPCaS%xS^J7v&+x-7 z#J&iMLgLN@%1q{FxZrUy;YZo&)kon5Ltlpix1G3=G;`bZ9Dyk*LCHAA*AinWAHBW0 z)>ZkU1I1)Bh7pTl%sCMlu1jMewi~99`}Ym+O4yk3gc(TNmF2W$<|y-^m&fa)+mP}h zBY6VMvV6b*(!X&8q_&tq%QmF5GW7iv>R3i*DRo0=QfW28tgvEXcF``veeh%?`O;S; z@H*WEI*J7JFJR1Bx%wK63xGonsA8@Z{p~>l;K0-%7end8{C-pEC#9xOd$+eXl1-(1 z2T_dleH09^>wMQ4*0{bQp>is@PB77ywHz~d@AJF1#U91L2s4oFDtfSgH03J@7|9N) zU_ua#2YFvc+PWf9Z)R*IVwD%UigyHC|F#&?@EvsFt~?6%N;T@+(j3ac zj`d{v3~gse8k%|wd*zzf3rbAbj%YQK^MUMZsM(5-_)Ijc=ypTjo{FNk!8SC#S}hx0 zrK~5Dm#ULq){8gM^%!=&6Nt>oFBlzEZumkAd}EH@1JaKxBg#xs4>B|9bmx0UKYb7c zC+?sK*aGk7YK*cQQK%P;$8HE}+3b1P#CMC=!<^u7`_dDi?cDLeeunV+HCma5DE<72 zJG-{)!2$~u?5q~;cCBvW%_cmhr`_zGSIC0U_n4oDqjJk5V=76AtRAZr{i8ZG0Wjtv zaCGHt)+(u7`%;bkG`V8FK8R#tj#!dFsKT+>sK1qGU6;ez!}HY-NRDPDZ{%$t}gjrdH5`L z#Qa3NRSC^ER^F>4hTp$)IBol50r=<`Yra~(KlZBAj0Ox0HfMb)9(D+^!{ODA7DfU) zVL}vTB?9xC2mb2Vmnl4LmTm@{(e`1fLkWI69xOh{r*xwEUDxmhmrCN^{;1@qyH!O@Op$&qe`z`h{X z`+G)jVZhuCAHYdCkyttd&Y~{Y;AE)Cl@Y0pQUP_}O!FSx`Z{Cu(zfqD-xIxOxXub( zBO`MqUlE1yYp{xbGA}+nSItk&NFt<{F)sLuoRt6`q1Q#91+XVdb|*6ZSub$%xR2;k zVmy%LiU#qC=*Yu;T}3X6$OEmk<;@0DdLQ5w`IKHq?ZkI5ny%UHtZSakgl8QSBmd(mW4hDCj4s{@A*5^vpg$#SvQYZ5q1n z+jRR6G@m^6ZdZ2X7%8qqv)!FalihVmZqP80Akyz6&B+( zZjc~O^05z>G8<{E6WkiwXNS$~lb9^?FV^Bhdn|dd4vR#2vhEdy2wg1%6-haF_ zW%olK$}P8}y+T94yD{{Wy(st*brnyw(hI&#e?W`r3sE|Za}tYbInk-kFn7?~fC}2duvXPSswcZSvm>-AHcx5yqihg#2gFRW zAq(IHH!f;Co+`&PCHeu+Hn#(=i+J`$!0GQZCEmq!I^7=Ys5}y-QC}Ba5Ez0fIc`i< z-Eo}^?I1Q1;2kD&>KHM#sJWS0H6Rk*A5bN8vkSC5I-Pdpbv`YmF;k9~-<&*GXcYK@ z|7qr2)mZwX#K?-&K(WF6LE1cA=h%voe8D7y)?BeEu= z#2JI3-BAWJ$%J6w^;^>O8P$hDQG0rcSc}~hY1xHrL7p2BPp|jN~Ix{NQ}xBUTRn$7@e4%m-Y;2CsRtWWkx^+fW_#T~Dn}TC1e_Im~7X z#COrVW(k7>5;f<})(?qNRP_lWF}YV?%tk$VIXWF+U##jJ?wh(-6+3WPTn z%r_A(p%Lhr@B~^HFbXO8z#d$6_{!Gc*4?G zutY|g6+|F%HTED;h1u$E)8~*y!&p`nR9~O{`8dK1J=8E+(9ObBT_RDB-3B28VI-E3 zw&q9=4$mG}JXaQ1Am$m6qA8w!PuH%}e%n*Riv>eNXQug`ODF8^(4@jePKn>Vj&f|y z*?Al*o6fn`a|`-WG+dP^CBY+Y59BX%#oW?TLs`aFieLZN9_)^lC_uvvF! zpowD#U_>qx2qq|@@1N`4hHQy&Km73C0PPzyrGJ!6^zt{8a!_y;aTG2kwl0~}| z@vS~s#QtaMG8kpKPl36vN<4mJ`iUP*UsfvVB1RmDt)k(s(5vqeRciBKSBhWIcTZcp z&xKxLq)yheUSu@;F_tdRgZg>^dkzrJISQ-Ed(J+IJY<4Q(1VCh{9zwYm-@e$wWu5BMBAHuO$)E%1WEX%EoD$%LYTLj1=hef+XS2OeH>`_$F&(|Y_> znLeq3Otr;dMhP)B{GPxGA8;z&>gl93FS9D~u22*LEp#V=yzEI_Eq>8>js4NUB98T~ z)*_PA3R9qlJZatJY3sN7Q&4y4_hTqDMF2fc4v6si+w@Lga+Y>cp*6zDK27 zw0gTHl74kLcAHf`kA*$aJ-*0|i8wSBxcTYWGx*vJyc=Y?{GjP{``^v_?}l8C?ucx;k3%Cvs3O5x)My7y@P;}$p_Sa|nMV3?>8yN& zFrK9eyxO-|*nt3YyfE`3IQg)j1eY_M{`FFje)}AGYIGBc55pP#WpDda^POTZ+;8>D zMrH5~W{&`8@&DfCG{TGPBY{f8d-RPioWi$7a-x-|Zn&;O-}%B&BoH{=^Zl$MtTkZB zzhYh2{W;Ol&k`}2g4^zQX6VK>So*hF-YUX0J(K=={bwp|A_?z-?=@*ORO;0DJcu5} z^lK#a5_9LR>g9%h+S7seI{WYwsQFAXWI;YCrJ;o}@|g`K5KI3hPp3T%5BOE>^?Pcj znkQB-e=99c@m`b1kE^}krc&ZSF+Lpkk+d*MD?(sE=*V;@+D#eWgPs{>>~G9V;rAek zj3EQ_hMn{Zue0cmx!$7XN!Btk>t7Gs@>Sn_yBhNVadt%PaOvl~s+onWy z4RM~qU6J_FmcA6aHJmW&?c_jeN7Pvrc2y5o@jLfhsr+P+-CE87t5k({#;(xdUN#hY z7EcSJH~zhT!2!gXB{p=(yc-c9;$c?EH`>*t=O>fIb?%;N*du?Z*z{tx8RoFEn^6G` z%~ZyKT_h5xi6@ImiqMp?SgSv+g8GCa95$zXL6C-Tsvb9 zYxH-b`ghvVE$TNCBI??sGrp2$_|#UdOxUal@1oicG)^d} zs0dL(P!VaBK^c;EWD?_mq9CLZ6#+4=fCLCB6#+pZEr@(TOH@EahNw(3B{GYMVGeUh z!b~6ufuu+!)#vqh);jmD@7}ZS@2vIRKXe&0RMlJW-uv0le)g{adBG84P3b%3b95(( z<0M{_=^;+BoW43CG+0}6^;r~!f=~O1`}f;1P5t+)!~DB15Y)iT`p+R}UmyEYnatKMqIe z0%(a~*??%x8OHa9O4G$0#Reju5T)48#i%v=X7ru{daK^s|13H<20atrCd}x6MS$04 zxgfnbST{=`TmKmBhH$(AqLw#_Q-oAz4UGvTL$n^mCKR&__HqzSS2oDOtkNS$6II3$ zGL?n&@DbwO=h+RHX3V9x;GJHGxniY=^)Nsxc`Z`LAC=siMO)1|$pBm2wXD3?an@z) zbn#3za}_j{hgpr|UPK2ehvGIV%KdKNEP4k`WcBLW0Je!Ew49~)PbxBi!w;`mQRY=I zTttd>Mvuz(_J*p-r8EfbZ&9hE6_nKFPjo#5DhsSr{G3c?%gU+OEmuX9~c9?S| zU;zSQO+pDI3XLpgq&Vn=dfdk0~$ggDMk_$bf zH?zZN@);oxWx0>I2P50N-zFgjiv7ieQXi3A10x0r66Ba-L0T zBoxY`aJOhZO+YMQwL=1Vkjn86-RI74P6KGNvqEhIXeaLKrh|3@ZQm_nU!~O7vqfV9 zPw|c}Ymol!Z$w9cJkIUU!M#5V^@|$|F1OAbnJY_Qk@yLyiaUS+OD}?Tc|__1Eo(!# zCvtjhUV!D{ip_jvK*hn)LNoG^;MuxXOejO$`C?;cVWYkgZa_9r0_cT zZKD1`y$+($D?5_`oldLfXn%=tK??b!E=QDd>lELUTc>Z5qfUfD;xVaCTg6$TSteJH zYTp5hDLBmAJ^18&l*xBd={@zgmRhDO0^08; z^z2mLe33U^q*qoM(;Q@5XEz~q6m`+^$-gsQacUDmN$rijznV9?7a@Dm`V!986x>=; zZ2n#=3%zf~iXwn;{83=1CGJ&7s3I^GRn0_HNlc!>eZ>+lp0nto>nXVlOL|1l_<&x! zr64Hm@eJ@4E8xGe7)#zJ<<&g9Kfox#r?+w(1MVg;3mB98Wl0y47t(aBj@7NZ6qD$6 zzpCj{^~Osv&n9*^b9xNY&&G@v1jT#Bz(na{IFU#446QF}BeRCeKrw`8-jxe}-4Val z#fD++d-OALW@iV;HBN*%Ap0mSWd_sN5et$z&pC>K>Y*lW+dH@64}Op5gpfrzf=kbQ|meF@^gh&c0^v7gF~>N(Ya2m=j*;kf5eqCy%fKcvm?pa z>1ppDcTw*P??))IF-2}g920(|Ff|S-+Wo`r`kv0eQ-zRs*0-jw5}XzafPp$=lWPS# z#k#KfZ-{aef-vY|nPGI1c!ixof`u}L;7rfNu7|2l2O7t6M9z2&<;UPwQFEf~5vm3I znXV?*Hr_?1g+Ks=yrlV~t~+ia2%m-|3NVFgf*>v{_TE7^*AvTQ>0xsf5oR`qfO-a5 zbk9^;iR$R(yV8DKOad^Kqzf5ij``nCOWq=;`bigjlbwbXeY=$a2z%-So?@Tjw+r5O z_nwY~Jm=iV=$xME9jhrkPj=;MdkzVo%v9ibzg{az89+}0j@=VYF;E`NaxSXrKY};O zw5)2+ah&bQGLBIfAXydH3gbHi9VTJtn0I4N`U~27=4eJP=iuz70-PinMteKBxn$hyyE@KcsHL$G;edqQu0KxVUo|p(mHs5UfXOVW+6C(mhEN-XM zUtu#8!StynbhW=YwgL2gMB{#qsOv~NKjH7YacPO7;CSc0Vgto;vm>zctQMh@t=OWQ zTbu)I-M`J_1`y*BW9CC|L*Zj~IBDi0%ImWyz?|Cu+@HmY7N}}UYOabvWTP^0yc4P1 zNt-Ql3m@}LkX+j}?Fhb!C{DPO-!h&HxZYcAB!XGw5bQ&PUc%p1IJI2Z&bhSXfbAZ}A+-XuPu|iw{ zW`2>tY()E?Eu`XtKb6tCjk&WVf3D@!E!srdwwD}8xgY-wH>)pSzycM$ESJ&*Y3!>o zN1POk#bZ^WhmGA=Th?}A#8xf&8c-uL4EEm(NK*3yOlZvvIN7-Q+jJV(V@pBa|1eo$ z0sj6(Ueuv8*ky#P?K0!l)Vr@{D2L^tUsLsf5VaHilM28_7OMI&D{W>qmdHpT(+v9Y31~?0{0wIq?mF`kMFTFOlW&TQ zBY_Bf1Nlt*bVkQ~`D##ex^)b##CXXIgUy9O$2kUKc246SmwKUL!xkG!d8G^Uy!D*T zcY?@42S{2^Mai~glTpHu0 zD5(xy4@u&bQgck~9A)|hftY?C?`o_-Ddfw#DTn&B)om1A67GUXTI#Vp=Z)`+^WMF& ze>aZW1p8Xt3V@_~v4e?+kDjgA;@ij(+nm&0>8ghFa$j>`u+0%ynkf^=4@%O9*oqCB zJfNNf9omFU*k2m#>Wc4Sabv+vslLN2Z(2`Moaw{J%vd{!c)8bz0OCUl*8=CWre7o2 z8a_+4ndfMRfnSbKFv4iFKWpQf+N7juUBrjNf`Izvk&YS+8YGuYJ>=Kw_`}3%?8cAr zGhvRhx3YL|+E~~O&9b}^^jQs-v0#1bv>jqg>2jgR6bYZ=mYHzEDN8;-RJGdebth0S zWxhs%pl$Kc@@5+4SExS$Hh3X-@Zaz@>qnvUb>yf5i;tvjGOgUBH#?n9V-f@3ewhUKVUETbU=oc#L0=Z~4O~kRv*d$N zp9Nwl-3UW>hB!Sh{2Ux3*+@{WPGASd8&uIN%^QBfAzJ?|nzMlSPR2JEfHSea*-9;W zS7w*hWN+N)km49P;-=O_N})+v>fF0~m|tFFay#7&cJ6c8AJ!KCyf?_Pif|&|T(N$3 ziZ837_1tA&QYLS2F%Ub4&u?hCSTXddRz@;=$O1R-u9V-%DjFRts;od z7)RnnmxMiDwHFBT3-N5zDnVR-j$T9+xp1CgRNcXQ{BBfj|>vH~MVB$9LrY zV$G*{WH2(5=b*GXnzGAXO@OFA3f&eq^fX0n@&oo+d>Qvuzcxoxg$3q&#c*;6&!4FWLX(h zEF`R;bVDLH<6jkr;--$nu0$f=+II%>X=YgK0~gwj9tH$WEubhW`kM|_jm#~G&!3&0 zRI2wQ*p(vTV-kJC{1xizJugQJb8&xCM zspPG8xYA|X$29nnsyvzGCmRijz}oWTOoFNxw=wN-ATjI1)dSxoT&=K%&x#DBVd9`V zqo7OegW9@+dkHT!PuWuNbIH$e!3JUmnJKJDR4#S;U~cahhFe-D`jQCreG6cRT@SXpV0XRr ztp+wd%PMh2Wu|KR5$2)OU7;C+Wx6oy$?n`Ni>f;tRv#YQYJIyc!p7%r(o2iDJ;>}g zdudVW768{*mQ7&8Ol>Y(sgKWz8^FcUzX61h4U+*?8aX}XAZ95QWhFXB{Ug9mzW}N%_vYl1UgYv zvlM7iN}^2xBbYJ^Nn;mLQd|FXpyDS$SqvKi0`m*mtn9NWNFd?wx~HV#E4vCWv%(~q z&qcugP39F+=Xq1`sc6NPR31>VKWwVKD0oj^%y+~&(S{Mx_kk|aUZaXl&*T3zrQ(71eXhkurW&jA@=ap3DIh5?XoUY%qGII^qxx`m~h4+v~d*Lw>~I^?Zw(oRbCG}C}P^Q zP!3RQZZXJN5wkiE zQ{Q_9^7+(Wh*txw6_JRl9FH)sLJ{Rr_X+ei_()lw_Rht?>|%ZNA|Z& zm)KqPyNmnANd|La-$_)%P1lG>Cq0QP*GA#nYuDuVp(gDDgWo(yp|`wSAKb;_KocV+sl z12^?a+8`}@3*4x*jkz7G4pr$hEXWtdZk10+n6sjRjJUKwv=G}wyiMZ(NJ>Q~x0@no z5i|0y?a&RvgETD>HbF0;2d1#UN?ux(?$hRwfiityV5(g-wy&XuelGLo=XRgow{*jt z%h+#Y^~&Eq7eo~7Em;=+r7O!-YsKIFEzhs<9bR1dcJ9f&(InlooB`qq=1#<)!lteH zXVD*=a3irR??m2QihESRi!+WMnaZ-6ff)vXfx$um3Zj8gI)A4X&=p(hfMIKAZY3K7 zOR1rWKBTj)T;CnZoEViH{e{DV2HsA`A?Ilf4O!rv^E7Y^%T(d>$Rd(wP02rtHquSC zEsuynNkMwc{I>}W!jap3RxHjfn{L{I2Aa&ij94hVz{hP!H!$lJzp*Pcc`pwIR@yEU2ni+2>$I)qnPv~8F);3iIq2UL4i|-gVEl1jq6BunRtF7|F*8^^@M??b(chRsb=_Ybf?MA3CpJ5sQ@G#`G^J!V92$2Te2Y=r zB|jPHX-F0@_+$7U;BqC%eo8k%s}K|Et|rzm%?2b%oV4pA8>>@hyH9)FaQDmvXgz41 znofZc>1VJSU(hCu2%mS_0kf8rQ`1g*-&v7tf?0XF;hhhlXE-*q6kAF{nRA!BhFH7g zX@&9Isg2%N_d+sr%|B#*S6iB@ZT@x+dWliSC|AgECve-DL*i)HiaKeh@D!p<4YS_2c!0+6st; zOQdF)QbO_<704aIG^DMeLUJQI{=C{MbX<0W3$ zy^;a!ilH&cT*iyjY4*L*xg~42gc+F8TjF@{q8n@8p$en@6N-#36GJZDHl9; z?_%}t6gD2?rKZXf9wsityIu~n2F`iCGC{M7vnJ5^iAyD2af*y*o^Ay{-c%Wn6Y7B? zU%Ub80MH`A8SP^hRp36Ff9a|4#XV4jiB&x-BO6WVsuQ$cO%5(uQU)zuCjb=gJPVm0 zXRe`OCYY*<#cyCQnQS(}Ac9zn~Q zH@LITE0OLKFVjFw_!+vrle_(0gZ<5O4&k~%i z5>*jv*3|TXBNH-*;~Z0QoPcbQ$x5>|>+vi4jRzA>_khY}(9aS{7w#$z-h=zXgm-Ny zH>gq5_LkRwT#E52ZQYk1s_oK>iyiDigLSDPWU&xW6S8B#6&^PoVKZ+k&8k85>@V!g zRYVl|nVY<_aBdz}YMp#>M;_2C4o|iFaP(Na z=|6DrA+I`um&mI|Tk{UcZ5SQKZYrKDj$qKP5am|-R~l?gr=BiH&cpskVijMlaNOZw z4K$c#)n*CmbS9CE?@BB9B=f`19)=ulVna0t-a)T2w9I2rGOvf_k(vEv9s9CCblYC$ zEanpWOB5Qyp?}HyU+MP zeZ4?LR~|#GFumcZs;8J$CGKXt4h!Tu*sEnQ^fHJ9z9Y+P%P$r7Dt+heOC+ zn^CC5-4aBR25^YgRvuYXA-)SiW<(~M{FPQuB{)zzp;Z`k1%#|6C43oFgCpLPP0Peb zcjnDxda3k|6*!JdT*`>}cw1T4*NIU*nlRJ5X~mNl-Z%sL*z3%#7s#e~-=nwHeCrAY zb0KfjDvM_iak@*;71=|C-YIFDSXQkTfmQq5VYOK#bC3wF?s$H(-pG^uh{e{e*a0y| z8kw@R_d-K|6GTqNkxUVYH<1>y2&)s^P=Xo^ zsumnyo-m5BJ$7G4NjIY7n2;qL;>=>_2C1_aVa@0h1P4`_Gk!s9Fd7w?#fDtr!(8&Z z_fsLk#hrSErOA6ei)|#sJ$}r_D5XnSExKa#ry8e9kFu5Qq^zh=MUHz1r8GQ5w~nD0EP^|1{G&b8?C8iR>@bhlDz==P}??wk=?~9MD~YM z+{I@d%XO<6`lLZ3sK~7~jlE46V7v~Hyn$FIDA@VTz=#tYzTNuYRS#_LY`M}`X5Jsq zcmzUmFxjT4;-grl=H*3i-8i{?(EC^(P1WJ@AmZyht{X*DRT1Q{7 zo!Z?Gx3+)$XVGEB6c;W~i>_wBrVYIsv$F2smAu?cw%hmViTxdk;S1++}$bHq8yw2Bw|Nt4X@%xbKxt$aW21XCU$seOZ zP0D{3W!^jKY$>O?OzBvmog!K^ruv+>yFRDO~45 z;w)&mS&6Qg$U^&dwux1$S<&uV_y|ztv)ZmF@@1^8z?4_mqKd9QR2~e4S<4g?5;lwZ z%d{!b2m|a*;NzA|S0->d)5PoMF(xd=#PZyGR+3yq4WU_iR)F7 zCWtWg#C10ddSQh7vdw)$KA0LJJ#a-2W3lrGYre%s*@)}If1Y`M(sA-ae19dE+07av z3Yg-9?r;?_`6!CIC@6ZkZM3K(&}~nn(Y1JXh@~PLshqWqmIwyt#l&c<%Xh@)JpB@$ zYjbPAPtfg2nxq*wMpG6zCQfmRsnbrB1C#01Sj{qtmGyOl$$1sLG9IU(RNP+a zRQ$k$P_9k6@p0wjg0jGyzK^OJ8K6p3nIa20)b2Q0IC^iwf3^a<{SJQZd3qFk*@Faf zUw!_LABIWkvFkY%xut`#y~Bwx24oqV;8Z}k5v4tFY;NvgU`0q_O28`Bo{_$*ohy)a zYlQVoKc+Y9iAeE_GEL#ZS9Ldm)F%AhjxbRd$AW9BK#r`}ov4`ZAE@k~Hpy*s(7k=b zQ#LiHJD=W-8%kpqNGPBdR$^RDh;sDOf9=gz+u$t^6xj7PkKzlNieJ?BgKGN*Wn+fJ zDxaL$_xI={;ppn+%A{1jjbeiie(Qj%gMN}Ip@!}9-d^o%NeN;6%t_9r(b79$sG6bt z&3|iR)qiKI)&KeoGZ_zCB}oA|ia3khh4H+>)LS1XXd4O%1wL#3N((fy(%Uzq#D+Q- zEkQq5LXLq82;e(*qcu!XFG$E82%90Vp2k2PJrWXM9V2i`D+k(eJo9Jwh-f48C~hbm zQ&eZMk#v0M`q*w47?Oz&dEUZ+0v$}60>TN$TGdm3W=|55e?pB;5UYs2)y0CeTN(b!& zq|op`t&GH)0MwsZ5|dwX#GnDUu_Su1*LBl0zg1Qt3AU6mKz=?-93<29S@Gr6I6sm5 zSOdCQ9Q8C;VVFxQ`9d`5UCpmfa+Z63PX&VmMwQWo_5D6c=4S;jMuRn!CGY2yZ^k18 ztN>Vy_)Rj``i-_#;8w|Xk_^5|i2p2d_zcXWVqc7*eqYFo#D_nFJq48NZuJj5O5IVNsJa9+Y`{{&A^l-c=m&2#(^r;_$i)xTY(WT;o_^9PvlUwweckp zn#gZHW{dWQ9|sYUcm3UE-7@fxX;h3sVQA(;eX0-dxnK@^Tf`Jt12$z{J=N%C-1B&d zHNpn37$y0@2C%=y;gXz0!&D^;3!ytcgK2(Cq^)o=vItZyR#SE;th+Sr+x-+_Ue|e~ z6VEjXy&Kh*&RStAY-afhjXex&sw9g=I70#9pioV5FiQ~17e;R(^a$wy%ul^MW^1#+ zuPEz5V`!qogX=*`mU!;z)zr}K=buj6|Msvv;giEf&b6|IojD<{=SS#UD2)^=(TVcr zJ8$g5{4k2*4-gKB7{DfmM}j+^S)H>u5KPn#1HN_X7r2!#?A4O~3j6Vi(cZwpR|7s> z)tHE_Q+c-PS;cJd8o+NjZku&~nLk1J6>L{Od|e7Lg6#Op2fJ5vLMXjaWJW!J$0qxO zeEarUTt9@!cH;MVADZ9#JiD>l*GZsAX?;ufKM{b@z-mr1Rmqpls5!?*nlV4(Xp=J< z$ix-pjWcIYTvZs#iojk!>O~yp@{a_vDOsP*B>g4&8#*xv7EEqsJcEzIrx{5iO+$D^now zfZ50Qy1*2wilmKOZZWNk|E86HF2zg#Su~ac@#hE7eyAAp02)fbEH1=sbb}8_p;eP< zx&&*# zX;EW^zGCOKbmB=u59}ox+(U}a2k&ME-b>6h?IfHS+Ttvz6nnWjwvbNG0O*tzWs93L ze>YF-q4G+ibO&&c_Ko4?_j+*X{duX^bgVcY8KMe2#OHaMQG@k<=c~{aofXG2D_R#s zcBXmHB{fVmM1Rg+!i4n*(`R|aK4(;wC1UY{o_c-+mBiHAdu&3lnzvfQQtC2+v|I`; zhq*RCcMuzW)4@R;G@3cR;I-ZGh+;L9hjE5q5TA|;r>}zCh2Yf$W3~_aB;4!`@AJ86 zZl-zmZ>ziYp8GrB>A5}{(9+EdyDkcqet?gFAfpbKi2b>Yq;!sN5N&y&ZGf8(I{VF} zFn$E2L9nFMMuN552Kb&Z7C)|{K>A$yIM6u!O#e@0bj7D^J!I8lOKy5{=qDMKZ*{_b z``4u6e6&20pz+kn_lPfjkIZLhryk9zWPe^PFHXDi)|D3?5kKGDzt&Z9x4vUCqvY(F z?3{ZcX}Gsn2qc@@wUct~`TC^np2ktVmc|kO1mSnsMk;Spcn$TjRw^!uzFQP0=ejUx z+1PwDA*M#A(`OJAxwmlI)Xe*1Pc`k1x@OO;vle2eSOM=wFeD+5 z7E@u&%QIW;z>_^D)XF6s(nA#xRFe&6pwDTZAclOI;(J$E-xyQklhEzfl)V9@sbzkE3Se~z;tZ*~(P{37f>8fGMOT?3O*>}N!LY%GJ`V7X ze)I^+rpn?9NuI(n*E(eV*pI(S}_=GZ)`1`ma z2BB~T$#V!~T&VWt<|oYcZjcXQ&%L6k8C{UI;Lrs2@IWX4_lD9$@R_&D9H3wI4St$( z`~Y-)0y96R(x?U281?!&SY8Wg3Dkf_HvNh@Q(;%$^?3$-oghN%?Ipk9vM(Res{Efx3Gvz0WfN1g7(-$qPR)@80Ucts-%4hD`xNE;vtvCs)0ezM>V{VoI@!oKc|Fh&>iru?T<)IitAoyAM1eSk z1{U9kmx5)Dh~TC!uMi8Xn3hhlJMag5mAe-z!{hEgP!9mKy81SZHMGZMu~L)L%-kBl z>^(n3ja2w%2oP+kPeO&Ig7vXkrVnCRf7pau5yb|oS_e*I991WuErq>!D{FMZ`F7zG zL|w&;lm}X7P+!0Tj$=DDU;lqi@)R(ZD0Zgu@wHE^waH(nne4&uwAml)L7Y_BC=2t= z7{$uSIX`o*tg4b&AJROMpX&GLjKtmj^k1%hy8*j+3dt2I{TptD`G)e{EsKZpy{e*S9xkhC3$gSyld8qQJ`DyqsvtOpe4 z1!7KgMc|vdJp4Ovb>C2{X8oSlb$b)ML?_2VcDWY~=E%I=wg~>ffL$u^1Kh?kGi4-D zjk_sqi#n_{gLzN1Rv(pG?)LmltC2svm3kE?FPY_pK*gz3!$L4%s-YbB5LZBZ2s#4> zHp=&fk1h?U4DP**KQ4ulWl4QEou;R94pFAMgLb&|2Oe+ZCTH7y?&D^W}tPHfFwL zb+UScc+PdKM@-R4T9gQgRo8Y^oaYPIk_bmd(qv^aCRsmfsnnMG4%pwJ_8b2!TDUQ* z%lw)+2vq^fVCe4@Pz9Aq?Mp-o_F8wCdFh`;(VSRQb^?kO{^as40s8zE%Y*uCP;>fG zkWAm1!t^meWL1|k$7oS`fBdBJn}CweupSypXNnw&k7=T_^e6I-;qS>;J1R60YuAZJ zWjWVxIY##!TS#88Vp$MNhZiU|a=?s(HR%Kgut!cYuE6#eEbEPhcC;T?JR;=9VR1Fk zPM?p+TQ=9ThwNbt2rQ!7!yt>K`--z%mZm-}&}5@aJ~eb%XGM^mY-o@+He*e84fT;~ zs;$b_^FN7bW6;Z$VF+SM`R=}08HcW&U@QZk4>4W|d!71c(Rm^;xl2OX04N~^>ozJE zuptXx(r(cfk~u$Oq`55kUfJ+&uxMXUfSZSZ1`t0(pPXz-ix;*3N?zfbX_+V8_h%)^ z%NErm#SyoV9&)C)Tui+)G9jIzm6g(fCC(V2QSKXZtIYCD^I!Tz+R@)0KVBDBgdBwp zcn#IW?#4n~kCam-As=hyKG?fMyvg755C?n@kd8)}u~`LcU?Fj#KYQ=gb8PPcwAH%fAlVnVExW;B+)^j7ohuB~a_}lpcnC5p@{j zB;r(aR=U8r<6ZN?ML9W^-ed8HjrEV)KrcC7Gu-1nm>K>oIzo9?D!)-&U?t?z1b6tW z&!ehm)xKaJ>+EU2U2)+Z*}6=GeNFd%C+TZoG)|uj;CvjL|Ss(X0RxEi#?CzixAwvi6jr2MU;U0xb#jnz`x2OFfRJsaR3myDw3m`PL_rp;q* zzKJerLpk|AWw)swGHg2(@D~7{VU;?X;@gS*FeBqRKj+h3d-aecvmiUGyU>?5s<+N< zhpEbZy38E};MRGU!G@`=@xzs+c{7|MWPvzLz6{Q!K$&U+!MdQ7w7tmqNpF2BxB0GD zYMnc_(|BJzR50iWd>VOJVl{T$)#yIH(YUpV1~9tLDYO~Vta~$>FP6D3QY>eKo|wN*cGt1 z-QBik@4Zu|Y&4vrMpAZ*iPNpfs^<5A%A#ZgmERWL6_!ersTQZmt!H5kdaPVPy+$pV zvD1NN?D|2?@*BnR`rOwEH>?dMr*>|L7i(0VZ&!PIE_>5l?4`>c4xj!GoAL_~P@4~Y z^NoTtM+ysTJk$65MYVN~czI$&t5f*-;{L|MM1l9&6C31<<^TFAY;|q~f-z!m4Z2w< z^(bozD5o3O4r0~4U-S{iN`SLX%4V*IjL}}`Bf$wysJ*ANTBJmC-e0Nm1Y@ABJ3GH~ zCa1Z_2WRGC10;yj83YxpO|Q!(y+39Wa>`5KVpKqbWuNe+G%05kq+@;H$q_Nm(=5ei zVCGAKwMV=}j*tqZQS4lDvjJB?d<|y%Q->3rN~to5f$~m~A5AL7l@j7`%h9!PIGQkHMxt_SU=W*?bxpV5#<<_9?3VyToC<8qY=wb zfI42}tA+T%0QQ z;Hnyo$UcV(>pM?pztdh`$A{00RY$w5cJIiVxpi8=$`ZGH5zej=S3uM3`FX+t@C{II z82a0NGuY;@C=G+#U}E0NP$Rb}X;hS{_l|wN*tap{6teRr}^(v9Pe4KodXVF!C(=uB zO$MoTJMA~Ov&YYpYG8lUxR;U5&qBH$Fre%6V71~TF#7E(Lh>yyMPbv=HVYG?O z@IYoXHMhWw(*%J9hgb;z=hMRIB@A`N4$)XWvAdpK#n5l#b}A0?!VdTvovy#EV^SXJ z6*e(Tk-$^DEpo}wOmZ&SOBor6Sq=NvnY*tN>q(uw>)%RX`dr#O!_fQ$h;C}ms={@Jgo+)i)O z!oX$)SK#uXWep9)UKT&7cI>iMLve9M=q8YWcvvmuGsU&`O51jUhC@5I*g5`q$#F*s zE*VtpD*eE*fX{H1f1Mjsadgp`!guDp&$qJZP~Up!l+< z8x$LCP}QnHM4KI(F-jOK+Nns84CQ=xwqn@<~pCW@%4wAJu4x?-1j`Nyre>tbwN z>dc%nE)MkEQzdV1Df`kvnkm1|9*@ zO*Y8W-@8mo69_(oCq9=(v>gxPjIq#<(8B;L=N3R3pIhnl4KA1b`scewor1tY5 zgELoXIt(!t8U(%@^**}&GCU)6VYcx*Tj#=d9L}pfU@OjI=}kz`5p*P2v#Rnsc|URt z#>xo;eKpxf!TJj{0RoWv0Koo?k6T5y5Jiku@>e?_YnjuwHXk5NY1;XEfmu@JifF>h zFY>GYwoN?wclzLumsmn#G49Zi<=z{2HY*_4gfPKG+sJ||FO*gv0iialg>t<60bfSXbE?J^ur;BVB zbdOIk%W3?OQo!tXVz((@;BIo%CbwLeXG>3KzpJ^P)2*VcQx-59zCmH;LK^|3%ash6 zcGi67SbjwY8EwD|zn)O+xzj{rV;TE2GweC+yhtRD#>8 zQ;tiSE%p4^li|bd3%pWF?{jmVCA^424mUV`_Kbjaiq*`XZhshI;CS>~+dcA!n5o0o zWm!V=bT7d?nJ=D_&MK23Pa0M81&S4C-6v~`!CVn;n$-7SW&m!XxC|5b!@UuoNV#b=( zwlEID1^6i0pM5@>XTg6jO0!3DqyfqpbRGJY_})>(;>sc_)|nNq^-;^btarfek;IG$ z$-OG40E?sUyADq0F=L7@L8PRVr;;qi>Om%sZ{&X}^B7OshEzUZU@noQnXoMyb=9a- z>b{CS5$?n}uFWvQ-Z?CRl<02s5-Yh~qZ@Ng!~gOq*O{Nzz_!{xQPHd zAw-o0#aJDDSM&a6S)nC2F%)VyM~4BDyvt0L%eAEHN~XdQ(SVU3#ongG6_cmKFSKcc z30b=^vt5{(Qz{U@9us#TQ{LgcS)|1Wc$4XE9A!q?EsD$5NzWW4$3sh{M8%)qX5vH_BU(4)VWa(M* zuY)P~)+csS!6H12yM(eig(~%-#*di4@LJJL3d;_Yj50QMSN}jd$5r!L>vS&HMzT&( zh=8f);Jes`DTR5iVmJ8iX0*D@x}+8#P1#W86~{UqaCJ3o@dH}^#W`)LcuOm zd`5Y?LXe_dQ2q-nwl6Lgi*Z;fi&Im-oF(J}ZiFRmV_3n!43@Hg7LS*yfL|_iArZ;0IgO3JpP?5Yfivk89988s08v#2ZhaoW`*zv#~e#g~jSE1sI3{;pit zI`7~IITSOCAZ+jK(lqc>X#iB+&1H{iDl{R!*ApJxgzQpm5*b9#3&E2dH5fZ;{6p_# z5UlfR?`>rLf|VUZ=}Lp|!0(?@kr_IpYw&QBSWD`!jIcT=R^gV!fCOU&hjhLnzTz3~H_{zVS();>qvnV!+# z5%Bh5-7qRzn~KVJ-ouDaF&|?#Fm_OC8LAApkDDr7NwzO|FsF3aYEHY(uNMAtrOcpT z(z*_K5lHeQ+DEXQE;xoblES5l%6KcBcx3}+DI2?<^l(;{Sv048TczZ4`=CldQ1>|X zlVfW8cl_u1SzzOw7ZT@8A9`P5iPt-B{f&j;6#dzQtNA+0=woBm$-oajOr(6X zxE(Fls_A0v6)6>evBEq|Bvm7WzzjGMtzch3d@5m!ow~!;$DF(NbH+U?(wGD=EvB~j zc5WL~ZJcAf6bxA_kb!Eoej+h$85>aQq zNBf+k%IU~1{*}^M)Yc=yEVvzNx5IIQ4FR-??C8RI3t?SW3}c^f+LBJ}mO)Z_wfuf@ zt1zF@HL(;KDoUl)D*NikCy+vL14S17%2%LVNLv9oXJ+5QD$Z7`;ILd$J!Zwky50$$ z&81~_3Tq}gekUuTU<~08b3WnsStC=b&022;>Nl#motDLT+BfMr3O7v zXk?O2M3Jel#?97xyI`p+TzdPC(#lJY)1v_gIk>MA8lplXn@Zm^FwQhC3CN@vslLlI z{jVj2eF~;;e>ghPT4fWwj-zD3%R=bWyNO~IX&%@Ijy~)`)vhAEs={6j-S-Qu#0rn5 zIoP$ILG}hwjvHfy8sDLQh<(_wp6k9fE73zRpJR9!*N_-&WIQmH$!e>(D^AdCNM@Q^ z)+{?wWUofIIp?r20(JrvI-o_hioX4#R8=jDFvws8GG!};#Jxz zV00>z#xiEZ-V1X5ZmC?3yWLX_0&a z3Oi=D2PpfLnYbWaBZwfaZ3WDsWWr_z*xbv!eqI>t!Pqt-W$bbz*g~sF_0=HdqTQ@0 zQhsm4$#fi}14qN=B)VXUAG&~(w3-*fJ8hz{1|;YloNf@qQ0w>E{hnm*$c(z)5W!U< z1G-=Y!6*!MqQdWXm?oPbdKT09Bkav4d2;grDE5>tDFH2^Pp!`K?dJYK8YN4P2DRVG-=w=IQd-IpK$1Xle{-U%Y*!NB2XQ#!wOY~y5(KeLm7MRR? zGzcH}W<9ZT4Aald^VJA0z**{|f}6CeEvxnzcwEF7C*o|17FYGwit5Vd^_N zuN(kc^Zf>*9RZ6VxtO7)FlRGCjr2`L@dw^02?%uh$K;s&^P$HW;DI{AJ>xulpYHg|^F=`gZZH-3GwTGx`0;jLp}_j*tgOWCwI*IABwW zv`_BI07M*}6;>PxIDbZbx=gpb_s1(I;vEo>&d;2D`4>bk#bQ9L{23*;-URN!p81F% ziih(pwLSSkPx5r#JkMOQ+ZcVD0tt|d=s5p7v~Y9W;>}J<-2eX%Q9MO@lwHKQZX(~c zWMLj4tSH=2ue1$U%&?;T2w9cj`jrLoXiF!gNQzFrtax3PB{Y_y+AcJZsSNo+P$Jv_ zOaM5e@T(@o-XD(sJeC0Q%p!U5&L{6HqOO{Dbu+I!a95~@t6J7B|7TIoGZbww-nj_E zGC#4VefjtvHQuTa;iGwcL2l|fY4sI5uWXx+j7G)Z?mYS106UZHNs92pN@ ze=bNqv}?RS+^OOq&lP}Y--~5$6QRJjlNS%D=H>gnC3%(HQQVyRDhG}4uTM~}Q|bt! zE5#pT*fn5kmS<>$TeeE{-k}9C7^L_cXz|4;#fm_=MZCkU^!lB z0lGXSgws5qF}B566wXW7s{&Bu1BRgwr(5QZg@cxT$LcVdm1$j$*7}ZABq_iK zM45UjaDB@Hm__s&00fJj1xlV;6b`6ww;$&gizArEx#A;78uO;}5Uc0^B&FV4fy>7zv z2ewz93}ioiOCn_d={2qp7LAN^`0ZwK^%Z4s5C@jU{j%lUlnaNBuuV~B%$3CD6NcLQNgVaPU+45k@MY#jUOuP8 z0#!8um`K1@a~BwMhu2&;-y2Ml^;U_~Ys&tlBdBW%b%=JA#jaEFhSK4t3?y`Oti^q;P1|?`X6`2OvJI0mGmcc1t>%KsfGr;Dx zTNzqth6%rA8B!%}B&k%9F=Zcx@#32D9R zEa~tU??BAp7ef*x1E>CJfS@cN;>ooiUjy{O5e4w!$IGW!;j_QV-l}->btu7bnhw zNuXDZUf!>EY>+$sN0Bb49Pj2yEPY$j?~|o=!RKD3tzY6F#C6^K2;cy{#dq*Jl@9PI zEJEywplT2flbwB6YU@B==AixTLrxL53N!J6c$%9(SqnUBRK{#0T(o*qRal*KsX_<9 z%|3M@?4mkj1sU+PsgG=z1QVA&XsVj(V5u3u#qp=jz4%RW?-8Lp_dnR;c0RwGo@#Y3 zbN%Oh1TdmqJHP|{1X@bbN$e()*mN07Kk>n|by)IgvIPeCnA|l?=}!b{8&s{@FvOdU zd>DiTt@`tf6Bie*Qz|SC7v9%p(p2%wK4J}Z8;7}3Xdp#%fu}tJ$GXUqJp9Le8{|UOYb#9T*_zgm=|u2@*pgY zdT~61RI~yqlQU$vkMJpRA(h9Pgl))rqf+d2*ZQg|c z;(G(+P7xDNX`%q~q@nk?m<%X`_G{~lL#D-0$qbBo7)J7Of4M2)(Lb98^$1BV&;OUU zy?@$*^G|*cfstZGAm~{@=fLvcV4_k(b8G-LB!IE2_*_tr?A(3jZzndmpJ!mJYl3X? zo}Y^1x||jOAVBSHA`!Wr6v;pz_pA@jYcC4DIB%Wp5g+t|>Zi7*K4p$VGkh@}XU{Ht zsNP&)|6UAJf&@*VqKPgmGW7@$dyw!nXsH`Tg7FmZTyER~m@q;$v31b?yD1-Lr|ulA z92mFFWY2I+v+EV6%`>f!VXD48{sbarg;xiDtin4?iXctkXiSgUCumK}Fa}|m;P8>D zRHb3qA8PNAPBW`PrSTF^(vDQIV;%+x+?Iweu$|Fl!~w(|1WZ4gs1XXg$^5fWSfXOg z#L9{j9I9!5vNM-e7stn5*tiQUaUBY1Cb{*ak9nRBb!QRHDLQY$7C}t&11SI}7)Kx? zq8D)VKq3LH14ZZ}g+pV^Sc);6jSt}5K-z$@lYjBKXlyp%dv+dIF94B-Lk!l47D%^4poB8Nh#-nD@^uV-m2!tmT=xMmhG90 zbU>+t{{(^bT+n?o125Q2m8?lysa1WLSd3xn46E}lRCD74h5y+FR*XG>I{y_V=rQY) zO-;@xbJ^@~kGIL}_6#u1Uu6P8-gzgOC1d{WZ8GorclNrLHcxPmA!buD68`9Kk|Fve zrtuR`wy*gwNd6P(t<$@oX;44scJSx0(oam6#uNsO26sdz(&YD2SWq5ha|z44$w9zC z3V&4};0_KD5WdATLJz^j5boi^YAjs5PJ`xb$C@M=gT)}&e*u0i7F%J%k;%NlI(~Bw zxhKeu&bEZxEaB}W!`FCIh=;XoVuz{dG~eqY%==L z0!a#G_W&OnfBljHgL1Zggj75WErPL9IJ6 zZ50_V`Xy&lO;&R4-T2UOOrr|eF~i6>8q>B$XXPNu*xH=#*px+jn3?IoyG6j`V60i+ z$*BwA`0_^nti0q!UIqz>D23CL7sZ*Wv__-|N&^SETAC34V7_JJFW=5FT*xXVdYwGg z@Qd%_#-9JEp8Xy7zx!Td&-mGBBg8>5jcCy*KYD{;OZKkSB`hEUq^?r!mXm}^N`QR3 zFSiCU(M^a#%C2Zu^hBYL4iF{h*Q&C(>4CBXK@PKj#VxDHoL!hP$ornYxr^-r+)OR3 z0=IbIU$a6hIwV)`E1rp{Gz1ye(UbcYrv4Urv$WQ}V-K(SXdW2KcBOx&kMO3lHqI(M zKIC4;F^T+EGUUAaE|}Yi?gbMYWEznp8@-MUkEWiT5XCKV(iv}M8Ew^Z{}*4lTdU#s zH6A&Rv$dTO=z$G;Fpw+t@)0a4I=-oO^zerP(nmbFZ)N5)RqXS2V_1iqr5$gPZiqgy2|_jMbGQP zVzT0BRlo~8Litu9)PeE|NMEmqnO19B2 z)6`~Uv`G&Tnil6+58doPl`{vr06d|c%>OF*3dy4)nqug9EtJ`B1-_1&+7(2B_H;KRlvpm;xqFt=>X0- z$`5$S9*MFXOl0=A@H!Wt8!iN1LhVI92+f$Wsu{6V%r$3s4v3=Pg$nTYZgB~yJFW^N zk&qMJQem1tfHGUPz^+7O2=}k*uB!v7@6!h+mnzFqzeDTd!&_s+p@C#vIH=JE8jhh) z+6qn>1!2XAvQXNjc${mmp*np$utjd5k=u>UAL@z-)?%l$Cd{r9=F2y>f$9~6E1=Te z?VfaC5@LyJ-WjQ3k7rv9I@;wCYzGSUk{EW;A{iQ_Vs~Ra5d6IETe2=PAe$P6(wca6 zM9lhaOaa+d7_n^jI1-Ub8*1Z;8gdIsUh(ngRu0Z>AsR`0J10A9So^7ezI7e$X!8(5 zA{H+LoRqv6vM2*iHI?WtSZPB`E}r3{HaQ84$AjilGEqDQ)^^3LHw!~1P<87Tzz@iXtA|Ldim|B-yOv`}K~eA_`okP;o{ z1lofmu(ZhZ3sFK{(Wgn0Jy+XU>fdLj*-0b91>w--TwJo+?`-^_;;HTj-^KW+lqR$J z%1s^lOMg1m{+LVYk4hp&#g!WGl1ADNECt05l$79?t|jg7P&T%y^vb9z^}er4S^bUG z{y8-Ep1%eA)5OGVr00(D{{g}4f3ynQ_YVbuYro(|b_`IfdgOzqZl zkI=t7s=W%ohVV}9OjtFkc(i9eF{(Jog86l1cbib}L zzl_b@upXyAeKY@X%D_1^cS9p2`<%QN?8tPc`;OM6v8iK(X{n9RgCvhvfRkX#*7k(+ zLyvYhu<{#!8Wa6fG?w- z|Av`(O*Mdu{8;a=LfTCC9`l-BI`eW)AmK#fMu~qSW6bPc-Aj=wv*;o5!$&tZCnT&+ zpo>CDt#USG+8*y48>Dw$wMsF6kF`|hPwID|Y+KHB-!#8Pn*d6Ab` ze}`@bQ-rLpeqFb4n6#$&PM6lj{>YHE%`e(|zt30!^NBVxOE+IKdQfzMImieh(_;Vl z-XT9POvmmUp6|L_=D#n?v_5${dWdc1dd^w4@!c;zF@$eV)Gxk+JXKn#b`wIHCUjh4te8-tab6^LdhG-w<&AZcN#(Ts^~n1^n~y;}xP%_mM=(u#K={~r!NkSu z9!~S5?qg}i;FW6wiO6JEdXHbHrN3i8&FYw#SCD0 z#A#J1s*`I=3OBNeuY-?8%j4nf?$X>D$3G5O+)d9mziT1x+qQz^gAFFqmdcs#V zGg!tf`5^jGMW2(nNIN4(nv$^mIs;ZFi&{DL{IQI?h4N(fDs5zRdoF+`j||t!A(Nzt zryvg#mUQuZQA&!JbT_{6dYzf7ol(O}dn+;9_X39t4`s5+h#n+IN{}IjXPaUgFCo0O zM$Zh**FMEH(nO&;Ml@D&@v{kS>_V_orW@Q!qDUcCdf$}#&^Lid=>}Jxs3z7-w^G>C#Qa4?t`ufiY^=%)gZm(*8AE7PD zFxg~-rS8lkVDzbOQzk9t(+nvK0Ue(u+lYa^xDSXgfp7rT z(I2)=&_v?#8m$YiUb0}aoKK)(zxw z>KJ&v3*fFHKd`bVTMh+p=W0nU8dltkR!e3+!-VBbGh%FatBJ{ht2c}9*cV8lnyBdc0d*K$ZP*QR83358A7kR$nC0m<<>y5eB!?I zs=le}^OtwO`k%Aq=PKD5!$g760Z2WOPTj?QjhVP9aBZctW#m%TV?w zq8>LDt>=@CX|du;U96#eKyaKz1q1FtZ>EI(ywgGJWTSYR_a;hQsnpfIFR~& z4x$2EDv_4%KfaCzo92rjOLrm$hP$Q;iXtu21^ z7thYB{i&SNv;jPxlQ?uJCK0X#1kFhI&LLwkVjLI7O01GVd8P%+A)StwTX3vjt2jDp zSDo>DoZfSDQAW=}cYepEJ=L;y$ld}lUu_922Mri^u=q~UJ%BzHieXeeS?}2mWo|r< zFgN|SSmCGXr+uxgk21WN=cJP5U<$uiWv_O1@5MJBXA$6<<0Q|XSR~U|YpIAO-?ZHj zK@N~{Mj5d_RP|{Ezf!*(9`Ce_&owV^D63A%kDeK8F>1eOKNC4Q?U=|weA~63pVP_F z157g@8m$X<+L#wYfkfwLnMd(i7QfTj%a&?nxVSG5FkB`Z3qu z1<~2tS~JS62JB~0r@eI)tSCVTspv%#L92Mfg_0RZjAl$OOnDzq7<=GAhsCyApP+b?T?c`_JC6? zmRn1jnEb^j&jTmE51 zcg&7l(Ab6WCqQRFNdP2xNQ*EwoO3&8BZQ(tgn9!))L!2y_LjUc0zI@iDXErz=Wf>@ z1-Pd-RSw!cv;ESDxej)aGWQY5E5dc7m77%0&pLODd8>%|(~%YZLd!o$^n8`|-O+J= vLB1DLLPty*ojX$Wk5?M6Zyo*Dx8DB){_}r_GyVV9{QooizhVR8zlQ$}#1~Rc literal 0 HcmV?d00001 diff --git a/sd-card/html/common.js b/sd-card/html/common.js index 0bcfb732..4d42d056 100644 --- a/sd-card/html/common.js +++ b/sd-card/html/common.js @@ -7,7 +7,6 @@ var domainname_for_testing = ""; //var domainname_for_testing = "192.168.1.151"; - /* Returns the domainname with prepended protocol. Eg. http://watermeter.fritz.box or http://192.168.1.5 */ function getDomainname(){ @@ -37,12 +36,10 @@ function UpdatePage(_dosession = true){ window.location = zw; } } - - + function LoadHostname() { _domainname = getDomainname(); - var xhttp = new XMLHttpRequest(); xhttp.addEventListener('load', function(event) { if (xhttp.status >= 200 && xhttp.status < 300) { @@ -54,21 +51,16 @@ function LoadHostname() { console.warn(request.statusText, request.responseText); } }); - -// var xhttp = new XMLHttpRequest(); try { url = _domainname + '/info?type=Hostname'; xhttp.open("GET", url, true); xhttp.send(); - } catch (error) { -// alert("Loading Hostname failed"); } } - var fwVersion = ""; var webUiVersion = ""; @@ -126,7 +118,6 @@ function LoadWebUiVersion() { } } - function compareVersions() { if (fwVersion == "" || webUiVersion == "") { return; diff --git a/sd-card/html/edit_alignment.html b/sd-card/html/edit_alignment.html index 6445144a..a71ad3d4 100644 --- a/sd-card/html/edit_alignment.html +++ b/sd-card/html/edit_alignment.html @@ -291,7 +291,7 @@ return; } - var ret = MakeRefImageZW(refInfo[aktindex], enhanceCon, domainname); + var ret = MakeTempRefImage(refInfo[aktindex], enhanceCon, domainname); if (ret) { UpdateReference(); @@ -484,7 +484,7 @@ return; } - var ret = MakeRefImageZW(refInfo[aktindex], enhanceCon, domainname); + var ret = MakeTempRefImage(refInfo[aktindex], enhanceCon, domainname); if (ret) { UpdateReference(); diff --git a/sd-card/html/edit_analog.html b/sd-card/html/edit_analog.html index dba8c0a0..8b376e8b 100644 --- a/sd-card/html/edit_analog.html +++ b/sd-card/html/edit_analog.html @@ -25,7 +25,6 @@ - @@ -945,4 +944,4 @@ The following settings are only used for easier setup, they are not persi - + \ No newline at end of file diff --git a/sd-card/html/edit_config_template.html b/sd-card/html/edit_config_template.html index fc9e7ea8..6fac3ddc 100644 --- a/sd-card/html/edit_config_template.html +++ b/sd-card/html/edit_config_template.html @@ -208,6 +208,9 @@ + + + @@ -277,7 +280,7 @@
@@ -294,7 +297,20 @@ - + + + + + + + @@ -305,7 +321,18 @@ - + + + + + + + @@ -323,7 +350,7 @@ - + @@ -335,7 +362,7 @@ - + @@ -346,7 +373,7 @@ - + @@ -357,7 +384,7 @@ - + @@ -368,7 +395,7 @@ - + @@ -381,7 +408,7 @@ - + @@ -392,7 +419,7 @@ - + @@ -410,7 +437,7 @@ - + @@ -426,7 +453,7 @@ - + @@ -439,7 +466,7 @@ - + @@ -452,7 +479,7 @@ - + @@ -465,7 +492,7 @@ - + @@ -478,7 +505,7 @@ - + @@ -489,7 +516,7 @@ - + @@ -500,7 +527,7 @@ - + @@ -513,7 +540,7 @@ - + @@ -525,7 +552,7 @@ - + @@ -538,7 +565,7 @@ - + @@ -551,7 +578,7 @@ - + @@ -564,7 +591,7 @@ - + @@ -577,7 +604,7 @@ - + @@ -590,7 +617,7 @@ - + @@ -603,7 +630,7 @@ - + @@ -616,7 +643,7 @@ - + @@ -627,7 +654,7 @@ - + @@ -640,7 +667,7 @@ - + @@ -651,7 +678,7 @@ - + @@ -662,7 +689,7 @@ - + @@ -673,7 +700,7 @@ - + @@ -684,7 +711,7 @@ - + - + - + - + + + + + + + + + + + + + - + @@ -772,7 +823,7 @@ - + @@ -831,7 +882,7 @@ - + @@ -854,7 +905,7 @@ - + - + + + + + + + - - - - - - @@ -1092,7 +1129,7 @@ - + - + - + - + + + + + + + - + @@ -1494,7 +1542,7 @@ oninput="(!validity.rangeUnderflow||(value=1)) && (!validity.rangeOverflow||(value=20)) && (!validity.stepMismatch||(value=parseInt(this.value)));">Bits - + @@ -1502,7 +1550,7 @@ GPIO0 Enable MQTT - + @@ -1510,7 +1558,7 @@ GPIO0 Enable REST API - + @@ -1518,7 +1566,7 @@ GPIO0 Name - + @@ -1553,7 +1601,7 @@ - + @@ -1565,7 +1613,7 @@ oninput="(!validity.rangeUnderflow||(value=1)) && (!validity.rangeOverflow||(value=20)) && (!validity.stepMismatch||(value=parseInt(this.value)));">Bits - + @@ -1573,7 +1621,7 @@ GPIO1 Enable MQTT - + @@ -1581,7 +1629,7 @@ GPIO1 Enable REST API - + @@ -1589,7 +1637,7 @@ GPIO1 Name - + @@ -1624,7 +1672,7 @@ - + @@ -1636,7 +1684,7 @@ oninput="(!validity.rangeUnderflow||(value=1)) && (!validity.rangeOverflow||(value=20)) && (!validity.stepMismatch||(value=parseInt(this.value)));">Bits - + @@ -1644,7 +1692,7 @@ GPIO3 Enable MQTT - + @@ -1652,7 +1700,7 @@ GPIO3 Enable REST API - + @@ -1660,7 +1708,7 @@ GPIO3 Name - + @@ -1705,7 +1753,7 @@ - + @@ -1717,7 +1765,7 @@ oninput="(!validity.rangeUnderflow||(value=1)) && (!validity.rangeOverflow||(value=20)) && (!validity.stepMismatch||(value=parseInt(this.value)));">Bits - + @@ -1725,7 +1773,7 @@ GPIO4 Enable MQTT - + @@ -1733,7 +1781,7 @@ GPIO4 Enable REST API - + @@ -1741,7 +1789,7 @@ GPIO4 Name - + @@ -1779,7 +1827,7 @@ - + @@ -1791,7 +1839,7 @@ oninput="(!validity.rangeUnderflow||(value=1)) && (!validity.rangeOverflow||(value=20)) && (!validity.stepMismatch||(value=parseInt(this.value)));">Bits - + @@ -1799,7 +1847,7 @@ GPIO12 Enable MQTT - + @@ -1807,7 +1855,7 @@ GPIO12 Enable REST API - + @@ -1815,10 +1863,10 @@ GPIO12 Name - + - + @@ -1833,18 +1881,18 @@ - + - + @@ -1856,22 +1904,24 @@ - + + - + + @@ -1906,7 +1956,7 @@ - + @@ -1918,7 +1968,7 @@ oninput="(!validity.rangeUnderflow||(value=1)) && (!validity.rangeOverflow||(value=20)) && (!validity.stepMismatch||(value=parseInt(this.value)));">Bits - + @@ -1926,7 +1976,7 @@ GPIO13 Enable MQTT - + @@ -1934,7 +1984,7 @@ GPIO13 Enable REST API - + @@ -1942,7 +1992,7 @@ GPIO13 Name - + @@ -1951,7 +2001,7 @@ + +
  • +
    +
  • @@ -193,82 +199,114 @@ --> - + streamPopup.focus(); + } + + function init() { + LoadHostname(); + LoadFwVersion(); + LoadWebUiVersion(); + HA_send_discovery_visibility(); + if (getCookie("page") == "" || getCookie("page") == "reboot_page.html?v=$COMMIT_HASH") { + document.cookie = "page=overview.html?v=$COMMIT_HASH" + "; path=/"; + } + console.log("Loading page: " + getCookie("page")); + document.getElementById('maincontent').src = getCookie("page"); + + updateTime(); + } + init(); + - + \ No newline at end of file diff --git a/sd-card/html/info.html b/sd-card/html/info.html index ec17a1ba..8d027eb1 100644 --- a/sd-card/html/info.html +++ b/sd-card/html/info.html @@ -1,8 +1,8 @@ + Info -
    Hostname
    Fixed IPLeave emtpy if set by router (DHCP)
    GatewayLeave emtpy if set by router (DHCP)
    NetmaskLeave emtpy if set by router (DHCP)
    DNSLeave emtpy if set by router (DHCP)
    RSSI ThresholdWLAN Mesh Parameter: Threshold for RSSI value to check for start switching access point in a mesh system (if actual RSSI is lower). Possible values: -100 to 0, 0 = disabled - Value will be transfered to wlan.ini at next startup)
    - + $TOOLTIP_TakeImage_RawImagesLocation
    $TOOLTIP_TakeImage_RawImagesRetention
    + Save all files + + + $TOOLTIP_TakeImage_SaveAllFiles
    Wait Before Taking Picture $TOOLTIP_TakeImage_WaitBeforeTakingPicture
    + XclkFreqMhz + + + $TOOLTIP_TakeImage_CamXclkFreqMhz
    CamGainceiling $TOOLTIP_TakeImage_CamGainceiling
    Image Quality $TOOLTIP_TakeImage_CamQuality
    Brightness $TOOLTIP_TakeImage_CamBrightness
    Contrast $TOOLTIP_TakeImage_CamContrast
    Saturation $TOOLTIP_TakeImage_CamSaturation
    AutoSharpness $TOOLTIP_TakeImage_CamAutoSharpness
    Sharpness $TOOLTIP_TakeImage_CamSharpness
    SpecialEffect $TOOLTIP_TakeImage_CamSpecialEffect
    White Balance Mode $TOOLTIP_TakeImage_CamWbMode
    White Balance $TOOLTIP_TakeImage_CamAwb
    Auto White Balance $TOOLTIP_TakeImage_CamAwbGain
    Auto-Exposure Control $TOOLTIP_TakeImage_CamAec
    Auto-Exposure Control 2 $TOOLTIP_TakeImage_CamAec2
    Auto Exposure Level $TOOLTIP_TakeImage_CamAeLevel
    Auto Exposure Value $TOOLTIP_TakeImage_CamAecValue
    Auto Gain $TOOLTIP_TakeImage_CamAgc
    Gain Manual Value $TOOLTIP_TakeImage_CamAgcGain
    Black Pixel Correction $TOOLTIP_TakeImage_CamBpc
    White Pixel Correction $TOOLTIP_TakeImage_CamWpc
    CamRawGma $TOOLTIP_TakeImage_CamRawGma
    Lens Correction $TOOLTIP_TakeImage_CamLenc
    Mirror Image $TOOLTIP_TakeImage_CamHmirror
    Flip Image $TOOLTIP_TakeImage_CamVflip
    Downsize $TOOLTIP_TakeImage_CamDcw
    Denoise $TOOLTIP_TakeImage_CamDenoise
    Zoom $TOOLTIP_TakeImage_CamZoom
    Zoom Size $TOOLTIP_TakeImage_CamZoomSize
    Zoom Offset X $TOOLTIP_TakeImage_CamZoomOffsetX
    Zoom Offset Y $TOOLTIP_TakeImage_CamZoomOffsetY
    LED Intensity $TOOLTIP_TakeImage_LEDIntensity

    Alignment

    Search Field X - Pixel $TOOLTIP_Alignment_SearchFieldX
    Search Field Y - Pixel $TOOLTIP_Alignment_SearchFieldY
    + Search Max Angle + + degree + $TOOLTIP_Alignment_SearchMaxAngle
    + Antialiasing + + + $TOOLTIP_Alignment_Antialiasing
    @@ -742,12 +793,12 @@ $TOOLTIP_Alignment_AlignmentAlgo
    Rotation angle - degree $TOOLTIP_Alignment_InitialRotate$TOOLTIP_Digits_Model
    @@ -791,7 +842,7 @@ - + $TOOLTIP_Digits_ROIImagesLocation
    $TOOLTIP_Analog_ROIImagesLocation
    - + Previous Value $TOOLTIP_PostProcessing_PreValueUse
    @@ -877,9 +928,9 @@ $TOOLTIP_PostProcessing_PreValueAgeStartup
    - + Skip Messages on Error
    - + Allow Negative Rate $TOOLTIP_PostProcessing_NUMBER.AnalogToDigitTransitionStart
    + + + + + $TOOLTIP_PostProcessing_NUMBER.MaxFlowRate
    @@ -977,7 +1040,7 @@
    - + Extended Resolution
    - + Ignore Leading NaNs $TOOLTIP_PostProcessing_NUMBER.IgnoreLeadingNaN
    - - - - $TOOLTIP_PostProcessing_NUMBER.CheckDigitIncreaseConsistency
    $TOOLTIP_MQTT_password
    @@ -1103,7 +1140,7 @@ $TOOLTIP_MQTT_CACert
    @@ -1114,7 +1151,7 @@ $TOOLTIP_MQTT_ClientCert
    @@ -1125,7 +1162,7 @@ $TOOLTIP_MQTT_ClientKey
    @@ -1141,7 +1178,7 @@
    - + Retain Messages
    - + Homeassistant Discovery $TOOLTIP_MQTT_HomeassistantDiscovery
    + + + + + $TOOLTIP_MQTT_DiscoveryPrefix
    @@ -1482,7 +1530,7 @@
    LED Type (NeoPixel) $TOOLTIP_GPIO_LEDType
    Numbers of LEDs - $TOOLTIP_GPIO_LEDNumbers
    LED Color $TOOLTIP_GPIO_LEDColor
    G
    B

    Auto Timer