From 159633e837f6a1af588c237e46185176274bb918 Mon Sep 17 00:00:00 2001 From: Taras Syvash Date: Sat, 17 Jan 2026 09:53:08 +0200 Subject: [PATCH] Initial commit --- .clangd | 2 + .devcontainer/Dockerfile | 13 + .devcontainer/devcontainer.json | 21 + .gitignore | 18 + .gitmodules | 3 + 61252685.LR1121_V2_1_data_sheet.pdf | Bin 0 -> 968122 bytes CMakeLists.txt | 3 + README.md | 68 + Waveshare-ESP32-components | 1 + apps/rx/CMakeLists.txt | 9 + apps/rx/dependencies.lock | 10 + apps/rx/main/CMakeLists.txt | 5 + apps/rx/main/rx_main.c | 448 +++++ apps/rx/sdkconfig | 1715 ++++++++++++++++ apps/rx/sdkconfig.defaults | 43 + apps/rx/sdkconfig.old | 1548 +++++++++++++++ apps/tx/CMakeLists.txt | 9 + apps/tx/dependencies.lock | 10 + apps/tx/main/CMakeLists.txt | 5 + apps/tx/main/tx_main.c | 581 ++++++ apps/tx/sdkconfig | 1716 +++++++++++++++++ apps/tx/sdkconfig.defaults | 43 + apps/tx/sdkconfig.old | 1549 +++++++++++++++ components/common/CMakeLists.txt | 5 + components/common/Kconfig | 17 + components/common/common.c | 44 + components/common/include/common.h | 5 + components/esp_lora_1121/CMakeLists.txt | 20 + components/esp_lora_1121/LICENSE | 202 ++ components/esp_lora_1121/README.md | 117 ++ components/esp_lora_1121/idf_component.yml | 6 + .../esp_lora_1121/include/esp_lora_1121.h | 80 + .../include/lr1121_common/lr1121_common.h | 162 ++ .../include/lr1121_modem/lr1121_modem_bsp.h | 294 +++ .../lr1121_modem/lr1121_modem_bsp_types.h | 203 ++ .../lr1121_modem/lr1121_modem_common.h | 107 + .../lr1121_modem_driver_version.h | 56 + .../include/lr1121_modem/lr1121_modem_hal.h | 227 +++ .../lr1121_modem/lr1121_modem_helper.h | 295 +++ .../lr1121_modem/lr1121_modem_lorawan.h | 1006 ++++++++++ .../lr1121_modem/lr1121_modem_lorawan_types.h | 339 ++++ .../lr1121_modem/lr1121_modem_lr_fhss.h | 144 ++ .../lr1121_modem/lr1121_modem_lr_fhss_types.h | 66 + .../include/lr1121_modem/lr1121_modem_modem.h | 393 ++++ .../lr1121_modem/lr1121_modem_modem_types.h | 296 +++ .../include/lr1121_modem/lr1121_modem_radio.h | 1052 ++++++++++ .../lr1121_modem/lr1121_modem_radio_timings.h | 97 + .../lr1121_modem/lr1121_modem_radio_types.h | 653 +++++++ .../lr1121_modem/lr1121_modem_regmem.h | 209 ++ .../include/lr1121_modem/lr1121_modem_relay.h | 99 + .../lr1121_modem/lr1121_modem_relay_types.h | 127 ++ .../lr1121_modem/lr1121_modem_system.h | 606 ++++++ .../lr1121_modem/lr1121_modem_system_types.h | 348 ++++ .../include/lr1121_modem/lr1121_types.h | 75 + .../lr1121_modem/lr_fhss_v1_base_types.h | 127 ++ .../lr1121_modem_printf_info.h | 141 ++ .../lr11xx_bootloader_types_str.h | 48 + .../lr11xx_crypto_engine_types_str.h | 49 + .../lr11xx_lr_fhss_types_str.h | 49 + .../lr1121_printers/lr11xx_printf_info.h | 14 + .../lr1121_printers/lr11xx_radio_types_str.h | 70 + .../lr1121_printers/lr11xx_rttof_types_str.h | 46 + .../lr1121_printers/lr11xx_system_types_str.h | 54 + .../lr1121_printers/lr11xx_types_str.h | 46 + .../include/lr11xx_driver/lr11xx_bootloader.h | 213 ++ .../lr11xx_driver/lr11xx_bootloader_types.h | 179 ++ .../lr11xx_driver/lr11xx_crypto_engine.h | 342 ++++ .../lr11xx_crypto_engine_types.h | 199 ++ .../lr11xx_driver/lr11xx_driver_version.h | 90 + .../include/lr11xx_driver/lr11xx_gnss.h | 757 ++++++++ .../include/lr11xx_driver/lr11xx_gnss_types.h | 573 ++++++ .../include/lr11xx_driver/lr11xx_hal.h | 211 ++ .../include/lr11xx_driver/lr11xx_lr_fhss.h | 138 ++ .../lr11xx_driver/lr11xx_lr_fhss_types.h | 65 + .../include/lr11xx_driver/lr11xx_radio.h | 1044 ++++++++++ .../lr11xx_driver/lr11xx_radio_timings.h | 95 + .../lr11xx_driver/lr11xx_radio_types.h | 654 +++++++ .../include/lr11xx_driver/lr11xx_regmem.h | 207 ++ .../include/lr11xx_driver/lr11xx_rttof.h | 201 ++ .../lr11xx_driver/lr11xx_rttof_types.h | 82 + .../include/lr11xx_driver/lr11xx_system.h | 594 ++++++ .../lr11xx_driver/lr11xx_system_types.h | 349 ++++ .../include/lr11xx_driver/lr11xx_types.h | 76 + .../include/lr11xx_driver/lr11xx_wifi.h | 628 ++++++ .../include/lr11xx_driver/lr11xx_wifi_types.h | 410 ++++ .../lr11xx_driver/lr_fhss_v1_base_types.h | 127 ++ components/esp_lora_1121/src/esp_lora_1121.c | 112 ++ .../src/lr1121_common/lr1121_common.c | 837 ++++++++ .../src/lr1121_modem/lr1121_modem_bsp.c | 472 +++++ .../lr1121_modem_driver_version.c | 83 + .../src/lr1121_modem/lr1121_modem_helper.c | 170 ++ .../src/lr1121_modem/lr1121_modem_lorawan.c | 1401 ++++++++++++++ .../src/lr1121_modem/lr1121_modem_lr_fhss.c | 226 +++ .../src/lr1121_modem/lr1121_modem_modem.c | 672 +++++++ .../src/lr1121_modem/lr1121_modem_radio.c | 1303 +++++++++++++ .../lr1121_modem/lr1121_modem_radio_timings.c | 234 +++ .../src/lr1121_modem/lr1121_modem_regmem.c | 332 ++++ .../src/lr1121_modem/lr1121_modem_relay.c | 159 ++ .../src/lr1121_modem/lr1121_modem_system.c | 699 +++++++ .../esp_lora_1121/src/lr1121_modem_hal.c | 344 ++++ .../lr1121_modem_printf_info.c | 315 +++ .../lr11xx_bootloader_types_str.c | 159 ++ .../lr11xx_crypto_engine_types_str.c | 260 +++ .../lr11xx_lr_fhss_types_str.c | 165 ++ .../src/lr1121_printers/lr11xx_printf_info.c | 40 + .../lr1121_printers/lr11xx_radio_types_str.c | 926 +++++++++ .../lr1121_printers/lr11xx_rttof_types_str.c | 57 + .../lr1121_printers/lr11xx_system_types_str.c | 325 ++++ .../src/lr1121_printers/lr11xx_types_str.c | 57 + .../src/lr11xx_driver/lr11xx_bootloader.c | 294 +++ .../src/lr11xx_driver/lr11xx_crypto_engine.c | 589 ++++++ .../src/lr11xx_driver/lr11xx_driver_version.c | 88 + .../src/lr11xx_driver/lr11xx_gnss.c | 1339 +++++++++++++ .../src/lr11xx_driver/lr11xx_lr_fhss.c | 216 +++ .../src/lr11xx_driver/lr11xx_radio.c | 1349 +++++++++++++ .../src/lr11xx_driver/lr11xx_radio_timings.c | 233 +++ .../src/lr11xx_driver/lr11xx_regmem.c | 323 ++++ .../src/lr11xx_driver/lr11xx_rttof.c | 194 ++ .../src/lr11xx_driver/lr11xx_system.c | 664 +++++++ .../src/lr11xx_driver/lr11xx_wifi.c | 940 +++++++++ components/esp_lora_1121/src/lr11xx_hal.c | 220 +++ .../esp_lora_1121/test_app/CMakeLists.txt | 8 + .../test_app/main/CMakeLists.txt | 3 + .../test_app/main/idf_component.yml | 8 + .../test_app/main/lr1121_config.c | 394 ++++ .../test_app/main/lr1121_config.h | 212 ++ .../esp_lora_1121/test_app/main/test_lr1121.c | 330 ++++ components/input/CMakeLists.txt | 5 + components/input/Kconfig | 32 + components/input/include/input.h | 18 + components/input/input.c | 94 + components/lora_radio/CMakeLists.txt | 6 + components/lora_radio/Kconfig | 56 + components/lora_radio/include/lora_radio.h | 40 + components/lora_radio/lora_radio.c | 476 +++++ components/ui/CMakeLists.txt | 6 + components/ui/Kconfig | 38 + components/ui/include/ui.h | 5 + components/ui/ui.c | 386 ++++ components/usb_api/CMakeLists.txt | 5 + components/usb_api/include/usb_api.h | 17 + components/usb_api/usb_api.c | 75 + idf_component.yml | 18 + py_app/README.md | 25 + py_app/main.py | 426 ++++ py_app/requirements.txt | 2 + sdkconfig | 1265 ++++++++++++ sdkconfig.ci | 0 148 files changed, 42795 insertions(+) create mode 100644 .clangd create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 61252685.LR1121_V2_1_data_sheet.pdf create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 160000 Waveshare-ESP32-components create mode 100644 apps/rx/CMakeLists.txt create mode 100644 apps/rx/dependencies.lock create mode 100644 apps/rx/main/CMakeLists.txt create mode 100644 apps/rx/main/rx_main.c create mode 100644 apps/rx/sdkconfig create mode 100644 apps/rx/sdkconfig.defaults create mode 100644 apps/rx/sdkconfig.old create mode 100644 apps/tx/CMakeLists.txt create mode 100644 apps/tx/dependencies.lock create mode 100644 apps/tx/main/CMakeLists.txt create mode 100644 apps/tx/main/tx_main.c create mode 100644 apps/tx/sdkconfig create mode 100644 apps/tx/sdkconfig.defaults create mode 100644 apps/tx/sdkconfig.old create mode 100644 components/common/CMakeLists.txt create mode 100644 components/common/Kconfig create mode 100644 components/common/common.c create mode 100644 components/common/include/common.h create mode 100755 components/esp_lora_1121/CMakeLists.txt create mode 100755 components/esp_lora_1121/LICENSE create mode 100755 components/esp_lora_1121/README.md create mode 100755 components/esp_lora_1121/idf_component.yml create mode 100755 components/esp_lora_1121/include/esp_lora_1121.h create mode 100755 components/esp_lora_1121/include/lr1121_common/lr1121_common.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_bsp.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_bsp_types.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_common.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_driver_version.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_hal.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_helper.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lorawan.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lorawan_types.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lr_fhss.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lr_fhss_types.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_modem.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_modem_types.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_radio.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_radio_timings.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_radio_types.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_regmem.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_relay.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_relay_types.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_system.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_modem_system_types.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr1121_types.h create mode 100755 components/esp_lora_1121/include/lr1121_modem/lr_fhss_v1_base_types.h create mode 100755 components/esp_lora_1121/include/lr1121_printers/lr1121_modem_printf_info.h create mode 100755 components/esp_lora_1121/include/lr1121_printers/lr11xx_bootloader_types_str.h create mode 100755 components/esp_lora_1121/include/lr1121_printers/lr11xx_crypto_engine_types_str.h create mode 100755 components/esp_lora_1121/include/lr1121_printers/lr11xx_lr_fhss_types_str.h create mode 100755 components/esp_lora_1121/include/lr1121_printers/lr11xx_printf_info.h create mode 100755 components/esp_lora_1121/include/lr1121_printers/lr11xx_radio_types_str.h create mode 100755 components/esp_lora_1121/include/lr1121_printers/lr11xx_rttof_types_str.h create mode 100755 components/esp_lora_1121/include/lr1121_printers/lr11xx_system_types_str.h create mode 100755 components/esp_lora_1121/include/lr1121_printers/lr11xx_types_str.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_bootloader.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_bootloader_types.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_crypto_engine.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_crypto_engine_types.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_driver_version.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_gnss.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_gnss_types.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_hal.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_lr_fhss.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_lr_fhss_types.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_radio.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_radio_timings.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_radio_types.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_regmem.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_rttof.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_rttof_types.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_system.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_system_types.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_types.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_wifi.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr11xx_wifi_types.h create mode 100755 components/esp_lora_1121/include/lr11xx_driver/lr_fhss_v1_base_types.h create mode 100755 components/esp_lora_1121/src/esp_lora_1121.c create mode 100755 components/esp_lora_1121/src/lr1121_common/lr1121_common.c create mode 100755 components/esp_lora_1121/src/lr1121_modem/lr1121_modem_bsp.c create mode 100755 components/esp_lora_1121/src/lr1121_modem/lr1121_modem_driver_version.c create mode 100755 components/esp_lora_1121/src/lr1121_modem/lr1121_modem_helper.c create mode 100755 components/esp_lora_1121/src/lr1121_modem/lr1121_modem_lorawan.c create mode 100755 components/esp_lora_1121/src/lr1121_modem/lr1121_modem_lr_fhss.c create mode 100755 components/esp_lora_1121/src/lr1121_modem/lr1121_modem_modem.c create mode 100755 components/esp_lora_1121/src/lr1121_modem/lr1121_modem_radio.c create mode 100755 components/esp_lora_1121/src/lr1121_modem/lr1121_modem_radio_timings.c create mode 100755 components/esp_lora_1121/src/lr1121_modem/lr1121_modem_regmem.c create mode 100755 components/esp_lora_1121/src/lr1121_modem/lr1121_modem_relay.c create mode 100755 components/esp_lora_1121/src/lr1121_modem/lr1121_modem_system.c create mode 100755 components/esp_lora_1121/src/lr1121_modem_hal.c create mode 100755 components/esp_lora_1121/src/lr1121_printers/lr1121_modem_printf_info.c create mode 100755 components/esp_lora_1121/src/lr1121_printers/lr11xx_bootloader_types_str.c create mode 100755 components/esp_lora_1121/src/lr1121_printers/lr11xx_crypto_engine_types_str.c create mode 100755 components/esp_lora_1121/src/lr1121_printers/lr11xx_lr_fhss_types_str.c create mode 100755 components/esp_lora_1121/src/lr1121_printers/lr11xx_printf_info.c create mode 100755 components/esp_lora_1121/src/lr1121_printers/lr11xx_radio_types_str.c create mode 100755 components/esp_lora_1121/src/lr1121_printers/lr11xx_rttof_types_str.c create mode 100755 components/esp_lora_1121/src/lr1121_printers/lr11xx_system_types_str.c create mode 100755 components/esp_lora_1121/src/lr1121_printers/lr11xx_types_str.c create mode 100755 components/esp_lora_1121/src/lr11xx_driver/lr11xx_bootloader.c create mode 100755 components/esp_lora_1121/src/lr11xx_driver/lr11xx_crypto_engine.c create mode 100755 components/esp_lora_1121/src/lr11xx_driver/lr11xx_driver_version.c create mode 100755 components/esp_lora_1121/src/lr11xx_driver/lr11xx_gnss.c create mode 100755 components/esp_lora_1121/src/lr11xx_driver/lr11xx_lr_fhss.c create mode 100755 components/esp_lora_1121/src/lr11xx_driver/lr11xx_radio.c create mode 100755 components/esp_lora_1121/src/lr11xx_driver/lr11xx_radio_timings.c create mode 100755 components/esp_lora_1121/src/lr11xx_driver/lr11xx_regmem.c create mode 100755 components/esp_lora_1121/src/lr11xx_driver/lr11xx_rttof.c create mode 100755 components/esp_lora_1121/src/lr11xx_driver/lr11xx_system.c create mode 100755 components/esp_lora_1121/src/lr11xx_driver/lr11xx_wifi.c create mode 100755 components/esp_lora_1121/src/lr11xx_hal.c create mode 100755 components/esp_lora_1121/test_app/CMakeLists.txt create mode 100755 components/esp_lora_1121/test_app/main/CMakeLists.txt create mode 100755 components/esp_lora_1121/test_app/main/idf_component.yml create mode 100755 components/esp_lora_1121/test_app/main/lr1121_config.c create mode 100755 components/esp_lora_1121/test_app/main/lr1121_config.h create mode 100755 components/esp_lora_1121/test_app/main/test_lr1121.c create mode 100644 components/input/CMakeLists.txt create mode 100644 components/input/Kconfig create mode 100644 components/input/include/input.h create mode 100644 components/input/input.c create mode 100644 components/lora_radio/CMakeLists.txt create mode 100644 components/lora_radio/Kconfig create mode 100644 components/lora_radio/include/lora_radio.h create mode 100644 components/lora_radio/lora_radio.c create mode 100644 components/ui/CMakeLists.txt create mode 100644 components/ui/Kconfig create mode 100644 components/ui/include/ui.h create mode 100644 components/ui/ui.c create mode 100644 components/usb_api/CMakeLists.txt create mode 100644 components/usb_api/include/usb_api.h create mode 100644 components/usb_api/usb_api.c create mode 100644 idf_component.yml create mode 100644 py_app/README.md create mode 100644 py_app/main.py create mode 100644 py_app/requirements.txt create mode 100644 sdkconfig create mode 100755 sdkconfig.ci diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..437f255 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + Remove: [-f*, -m*] diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..dafb8ad --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,13 @@ +ARG DOCKER_TAG=latest +FROM espressif/idf:${DOCKER_TAG} + +ENV LC_ALL=C.UTF-8 +ENV LANG=C.UTF-8 + +RUN apt-get update -y && apt-get install udev -y + +RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc + +ENTRYPOINT [ "/opt/esp/entrypoint.sh" ] + +CMD ["/bin/bash", "-c"] \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..b801786 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,21 @@ +{ + "name": "ESP-IDF QEMU", + "build": { + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { + "settings": { + "terminal.integrated.defaultProfile.linux": "bash", + "idf.espIdfPath": "/opt/esp/idf", + "idf.toolsPath": "/opt/esp", + "idf.gitPath": "/usr/bin/git" + }, + "extensions": [ + "espressif.esp-idf-extension", + "espressif.esp-idf-web" + ] + } + }, + "runArgs": ["--privileged"] +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..24aa298 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# ESP-IDF build outputs +build/ +build-*/ +**/build/ + +# Python cache +__pycache__/ +*.py[cod] +.venv/ +**/.venv/ + +# IDE/editor +.vscode/ +.idea/ +*.swp + +# OS +.DS_Store diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..1f350e0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Waveshare-ESP32-components"] + path = Waveshare-ESP32-components + url = https://github.com/waveshareteam/Waveshare-ESP32-components.git diff --git a/61252685.LR1121_V2_1_data_sheet.pdf b/61252685.LR1121_V2_1_data_sheet.pdf new file mode 100644 index 0000000000000000000000000000000000000000..013b477def03381ca13463e9189ecdd4f3970c11 GIT binary patch literal 968122 zcmd?RbzD?i_diZ|hX_hY2uREf6U@-vC7qH(H$#I82q+&%?9&faIQz4qE`ul-(Y;L%Y~5`qXLD0$`=21Y2! zU~m)%n8U-ynNmUmq~YX_w)S=kLfdgbKpGq<1PlxYgS0td2slWQ10o7WB1Ax198fq& zp92MlAy6og3Wpho2po>!fFR%q3n?i|w7VVlZ5;nPD9Hl6(e}SvfmP1bO;>aJcB% zx}p$3s@UVNzQK`zkWSN3DA;Kl0wQvz90G!Xo-Kzu(+|Wy^aFuFoNWVvIwLDli0GOA zii(KBPq&K%gU^&hf{Y5ovU2>djE2q;wKOkF7AOn(tj)EPYmXvi~dz##Bbx&{G*5%5#v2GCHa=LZA~ zLBLME4*~{+b!r{~H2CRp!_tta#vGubPRj~VA^2$-05s%j{ey@gp{M5{K!cs0ZvYK0 za+*J2$f2iYAc91mmNNv3K*3Jg13=LbqNnS^!9X+rKDQunQ9$v}(9YNwz|x8$PU#gu zJKJ9b9ECXD1_A*L$*DFF2odn3CUV+t05s?s9Y-MHr)>^U4j81<G{`sj`6m3az%Snk^x?n9^gQ6z(qwsx*i@F zz;^;z;m`%Cy4!nj00!;%je?m345gq5LCDIAfRUnbS>Oqv`f{R5D3pk(B1~3JPD(-+ zC5sf5M~cAYWfg(ZLZakhU`2Vr;LD1N03To}3y_XC+V8{_Ly7{v7nu*n+8guR-2zjR z@$e{WD^ZgD;fYa_0rurDUr&)kL==GlDLJ`f(B2>=S8EJf0d4DHhX$zvPmm%Y4Ya!h z#*qU8^b>Fa0Z#x7(&G?;A)!)IJ{WJbwHqaw<7}y+la8JdgA|CHn;?!RwTjY41x#?B zM=;+w^E}tB^Dw+H8bL)xnfEkCe2*e0(T}o7G$gX^Hr=j^l^ zH+omaqS)Z#JL&aMTstSvv}M&C zh9XFC1C~}%v}USaD++CosK1%}Fxud_zTu%OSK0h2kcyLy7E3kVhiwWj%t*Xpl^hqUh^3@mFWR z8^~)Y42RuAj6X||2)8TF&ZF8$3-p~qxNeFV*-!^hL$t^aCezLZRy&#ny0~t4q#F^h zNCxDDW~NeMD%BEf_${&%yx%|MbbDxBGNNkASH3B#P8>W$s{vNtH`BMuEOtX z63C12ZL2*~v<&Nr_R0Y|7_q#IG-Hz0{O~d)|0Q2S??miqR0@A2i@Z<9C6#v*H2%du znsxjnA>5xzzkg-WwO&kGOB=a2Q4;>$A+jp!n;!G-Gmjs`Jj?Ru?8ZWr@?Pf=$h+>GHc}v~vTik{FRN$@%Ky|0GyH+ben z!2S8{2uF59nNctb?}?Mv=tC~fd+Y1@M<7eM} zO}QX=)@nm8DG8hU&X2ZxCVGhzS0B-}D|E+keD`(i|9UG(V&G13M%KrSpOq2DM^U%x zul33E)X~jlPexqW=OHQD?0+&$6*Z(?s_FdT!LQru8ypuq4#=5;E%*x>?|Q5GAHGk; z%v=2EP39oXFbUtACsm<&78+o(P~iS8QgYyYIw#*<3aT>i z?+}=Vf5c6@21@xTxDOC+z4h5tX@ZbGBF#i zk7sOp*{IUqR%VUNn>IV|500~;FgXzP9IUfd2Z9(l0c@0o+nuw?;-_?49ju^uEq2`5^0tKdn@$O8I`kty)Try zx^zjg>CQoFr3wmN?rtsa@X0m(wvPQCSlq4}0=?h&S(8Q8X`EH88nbb+^x9_UTOEA7 zwh*(27FJ8*SR?|5_{3_oHGDDEWYjAWHv=!K2D~pzb%LqhWoP2a_Vr#K0@nO=f&vRkEa>Ng3yEu9k1}I%ySTn5HS}ySEP>^*+u~yL6;fyKhCu9k5Tn>r7 zo~f#oOCTQ28a0+jGH*M~_03Q{$6QMg$I{qsTT;-XI1GY}6AFKo_(i81H$bjI#Vwrg z>zf7)=OLGQ6xWdQtH=E*>s7CX=t_*i1kA>CSH_5M>@~fvtcXaA@*`lsw)j5s$nlW0 zh1>wt>gp=4xurj;*7aJqg40s6BOIeoErZXsEgk8}(AR#K3qug6nkBcoF80#m&jekx*P>@*SyJDcJZEh;uzsb4jeOD;h5d~Vnay=AOL?X5Px6+ zzn8#2F$QaQ2R^hrKS&dev9`0uSObe9;P3$}q?WZC+6P!Kfh8HZ)AmHW%i3a`Jlp{i zcHIIXjdr(1`v7aWf|ED;q;Zgjh%RvF+f;s z{tb`$P5GmOnjRlNz{y_?{QbpP-v1~k>+bFWFa$&V7Pp)$8f_=%5nu)u1~>#x0IL8# zMNz<~1xVh*9fNkq_?Q7M0=97u81jS(;QPes0lonz3QNZV7bo8*!wTG=j5zT9n`w}| zw}ywbN~jV>IP`8Jl3D{@I-rKoKBQb zQy$C#$9m8}Lz?ms;QF`ss3|W3T%Rxiuw|@m;|%C3z?rHWP#(KW0LrVn0p+o~)01nU zJl4}axrPGQCmjdMzyP8Dc?}2Xe=rEVRo4S64-PO$-`mOUKZ^Tg9$_cl-xUa}ELa5s z=D`W?fUr*Zw)kthVCjGA7{It5$Us*Wd-6GAFrH!{kiWmbun*b|gSK@Pw)JoW@dF&m z>7A6+`#pK(JiP4yztxBXjD2)+V}|WHpoqGFG|WUq!NN$OZ!nm!2;fq4Kw%JJ0G{B0 zieT^Iz(n-5!CaPtq0{5F>VJ4SvRq<>@t ztMjKBxvHh8tE?*^Z|&-21pDYTCkjRef8*pV z56ItB_J1V}c$w&+QH zI2QK(w?}`@b6{Hj!!GiV?ITna@z;J)<&Jrhg-ZmOA!_r8@wquD#!}T)_Evx0$2U?B zr>QHtE9vc%9YKe?>f36>s8DEnmaY%?xiP0nj@2ZRq@Yt6P4W= z1*IDe2<&!V9bw!WYgQU7nrXxJKM-BJXf9*+fNPcssvg#9*t5N^^w4cnK*Usc zO(*1nYQT6Gq%h_EsHH2G9+vgj9~%6k?QrL<7NcmU_GSa+(APd#_*h+~doz2G%4d&^fGn2i6&f=ucq}A%K%sY=d z(;MBtvw1u{LH_PX^hPyhk$LC0+@~>@hv>ZT^w90&UM(sTq|}!?SIj}|^fZd1=BD7S ztmaiodH4>V&|`RwhJKE_PWd2o$zX|jr~gs*E7@`@0gC*H?gtKm<@v_K#aVO{}{o^4rS&%UJ>$$m{-9*TeI7 z;lHg3CwA!%1Nfiy2#6d0n;xYM#7u)}qBpOTvHDZTx^*YHlHg9e$WtB3k4VXk8`ed2 zK<@4))|cmMyXJ%uNZBhvw?`b4>Y^vwB-}WUny*DanQjk!)Hya&#=iQoJv})4Gxr0V zj3^=9JyzjD9@WsVTi2)Ge*$Nhcf0crv(E-hk2eMscdn)fYYls>E{!`~XVQN4ZlPKw zfxY)zr^WlGI~r@dHW-H+gAYc&eaColQd)zeho%eHf6d$e3OsBAX?49uy`h*Gnn@k` zI6iW?k!0~%I=FK6$RWES@NnQ7`qst&>TEO;Xrhx*);h@W?T?Fqy{p+*kye!4R#( zl`1AU!vc$h<(6y3RrMhvGua!nJUJ;=v3WUE+A4~<(L}zXxgV#Z*)aNGjJ_kxMJ`j# zC&u-X)>Zw2t8xyL90u}BoZ=G87WXYi8bANyOiOA|MvL?cxbj(Fnb(mMkPw%!MN8hI zBeH<`yF69&sgE+%pv-9^Wi3*nv(U(ZiWp<=*;TwP{5sX_uJk6qjot@v;E?2wA7k4)vL168HcWi`S+8KmsE%a0YuG3ylB z$O-lsRZdgIh`N2vb2hVz_35}ZnxRW?Bz@WBb5aOA2%$gzIPFu%yN8(+LZH{*ewpsn z@U&By78J&_=BN|%J1-CFeWIDU;=HV*Gvuc2r>Gnna-->PR+$B%0sV-N@{yNq^Ol__K2}M%ucMSPHv?f_({?bq(zvM-c*Uz~3@!M?5v?hXI$g{qm zxiB^A=?cqE^lhKdj8p?sKLYgkd)QEeB+=#S{sc2Z{GkiCl3Qd(Zg$ENy`0){=OwVH z&|6ic4wNL_{<3FIFK4j7?Qxz-vcFr*PMxdxoeEs%l^UF^`*_5^nEm~Xx0m#t>4!ZG zyT6*fzDWk$@+PldPEpB~D8=|%v7G1%X& z#Hf**%SBO|U)NvRNHZCZFIniE3;E*XbctP#dOzouoGq`;+O53x<4q`wYGZIPB%ZY3 z4R>LR5_F&`;YZ=jSj%AnF^krwpVQ!;dPKb;`og!5FV*f1dj`Ef+J8B|STMvVZB?|c zZ#3(;{tfo-#rocZR(&ddO%E;Ub){#y32iDpdvh40hf_DjTU&W5hDKB(Ei*E5>(*OG zZ~th$a%FR!Szy{EFkzbY0f^W=^dr^C(z);_GJfv4YJ4yD+D7$R(f2N-=7xSuAeYny$*Bb$vmByokWppp4949`-Kg%}h*xmcEL+Rjg4;pODO+^Wv0HFOSxnyOVap zL2fX5UW7@{El)G_{>INdMjllD!MtDTQjYx9Zs@!rh ze}q6Sliax3XiDV-`8BH71c8($RGe}jizoYIafKtt*M8;8G>RN??k5z^6lN-`eUfN* zjq1$Yz50ovXF~p%?3@#xs5ET8FKX~uBm_tPO1GLkitZZOpku(n?dn}|sYvel1%}G& zORM+rD70|Rf{t4BH~q@DcAXqVemo0h@gnOHi_KJeIrIFMN&njiGU+%Vj&5cxvo_Id zgbcK}FZTil;{CIIhMbTLA8w96aD*Ho8lS?aKJK(DaH-jG;BcgRo_MblX7{q;GV-lvywP;HCv zr}L-Siw-&<>aMJkg@w&sfB#*7QT7kNjslM7%I8+bWT_9*+i;EqIos(=6RW?G204!! z^k1Bu+YW`dj!{dm&K+foOT`u3w55A^r@V1#G04+3xrCklyXSW0e!Ej=%f9)qPhy$d zg@iYvJ-_X^{%XkjhPqMxk-b_j=Q`qsdHsc7hI{OmErjJD@!O9g;t*F*?W`->J?(So zOOm_s(ap5AatCuV?*4V+M@byJWi-#j%ndszxDKF*2UxYc|v;g5aS zkR?l^F#dwGFTJ4F%*i6+`h7WxtM@eYm6)>jAYII@V8UNim+zLqp0d{XzNr*)j-3}C zsaqMmd5$|&(EHVAck>(anH)df#|HOqP&UKN-x%Piu5hbPVGNxX9X;cO1Y5@PcI=0J z&2tcw=HwL19q%8m3VuYU)Yr&Z=ew+!r4`5NSR{K(#s?YJBEEX_`t`u{3W^<4K03f& zjHXSZNKHcm*zDX(mZ`vH?yWPr4{{ZS^wCl3E;=(|Q0~TNcK11z5vA_pn)uhw_NDd$ zl+C5PU_$?-_LUAXp>b7GdZtX)@KuRmq(1UTnc=xg3DML#A1|V!2Z#$t{Zkj`8M4UU zwro8Q%xzSekYSf~kFFl8y3kDF?y1sJc+CE_91_sh4dr;=@N<0HzIkpeEBYyl@7`qf zr@-^3wI4NV;5^gmOD~8eQkb+u4U%=-s8wdN3=Z>_Jo5IJZ=bSqes@K` zkp-PAPMK8UQ6ls^Z{DyNNMV;sH~W05h&8o%t5j0Dhq%+?c!Di@qM8QQAM zY8OJCXfGb=Ke{87QO2Xl$I7zZ(V+x|uMCj}HVt7u&U*Jte7fwdIl{<<`?fu|#CZ9! zz;}C{x7Ophwh~o->aP?-wuT>Q4CqcC`sG6txHQ$C5)~^o3J#PX$)3kqQ`?-ct@0gQ zudJ%-NuOiKCBkV~pZ@xfHvG>Go-26;+XxctKDual6z;^G*+G?yv`^*8GQ5t(PAG_ zqUJqiaZ&ICSDO6(_=^-7OTA5*>pquS*>M-)V^-1PnBTDW-hs`X=; z1p5NGczrp-?KucH3yLl(%`~xuc%O~L5>D}&>iG6sF zbm!fqiCev8*y1l3B#25r4F6_o9zl_tC_JTCBd2vVNzq!FTRdEfkF%Dl^14Ol>&gpU zz4!Z@v%m5KkI7mqDNqI9m1=REZZ}Ox#gE-~bEM56lnZUP}ylnZWXTgrNmjljrTDpsXD0i=>T8rS5D$7>F6H2XB`r{ZyEL z!QX5rKGoO8Ei3$Qc@BMO9rZ ziHmjAO44~H_+3_$^u+jKX!YkDUX?nIS-Uz%CE1=8I{PxMIwzeiMnWPk9p2BbVefY0 z8fd;9I`3#-4XS&xc|(-b_FB)AD^j6)-y`G+lico=Xj5A~+s|-(S0X&ic+)W4CXRJU zU9&H2iHsL+BkY)Q*~j{VGnY4!2yA2}KIQ@on;nJFt=eBk1QrJ9W*h|7dFd1f&2{gN3tBP)`+Abqy^A0nI>fCu=(ZLKo6SJNUXhkHtr^p%xEs0H^+69DCb6dDn*CYu4LtyPNdOhe0pwY6z_IC*z$fwq@`s_Yb+EWI zk^>3U#ioV;*{y%@k8KN%O`Qc~0kj3gI$$s)@C^f~*fMY+X&4Tqc!^-sFHd42z?Oys z3EW|+0JaTK!GL6e9s@lGGBN@BU+)IeC;uLpI+6AtdnQ>dNR8nD^5B5zoTmyJi_>xd zDLx?VH~S8E?fkSYkkzJb7l56BpVar8!`K^T7zP!LER_DEbzCIuWkRiwe`fpDjj ztt>W93dD8vFlaXw4ut5*g&_wL`Il1vH5dL4=KL*YBllloM%d^m=>IEZ^gn_gAyDwY z?Vyv5Ok#oE;Pp+*oX+;amoKQnl+`{28g#A_iP2VgQvwf5Nyd+XBX8f#Nq~nk?akpI#cr zN+gkq8*4MkW|g@1Vf zzLfMM<8c6$5c*DA9kGY&o$F9Z)C7L!1?j}&u4Rkw2`gU;?&;F zdms5CX+i#jcaMst{G*)47TBC)#sk?=_7&_4a=qyh-dzIDHn*)J8xi7h;7!vfLW3gd zhqj7tHCLNN6WEGGq;Ns_^w+)mshLPc9L(OP70tLXV5o7l3$970(DG$xk_NLe5j*>R z>hjaxORUvlBKJ$D5w=3j|H^JYy5-6BPv<`irY+|NN)SxZz33(z z*>V3$Vql~lku!leN*c^+c;kv!b_O*6-jj|nJpm`~#SY%9R4b92Kj)Qy1brW(N{Y)l zcr$XRgY7}b)Q==p$w}t(_8pg%qt^#1uHJv=I08exxKMOY-;k4KE({L$vKrpGL-AW{4VKHToI$@1rSZQ18O8*khaq3VKkgdi(LJ}?(C`bxA^Yg3== z{UF%cL_EQ)$iNHA6XGK8K}ed{dy z2m#Ve^TKr_r)((GVk={{Tov*xL>-qB%oCBP&4aWf1s0hxW&6Dw+6?aB*p^4@Z038wcB+{A4|xUv3q9Yu(I9 zn3V6Rd7%EaB5hGiq6b$sNF^y+k)R;OqIBm0N8g9L!%;m`z4WEB+u^2aQu?8{4U||H|`>EQRlYcGl!rR3rootwG3_$q5IV> zt~Z=#-LB1K)K0m+{cd9R_VdbG=lX76uiJKTTgQAte05~Q`RrR&+&;&H#ywJLmMllV z25VZA)o%^TykV+t3-nK2t$wo^U7lW_5F1kUG@*~x&cZc)JgJrfwBgl!G|m0(`Cf63 zlzkgdlD{xByHI6qw=4UjzP{(E>0e|{R;41k1DVD|8|^*od;|_5R#p60HXHlkxhYgn z-rOdNjYuL1(ynBnS3!<{u=}<5{HJHkO+8Lx;t-W&A0?!*RSfw;H&t1J9PNj#t?Tc(~@e$JBqa&|8R@*nSlafBJd*sROd#ByhBbRU(%H2ozVXnjI z{YO>%v5G+7e1_n!6E{C)Xmq{SF>g8EKC-0$secu^JNG(fwMa(`3{tphIjY@o{b>L2 zwXbZ*xH)^1?dDTE$R&m=e4I?G1Py98AFNjT#VopOBwz7U)*yPR7ZM#Fkla4D7`xl} z>UuieXB!9K{$Rr!V`Ein1GSH@mJC9l>;dT2E_?9AXEaOI#~V`DWRyupzbwxAP$fTf zAcSSC>DZ+uk|6cFqViysi49v;Myp_P&HFlY5?ssF=g@p|{W-2aImvC8$e!V*cMxGX zNuEVKfA%Z(s5f*(q*WxuhB~0@_1LQ^wK2*1HeR%@su)r%?C64{xZ6=9Yk(_bP3b-( z)31^Oe*;9lg#(WFYAo^au6-&3THd3hTUp?iFajYgY%3$E71GW~%T*uiqFGGbQtvqS zU{+>|M9^pOehEa6R&gZ+w#ex=+$F%gPxjSyd>SDgP_OLpHKt12sOn<>Zh@oAU4tSi zF=BVO`|tMKpJXZ*t^YdwmL4&5fFfM{>2==Rmo1q2azT9--Dj$|`2GaiT{G=siV&?i z_aruTa(bi5){D|>JrfhrCY7drI{1WlcLy`oi?5XoEU<)qf?sl{<0Q&D9~C$#XqwTS z-B75qvELm(AC(^75=ZciVxzMeDttkeEbm-+xtM44{fpflkL67`3EzHPUUW}-RFP2% zdY?cS*Uz&?**zATGclaKmMCMC7KJ0B!Ar-dpqTlY_mVdB(VShbDh`g=NSr+~&5^9B zqqb;xQ4N}L^Kume))7itpZvU!90U`gml?WLS*zMUzZk}ybDpy;A}L!%J;WJv zby224-XfHu<7b>^2_H;?(BkWkR#ePTd|F?@vj@=7&MT2-rZ2O6=^EFE{HM8|^jBV( z7xO1I^gwtRmc5M4cpEj*kOkV`<&=j~DaxBLM790!kbh`{r@wggH1qK!H48gAOV6dH zcKR7Lw3M9n(O&VqAvAOn zbndY^x!r}ql08SOt5rPvBDo&p>Aq@>mCJ7ft~^aC?EJ}jq3NyC%h=YQIDvSp+fQ%? zx4(yv^DMOMER^2X`ba$~vQwA4zWvQTKQyT<>$ zZ6?2pgOJ=ipRt(LMAyTKf3kxoNofCVs0IS0bNwB&8Z{F406fi&T_e{(rjI04mhyFw zHIhJ%+rE)u9Ai9p6IB$GgkMR05M9jj4HoFe8*ZqOsLt38fiC`>8rU7_EHB6|cy$?f z!67YTpsXF%$zq{yEhVWT5%93Rk?vgx$^4a3DuLPb%Eo0sbe}jSp39W#Tl>DTyzR{a z$}ezbZ81;Ul@r-4O}0*x{)q*J?iZ`1HAFJF?rGug4mZ(o%AIM548YD<$aYZ z+a+eXND`rb<>lwhX>(P%A!*HNRxU5mq6di0*>!64Z0O$Bk3P1f#;JY#4jl{QZDEcj zeAAiXkV8gi$RQ3Z#m>g(5Ba@cT5&E`8kE?5BY`y#ypmydb|j7Wlynn~G8fcM!hPc` zSWn=>zJLDw@pM;|(tE!;JAcaZM9i&q~w0JAU z5JybYvF>wzXQ+fRJqGE9Id7-3x;*K)RcO)H@^!f1Y;LFk6?b!Db(xuQjp(8|ztwHy z>507nQ}nblo#58FYYPj^DZC*BHwlhH;u8uD>Oybnn78HWlW!^GcmiVNYIWVfz zs9EM7G}~4xU2Z-1j7ZA2IkoVE^#yxN+l`gV+rF9A@wguk`32TI?~CG1kIs^ojYGNm zC)9}Q1)S}5yQtK!Y(on|r8F1l%Zy0u$*6eZgiEYB&4)trzBcHyS-p^T>1vAky7rjC zo%=oCp%!U2KEM0Z5eD;}p2aJYzNA>2v7C|5)Q>bD-p-ZH%lYJLznq#omun&$xvfJP*GFRsH(Nx=*dd>g8ePA5 zGvpzSb5hF;^<4-#Qibz7{DCGN?L@M%6?S z`>und<)&}OzORAL7g)IYe96KP7qqMqeL06(PFEC&?`a}bWp?@b@|urcMuAb+*KHV? zfuK*PUzvUgG|gve>Or1!NXcswwZJZsBk$?rOEr(nTQpg0?!RLE(e^ad(Gs6ENK&dP;y12!D3>P>%l>VQJJTQL7#HAl`?6DfF2nyg;L*px{z^KD4|7%9p(c@`;krR?>uDS$l6vG&+p14Z{qk#j6|ry_?w!`#8bp%% zAPYID)kk|<%PgsYrOV+jDT`LUxnAtHO4&o!!q!7)(_VqtC7)AL3qV=+Da09B4enLe zYK=4L;4Y+iuhARYsoM~^`&8fL=33lsSmw$Lv3o1CAh@^s4qX4(k^hTSWYYNlds6m? z?|#IWW`4W8bU|j^Rn@X>hsD~AOx8ED&rH*u>yz^}kVJpb%Gk03Uc6n;43Poj5$D7F z){HC;&3)ZZn>u~xE@o+rhA)P*hWR8I6XCdB*$yUec|p1pL+=JAxI0aLZy1l&p5j6H z$TdKgA`|$y&??UO14>u0KR33k6sPD`^7NgE&-0oU)eEq?p zFc@P~1{-pjp-|?Ujd}7dsx&Fwzkrj*^Bl3H~(6Thnn)ZE%x4=)AnINx4JS>&=VK`j0?O zGnN62YHAm!8JAkVe|llQbz|V+CfSo%0i=Fu#{0+mP?PxPI~@~M*+b--c8@|}#xAWn z$@`4TBCm5V316OoQy=Z$X8vCKm1*ZA$!Ko_vDhsG`}mbW<%(bt-h`_9uBL}VDG6p7 z!tFz!qP-&n>7`VkC+lm<``j2d^O11cw|+M;6}ep8dTmM`XUx&x6i+EoJcVCxH)zn- z7>Bpa7q^i0E$miC%MP-LA5@0}A8*qYaYr8x3#Or!E@v^7wWN^#4BM*kQ{Rw$zReJp z*D<$g{#K^K*!IY%Y%#Bo;rc4lTAbp3h9e9ZVwetC~>t^iQs-9(b!7_+GlEji2Jg@=IvhN0Ob2i2wmJ zG*nD)*YJoLNYwy|cd9RyQo0^6*NO7nv$}fcy@dpF;k})wjF` zb~?|Q-xS|4S+Luo+vWqO+)a|N5{V&JR_V{^eZ;qNS-LaKwV>Hker;29{YCjZ-v=V5 zDm$+{%#**7Zw|4WtE8seT(EX#dI^`_3n~@5x(YTXEHJaueici(InZ1c_oynI(9eHo zoZHqUU@hiFuTv01cM%Np=0Gf7H%3hylPBxhRPsI1?i^*Gv;4%5;!EwkR{9G1+L+Dr z)e!{-db*WWJLh2r^LI-#VXHqAfK~W$qJQc?uEKvymH#h5DLCS9KR#BuQ)ATa{$7dZ z<+eaThN?{NgOzp1PNkbTS`02p>NEuXlc^U~aI0|kkD82TxE=DCulkS>s2Hr7S_;i1 z4Vg|K48?4sF^D9@Jxupw&9_I*_pb_~wkluYe~Iu`^$jOxrC4?rxE*Wx@d!V;cXn!1l@D)vsM>gRE(UJQ(bkZ!ljrYE+`YCZo*wt1 zW6nsn!CnJbglg*Ey$q+ea107IVP9i#nM)hWa{o5b60U|Tv(ck_oi<Tb>MkGc$VOI`m+BD{Z`*vX0s%?yEnUxh>nG4U#sWOl~SN%l5c(vn#oKv+%n-Ws#Xdp>^IZ{co*lB^Wtza?#5XI#2? zUKMXr@Xliu(nLq*q3*{K{jpDViorU}d|a*P9cyT0w`6DT6j(oNQ;lM4Jio=^r|R?N z0fflOTsG=1E9H%Ik4#xJIX&m;zK}n?dFQV8d-`!lK}s&q$R09oX2s@PX<4^w*iE2= z6_FuNuH@zF>|JKE)lJ^f&~^G6=GFYR>zz{*X%KE=*|WShk#(1;s?^ttLS{Q~YFcSTnhicbgsJc+QKkw0LVQv6WA7A7Mu(!>Qk=cK zOi(k~3%SQWwT}4eG&~i$L9cI_w3WWuO5;~9smV{)rq|?OYb1((DDKrtGxejSrU?&%a!bE3=Q5iA@y` za*yet-p6ekvOYc>D;^^+o$;GUc*~q=dNWpXNsC|^V+pbscy(@3uY}3CM7DW0Y-YVO zjI}XCnR{v%@=)nyBjRNP{NEXySXx(a z+o*a5z$FTQ87=VEWpl_fj&c@$kNKL=3+r5HDzs_-tNRsL#E;HeMv|4!=_T29jna30 zew>@2CBXkoxclM{v>~L^%mZFh=*pf- z(Xd*?LUGsoI9^koi(;TkCNBqcllx@&rsYc)p#UWP&q3nN#1Mv#;w};Tkq#7X0%?HZ zJeBy}FVTftuf>wu;jy%2BRFiG)tuf83F7qB!dYugR)L=1s-m`!1~#)a_|RE;~!)Ek&ggD!T$W`7$Q*MKY;uzH{|3;oWH~9fBY!(cX|j13=01XL_D56 z-oT-f1^!DK8prM?kZX>2JdJY;2k&18K5&Ik0DR&T5E5VyLL$PGgNTTjn1q;^h?JC! zjFgn(-w#}Te0)LzLQ*0kQc7}Ca!P6{;Gm{HDfGt|#qYa+ulKLJ7#V5dWOn4RGn8+*ao=8sR zSB93MIW9RyT^sK$C^t+#{9KYAPp)lME3wZod|n}fN&h||o%76>5sEClb{GTEZ%?Y* z`1nT_wr?fp)%-ilN``+i{U3S$FIYZ)heJhh!X_YF9BG_`pE0kp4<}2iZOZy$8WC64 z{(pX1m6oDkO;em(+G&Ne;U&vKE|Gcfj>P8TWJV|Nqn%`57+N%OErhx=WpD74?dN5l zb!oJcIj=#?F-|%A_*&w$h3RbT1B5@sV!FG@r*)N6v^zP`+bW`adTt;=f?%sljrSe_ z&Gk!@&F3cU-&mwG`neiRRT}*tU&=Rae$j+>v%nr@?rxTGmj=0oKZ0%R=D57XS?R3U z5=WbO$JqdwtM!-<#1N zWD1|-cRRm^XXLXnhAQfQWJ2}nXK?*d$vV}$F7(r`fFKz+l0&hBzHCu?OY2EH@hgFK z6<-v#scJv<)-(_K^6&f{3X@7_qt@z=p0i$iz4!#BFeyAxEpBTvm8N8YP?fu;Y`b|e zPvW-lfWvj8Uyf!!=Vx|~Wa&oU`d!`5VjxhKu!$JE`;vrr-gJG`ZN#N;G$1@dbXnW_ zWka-wEwU<%Re>-OrE)_|n$2suzRkhn?SsMpqv5c%7=}4jva{&`)W_R2K_Lclbers;=8j_3JW&2`2l8W5)K}-xCvrKET!!_OAFDMk%wg z2@bRO4W!2h*Tmnm!JoN;qPX_xAO>M;(H zh2R&Ho$Xt}3zwuTZijC4fA};2cf96Owf`gX`h$D?c#cbkDw8o=7-!o3Ca5qo7sLxx z_(Rlv>A;dYHouEo?5=J?a;`snVSXbwO4J=|JX$_vNZF;oWy6eeekV6xdh1%M!yY~N zYpZC!Z$HZEy9u%&Bs)d=W5P6aoocchFZ8^2H0_b)(}TC=ABnwxGCuK(AAQoH_9E+X ze8NITHDs35{$z$&4?L(Xc_q9WWM6Oh-W|3)t|K6`Y%0cXZyIsGQ;HOCU7V>_#LvE( zt%Mz^!XB_nzo_(PYA0#@vp}zzOM@zSM&Z7KR%h%}>%EJ!uNMvsCvim}zidw~2D0eV<%eGvXa*k$7XmCeceCEMQvN4O=LzGH zyh}L}?rwbnnzRQh^SQO_bE!66)*SLpKZ5IPSz}A1o{%nv%I7`#f5g3KR8#HNH5^67 z0-~bQ2_5Nz6p<1|rHJ$5|Ykgyh}c_jATL z&pF>W#`ivBobUO;uk3Lp?6uaMbIogAdyjMPc{0tAZ|@+Vh&Ia4=#3P+D~k)*7d(t> zHb{S4Q*hy3Xk>`rT#$Lo0dtgIlwL61ZrZyg4w=>$E;l)Jo5(xJ7hMeo(gGYJ75#=V zw5yt>brF5qq1PyiALjRn>OXyc(zA=GxQ~sh*cV2Qiu4ms6%e6~gE3A{xAmX47V&-L zKKIq*+*mDBpX$COwDW;G#XmSmHrD%@BpVKuXr5$iFakSN&{GAuJNo$>L~ z<45`7wc+aI3qKfr(xWXivi~<&Oqv@Q=Yl5tpi7=5Z*^aTnYK0r_&TOWzz^R87R1Zf zqqjC2j4du7zKR;LE2r(@`Eae_+BHP-9ZC{}(r5(;2XKjhWw}7lzRsKV#aQu&AiajdkscMvOQu4;%+%sD1&jY5&TRSZcWyd1i3LI zubNRBv!BTDVLM*q&F`xa_W9bPar4EbYb)?H#LuG5%?h8L{^o!k2^fE%6Re-?gZHk4 zN>Mt)-31=|zo`G4%F5hr*j*t1@lc+5)!~FoZkKV|FnQcyCgD+@e(+g0Nd3-BDG{06 zX)n={V=Gp~7(>q{W%%*^(1I@?vNRyt8ebs}kiO*uMaX4Ji!x;}9{tupHg;&mn3A*d zNt*uXRMrTa|Cm>3L$TDmf|*)&;eEP>`#{pigNzRck6J zdw6{&LHe4k#{^BbPvGcGPjf<38QXhq;Fh8e^O=WzM&G*SyM%hD(KfycdeJLA1)Wx& z``5^8$R5u^JlqGmclsKwRmV0fV8RQ^g}2e{pQ^P}we^PW=9E5!jrIeaiOk%Yw+|WoF5_O zEyKSuN|mx#RS}^qTIf!_w|TaV<3zI-di$0Kqol9$luN5C(V7Or*t!(3?77<4tUZ!H zAT0yLV8Rd*K(+cf|5##AsNq!7&6km^4_iG}>}*rhfpZwe0ONy^YO@*R{5XwpZSipD zlyDz{r`S+?!jZk2-&-r8JL6ymn=|c8q$ zl*KU`>fX2a5TeJu<w_796X{ zfEkUH?v?0HIc*B5=lqs%8NDX|GpL6Bm8-~y=+BD>A=W=9ekLNNpJcHvm@3t2CH{On!Us93p=9FClc=tr`I{Z~ zaz@%eD3R_VMM=R?hL7mX1D`shs2N`9c6wZYUMaG|-Ry4d0SBi0}}Q zrcBq6HqlIB@NND8yM01|Cz{DV8NWKbW!*HocfzM`fWcwzIF9)wd0wtKR;3MNk<38m zXJ%_f-4=F@|4iPHWIf`rfOeAlk9Hm3*s&-P#5+RCmkqnDQkJOqshOD-A>r;!&0Mv* z4w;Nf-aB^n&HzKX>v~}A6K#dP96Q(*alar_$}SdlZ8GD-Upz>eeF0Sx0F)-nB<-B_hx(_U!hAP6}UGQeIkvo9UXEr{-tX2H52sJ{Qksxml2~>NhMM3zw znfqOA;Kmp4WXknMljgIMZcg!#XwO-%>lqe$gbv_otf)GxlqdOgbLZO4%pkp9l{iL@lhf>XExC({QpGCSsR)3M)I>0CYM+enb_cRgNSToWsc zFYB=B7PY!g7^Zx|B=J>z!LE4CCyKB_6X&RC$RMMj#i3o-9>a1_97&JfbOr29B@L%H zU_a}p?pL)@q{uWsL41=qkh<|l3A6g;x{0*;=dC|*mCPUa>JSmCKj#E@O}8j{gSkS< zO`tis$KUF@+y(I&jAlNTz@49(5>>lvGjCCDcuK7iqF(7#LU$BRg79T!vmTh<*m;Yt9Y!ND=*7{UG<^&~ z{jPO12PsS>h>DwsI}do9841m$Zzp=`Uk@RxhI7hdaF z+7%3MM9p#@hFHHKG!T#@(n+pjzBB0IQ{@DHD+y%VjQupH?B~Rd}5ejznW|@ zyKO>(Op+i+Olv1s5y@Rb)Utba*$hORowZgUSF+Onjuf@*|16G_g97)Lrw<7dv#%^g zo9dfP;i=L5wv-rCc$rtq`MA1U?C$a#D!MHa#L99@u)QzpT5WqCIpWMWnH6L2;T&`E zeX`j6H^R)b?qL`cfAP3pd}&muCXAeU>PD|r14N3ag7;Vk34S;#p^K+D17Z`i4fgdjDU z9ClY+{KpvR71UcSyXk{FWNYgP#jkvAnVes`}v2^w1t?zj0-5> z+&kS7zklD|nEuyyY0FFI9LagAyWO86pCPVh`(;W8Uudb>RGl63kj=3Z9sVpCwHQrw z2+=tG99F(S)WFL`rR^~YIJLf~xzfHq5&iP56QW^$OVEPE3*4plmVLCT$2{}o z&|{sj_~7MYOUc%grXhu*Zz^$~f&=$Rkbe2~C?;f*s1@{lZ@F>z3<*;A@F`TSAYik3 zLfI5bO@agoHfglv4@YW>q>v!u?+|m!o5t{vsE>um53J+yMo09sf3+F|aB#5)YEJX| z6OQ8#jGq=B$0?~*GDf$v-#WOn5a6))2Kyv3esf(v`cU<6qi_j|Of61i zejeJ-eaGE*@@L$~tUH07j}DF$7L!xEwD}tGR7ds%O?NzDG;sQW2pvRLuKnlF835r+f2w8!bErAng4 zP8uZz&~~tMM0pS%Y%Cvr%5f=*jp`CG2}sN=$e5*(&!-ODNAy3jaWh&o;A*^4&XvNS zIH7&(auVaSGS~*EZ2I@{AI(W_%dp?(o3*PWreht6A{Iig*Q1)-(fwBn?vJCd$w)u? zJ;3?064-b8yDKY4W1)NA{C-*PU!LU}8a7(LZCniWu|xa>FcS)w-yfotNs#8Wa;v<} z!k5w6iI!389mBHo+}0kw)uaC_hSGO5mSvNp78mh;>2idWx^6rP62~SI@J*su#=(hZ z77oJn+k;5`dR{sUq8bbL9E$~FttPa!F4~BS&+a{0_8h&cVl4=`x>9@;QA%9sC}i{ ziIymO3@k)&={LGFl?bE40bcbroCw}(YXh9ZN-(;3uFW0e>p8gRl`I-9nLE0SI{!#B zs7&DT&wJcgV>KlNf_};NAezyJ#oZ$IpIyutzaL{)$bU!TY!0t(Wp-9pR)4FiF0Xj+ zxaG+H!tv9ir%xW<(x!r3aA?l|wMlCh#OShoi#8C#7{~zGgkQ1${|80>F5Q3lE0^8- zmum zj#<0E7mb6NO;*Mfmkj^eXJE*;ov5qas!Ge7ky+iJc~xB%u9Lif5;&VVX+h2YsC&q! z+}=&$0&R+_d>}L`<=5un&MG!efDp4fDcZKodHVghy~MRLyTAGDd4$!&%d$kEt|hD` z0%mx^zPTcGo{A%+AI_J+&hjX#d; z&zx&~r~Ly{#PJ+qCwV(Of5merI`e+7hC5T)q=D>2%c5P`$Ez6>YJO_MN7dFycLdNO z^bz?8&v^|QJWUj6K1>uu|E7B{K;_~WRss7d*0?4n<*5qW$tFRrZJy5qnN8A8aNRvh z#PHFzx!8W1gtSS~;JdP~Vf@zzGjltJ5Y6_INk^)AEC@L+1*{-F0Cg*G^nQ#ZL3&J{ z1x3bK_wPpbsOruVGZ3A3IJ)ZD6^6TVJ|^%rb(Pq4PdEGl><~rkQ|xEMBM!&Gem;xAyxH$ zJ@Nfc7h>OS9KGUReH!B&?;rp5JryJfKFUOu z_q!PQD7K*Q(}o_91J0*)6RR3XEkZVi7DOz#p!er19u*8)9-baO{|Ad<~^5$9B%*A!J`XP_N>38_+>IF+mMuw9%_ohm})@tagbumzL>c6wlm zM1v+gJPn*(ToV`7e7MR#jGhdpZQtHf2|VNqq<(kBz9;d1cAKCQDBjUu0Hk)1;?P!K z+seMw=7+F@jF|&%FA{_i=)xd?Bl0xP#tgrd6@RV{RXw(86|QiSoa&%VKRR{vWd{O(Q= zijr#l(Iuc_&ho{E;V{Fk4`c$HQvd05zj?X&Oho1~R>0*p&-R-)G>mMr)45nJ*iB9L~AF{W~ zlouiU38GG)P^wW*!+U55eX}_?AmO*wN&DY*(?}_*_8h&jF<8u$i z;!TeqFlc$v##2#Nslk|Cm2ht^P^lf5EF3ZgcWkaYGeqY35J}U8v}3}&3SK_QKFd3zWPU@rt}JGHdd+

$|pWQE_>oFE=QD{O=tT~XQsr3?N(+q;{nCI+R=mObaf9A@wQP=WkJ$J;_`}ZL zkO`tVMrGT*+Axq$D4tDkw}G}3$UVR%#*2@-pJ*>2tkRVIvwX_o>h=}o z_RxwpcBo*q15)eui%uc7A*>i!IxTrU_QKJzJMB~+zP+fs_H{0KaFD8{)bDf*jfIgQ zB3yNzgM^gT64_Sd+;=}AAx^S+#Zm#VFKM>Qyyh;l<&eP!gK`huM&hHGUnuH0SC(il zEg$3re!mQj71cx@Ie-QEucSd04BCf()6o+Wc_sGb2O)rcHGJg3I{iH|^vQy=w#e!+br%rtF zDO3p?V-|c%NVHV$%a_nZa}Nk*SDPN2bYxk3?p&w#(_0)FfeO(E79;~^-Aqt z6zAG7aH3Te-Qnhc^T&*eTs<=lqowT!SZBJLI+mZd%RQxD^_mzH5nBuB;n;iWdzHLP z=Y*0r4+mQe--ERLP?+7JO>LqnvfH8K?h=r{(OXc*d*Q0nTJ7NA@E$8QSCq8(SXnyPvJFeDRqJy+LvOHJnMOLje+%&*M1|OZTjtXFVrK z&0!Uqv5?{nC;={|-qVzl?)LR$=Wc^gOFCXbcYV! z{``X9`_+v;_F`f7yX2usE?O$Vv|*wnNTK&+f*6HW0dQ&;BOoNFPy8Z;Sr%xMy!rHe z`ul=H2>oR&*n(i0(O5x{wF-d{#}&93;H*?m-zuTGW)K|D{h@VyNOYA;X*Dg`QvN8u zbZ^GL7<`V~o1CpnR!EA#Bbg{X4bJ`1DoLD#~mn&$Sr!fwTA8slL9tG!Tc3h9)E;)1fK! z&T;#c@+v~g#|;zH>AxY?HUiD7jM8t^`(Ip+emFZgobo#N(cs!$im7u-Q~Ov9G>r@A zoanS)PBX!hBNk&%>=XunSU;-d_VD;``ZD?V4U zwS^r^HK_=`sjI^brH=LU`2B+lXd3|9h4=!T0k;?Ukn~CwIjJj#ha;j}LaMLdhsP)| zM)7}pps1J=cpl=(anK?;LU}b*^0O72_plV+5U-_;bIP*Lwn2FT+)-CX=vewKCGfZiTlSaWo11P&Js{43q5nSE5Gqj zlwm6a&9?IOInYCbz%IkK`EV5;$MYrCCs&?VQx|L@@?>$x@++O2EN_PlfS3>_DmU zvEF^dvZ0Mgq32D=+GS)4GC?=MsX9q}Xjlp+B6sD&6nj0yDb}$Z!RLQ!C;P+ZxIY2I zB-mjvB=mDkwB#9QW;=1NrGvwP_agf)QypX0FLqW|i&sZK4!U<#>c9`@m6hwQd1M*_ zi})x%uwa8>SZ)}WpA{F~L_ibDn7`$T9rKM^`$y}oFQ5e}eO=G<> zTi54=4jQ`ZujaOZ;3Yxsr0LuR!uoXkm=o_Ddn6KKwkhp6q8;QMzn-F!;0gCs#Ck`J zW_u55sOyzvT8oY6CdX1zz9?~w#wFFUIqs*jedc%hq`0hJNDsNC?EoP&z2M_Zdo3ue0YYG5Z?fa@11u1W5!y)1+~G;? zj}m+0t1ui~ZD`;Ahzox`v%A}Sk<$OViA#HLB~*_=+hXaaa=GxCF{Zo#AMbIz=ExRj zl*)BuqZW$yrmtp=f22~g&WFfq0B&m4W6~X2ww^{!MFs2P%o2A%cRz z*K5yUPR?(93+jmbqV3L@d^wbW-$4BL`dXD`^&~<5l!J3PY&F0ed%B+s3PErbV$A5e zEtq&x0Mya#eCtP)Nsb^>2{&4UF_3;2p?cGYdC$~k_i|)!3l^`1#4iuHJ?2n8mYvJd z{A{x|lR8m{^SA|s@2@sC+)n&$F!SWAp`m=4;g7eW9|Cy`m;%qH7?z#Ql$0Kx?GtO= zw#@Xj<%-^aX`#!FQT0;<(Xs|FpN)YH2#L?kKi{%ltqM~b-s(Cs52YPLet-_JmqU?N zmJc29GFkWdJ9@0=knyQvO;Ib3bXhw4m5T58XS0h$n?)iHk931}22_a85dC!CRDJNI zOgP>;qf*Ai->^5a$Db+p-LBzx=W_|ddAo|eZ8l2W(`n9^ELm#nociAV6HtT z^KHH*vmwWIH!CYY+ze*1?fk4gSb1Je>(S49IX|7ur*IoYx5xNxB3`eSkcBJ38`?f_ zMICh?kY^hk-9?RdLAPtZpKlA`l2)6V-*j33b)u}T^n1C}%^wbM0>1XnRy65i_AX+l zU9haIS9IoKcF)Q2Wd+P+3;9SGI}FRrA{a&Mga4;(Ec9x{nrJ_JUXJ;&-SFxY?X1h zH2JoGGb-s+gv_dnP`*>c-nxuQvWdFmu?F70g>YNv-%$`HSuEe~l}e_h&}z<~G%$R$ ztZU*+UtcD{ap+{oa6%_^RXE8FN46S-{+EzKujT($$yEP6qKkQ9V1?)aoS)aL=nJaH z-Ni?+${#jS&N^Fj*{t!3Wo>k38a1nr7aW*bpZiMORM`%0B;fI5tHlRdn*#o?Ep2}W ze!O`%Ysq+$Y|pwR5CS<5`6Y7o`*Mb-R9q$qU+KeOZIWYzfT$7Jx}bEXtP{B{!Zm=( zfB>r3uJ>T2wqHR3BES*?_%1}q&_f?lz!9TQOZDU8N;c6uP2_tr8~6>PI`qT`?DtwZ z2fo%m9&F+RH$kNTI31wUDLw&0NDx|n^t$W<_f;nv8-$Bc$UJi#@Dzl|D?JOlv2bvG zydm+`GDuC-U5MQv9dMi3z28t)6F}@5rgr#3yE%g z^-g=JBBd!IYs1vpu=?;x^y<>?Wd*sZlvQw5IE4&fVp0zCL@41mOYO z;7aE^>q)W}c&4@-J(o4yuX#OOe}-tac&dDw%<_WrWurbu0UmFC#GGQmhnRw;%683) zpUtLTQ8iZ=J<9t{UX_oRu#bp#=6RW{3RODluHE24Ic2{y%&t)Ezs5^;;-H_BoSMF) zPs4-t{-?7FpahZddqaZ>Ssh6Ai{dDB6tA>{Da1P4mS>Q;RH?cf_p zB*^0;;E)8-o0A|eg8mg=RhAaM%0IWfvIA(4AV~}R&Y%{~DMXb1v22Qfy^FAX(H7N? z=hX{Dnm_h+qSG$UlTZL|nd}@_&ru;tluzAi|58&P^F0MKw$L<#r(a&o);z_l#xb;; zqU39;_zB~U1|s0QBUV&pvE1Z#48=i9v@~gG7RT{v<$*gU&z;yz6qM?%%s8bK?p%4S z+6PY_$T{##OpVB`Jbgc^S0z!#ao`z!KbFVh6&(|k5(O>&TahH!i&7!Ip11MSYM{HQ zcD}7X85{giCnkRIEtTBWOKrT8IBKFBh^pLI!3qlO%RhZ$w0Z&fBPo^WS}h3 z891%TBSAhWq{DH`L|5=FpSb)-X*~g|qnn$1MPXe+IibGy^PLTzJhABdE}CQE5c5G$ zmdWHfTXt6}kc3<4ZgNDcC>vCy+E;uu(~GnYf2FB7r6PrW2rAJr!U?!-u$@MNyy`&! z`Q*V2==gHas5nsfB|rgC=;@vykDnzS|52^~a_zs}=2?)Zwb2t2r&m$fg;V(Jg_B^g z3+wx{^YGW2=v4(G95L2}CC26wYc~df)&Zb1N;<&NKEMh$kP$n6;P{|Rj0vVw`Npvu zRZu3a$?M0}@J16NV{cq&v_#GgdsLihZowTvbS4`9VpcC35tF$x{>JI0IDWc^HJV!W z(P$O1!|za5cZ>NFQWAk(gzxqcia{%*1WshNO+)CG*=n6$h!M); z%K-wp+b&t?RM#(myMN(L71gs)`U2sMO1nMRX6JO@5ina9dP{`*$77mT`Q48!F5p!K z%110X#B#Ow^L2jNPe_7767~QlA|19G;oHsp0x!^>_SyUW!4T1wt}b1#^C zXXo^NSs(de7Vc7AZM>3lkmG`KLwCAMZo>u;ooF87Gf;@$SF*_YH0t*qQEHnH$Mbid zo8Z5#_5A`d{n=BUGlv`*xSH;j&3%IxKfgcD2ot(asK*}^VUfLIX<=Wc*T(Bqnnx~h=_uNm#FU~;GztDoZ!35#f=+Fv-^D-JS_y6eg}R~IR%lf_+YL;*Z6t_84;m0TDobKZ{RP{d9YSLsED%*o1AGYy;ClH9u&r{C zf;29rD8zLw6My@5%%5^0Dv-O0u%qOGp_k^3Zg}dyt%jdFgcTG~4cKOnY5kObXp~o2zt>wh+5LTp_3E=NI^tk&?F$F=xB_#vQKsNwP;@TTsVL z@B#F3(;E7aqM8Irb@|-_`wJIA$n1_HG=atJKXA$kq8(yzoe0+N*r5suQe%ZCjQsJ_ zl_k6YDWW!F_3+ei8?nX<-vI4aJ^T(4Rp)%#A^3_^t{rZls1za|`H-KJWa04YD zNc~2~LSROJ2T&L&$TK}3{k~&9IL7IZxa;rrgoSDEWoKAK@@T5wVl{{zd{Z?xUD0h# zFSzhHQ@?Dsb_O%h=ghs<^c>G&5D>=^VdZI)q|wIbYSSZ?Wn$dXqG7aioUybv?UYpj zn=GqJTs$ZpVs)z?igE4Aa+WR+*^uz4+T)%`VgDq`9d~A8ZJ>WI&W_m&AExFmB%sz; z*TJ*us<@BJpN;kWB$Q&5so)p8kOK4Fiv(X&|GH-BDRW|m!*yLT?Q>7!&uD8rwH?U| zcuUAyb>!}neKAPL&-%eKyvLxpC7>I4`=TrleTM#YaYrq;@W<-njx!fOL_PwBO6_TU zh^PX4n!awVdP-ijZkC;g#^&ch%P2C@qJOwg13ZX+Tf|Bbo`%q|hTNxNR)b2ARiJBO z3&RbYO#!o)*A>5& zFy9{wX${2QG*>30;RBXvFSbi6CSI)o=0l z5cTvhLZ>F6+{{sSKF&`gzFQ!=>=1K60kX{pDvT-lKh4l4(}Jxhp%m2%KW+r+TrqKB zU+oSEfDC^OoNEtpRzG6z(Aw2SC^Q|?g2qtXS5N=kGGGD-(;#9#%Pwex#r(w^-$Qhb z!rkb=Y4sY25{>IKH@X9CLtl=V*Sm1GRUFL9*cgv#iwr-t6fNPU^upPj@wq?PV)@Q$ z6t)KKM)qDJ;B!qPC?&K27GD5! z;_9|F&LsEOWRlZU{?d`rkG!6r>pIB;KQW0k1bJFac~g`nXTD!VB7GR?JyRXUlx?0U zl^FUYNk6@n9}!ya*S*UvQ<+dN@gCc$1`I$6=?@f$Wa7yE>uyQQEBLv>)1B`J@D(aT zmLBY2IE~24tcJ#d<|P&i>SGc_6edtfCG0)XGrV3`LBG`(ugBx->2&V;cc=9RmINB} zP5Wm*(?1B*Q3^`ebVbjDsme&0CA8tK6a4xvq-CvNvb7o4=pIUDi%xoJs$wZG6YS&p z?~!@jD(H#xxVlr(Q2sv;qr~YYG`P2Msstk8tR5&ozlHyC>?Wm}hIasCcrtq5H;Nb| zfL_mZNA#e&g?a|M)BNQYIx~$`81{;a)+H!KC+;b)y}fQ=7!vcgMCIl+KCNeQF(TY) zF(GOcaK$`!3|!JXs&Cj(3AN63xNfIAEzIb`<|<}>TTESPTNN07B_SOCw_8VoM8a3C z32k;H$nJl)>&l6enIPULH!=`a)+PJUSWtlAEC4hK0@nEnUD#C<6{dvK#X~MAJ{pOO zT!Z3oFYI1NYFGNRD^Kk8Y|$8e4bd8|ykl_X`$(QR5nz^SXI?LmRHbHm-oZtxd}hdmta%4YjGW!!7;18DgIAJoVMsOm?;%W`Ph%@ ziWdmVkcYY6?mhB)T5;b>_2|_7S@Xe>P-z46jzYk1c75oRmJ7vq)`Y;5=ebJoSA2Wc z;G~HFf?qlt&{(ZU2zV=~-5%diXhJ7OxqFLAq%J+X`rMcDZAAQ;D)2dI-&)?@w`iex z2juq|pwssCD^(1B)(f*uPkDr1+^BFtnM(U)=KFz~0ShH69+DtcL?j7vJ&ijm?c+3J zmhgB4_9q`#CF;SCz(x`0!T3As-}J?5isXZ)e&Ugz$qq0C?#O~rN`iz(Bf7pL9o(4K z;1u8h*}DK6P@LKK!Vh}Ewc>=r=v;Y9gGdmjJ_e#i2?+k{SS-2=j!X5{BF>1z_phbI zdrN1+cE}t2cZRJ`lt2^Ef&}>(etP>Q-vmtf?%CB6_#V2LJ#0dM@-uVV2{9>;|`}>#QH#j;v zwds8jT^Wk?IqV`q{=E#CSsjV0-{%ezL21BOgr^~*T)-ZHv-(sEqz^e~P%nVDT+@gI zIlMKB*vy7rL;Y2Z;AX`C6jG(oaei^D&OKWfW_nE2h8BlJ4Rx)R6~iQLnbUJ(mFO}i z2X&I;Vz_1-yp|)Sg>TYctwq_*TaI#$i4{#WHO8M8xeMWatEjDWM(3$&v)O$9%eu(4 z0^6+v=aSrN$F+(A^_{(+R;=dH;%@#XxZ&j_sGQBp>a&qBG6T;wWX8{=`Db{a+}WF> zeuYJET%4Li`>bHCdG*^`BDD>HHOc{|rvt-cHv5hO#?W^@@4w#p_1I;f#{6P|D5?i0 z$E=IzO2ASH+BDe=aUZuQluj#nTXBsov!HJ0%WLVFO}@QH8|pLbM*Sgr1)g5lp5Xr3 zF6sE``fl9_#>Ohw#w#OP&*yqg3xQ(z?=ks5z6q#+CBeT~s$MuAnui!Q@|O0Sy*f-> zAuv}0?0-xX(3}gWf#a<3*bc&yLUcne5C-IWe)1lXS8-E}dQ@Mp%)^n$!WC)nuD5!n zeTJMJ1tJY<<>D$iRc6b>ER~gVM@P}g4<6vem~xLr*On%$~bv~%d zUjSrK5Es)y{ONIV1eF8GW(QgcE=isMKdg@`396)d|dJohOb z_`5CX4o3d;5`Uiy{-=>ub2pV+4VnYeh3BrZD~S%@)qbL(`_kee@h5jL4ELQ86?D=JnjOxU^#-G+L=33LMkQC| zkGx*1K=J}kJQhQaf?obFl>{%R z?Rviac9nnp_S)J^|~0@!KV>$AK-c z9-X^1_hkdG4w2mAFPV~-lGQ@=`vdmsRrrB;zezKTob@``ua#PcFSFAs9N{6&@hOTZ za+B|Mt|SkGzsy%|!cr30 z1;(Oz{mP5ICHjAx7#OM*D1EV=Zp-0XR90qmX%Lamk7ge^uXxVAM5_D^|7-eAvyb8R zTUvN$T#EtTDc7pRMu?)E^ zVSfd%6ZTIL1ba6_90kCJ%Thr_{MvI;pgBJ~#gfcio7SY2q~E zNi|gC_R}+?H_q^?@bWzizrZ1}IA+mn$L+z5iDL22?%Mq>TW!i#Sz{hE_mr&%%8I_t zt46(O(pdi0(kPhj|H6xrtS0<~5}zX^NRXc}62wB-tfwpc8@fSurG@B9P1KlUCz{U? z+3syHYwjRfF@K$V^1>6{qQG+TB|&BoYM@cpmuP|6s8>5de((i{Qb-U&-XY?k3rSRM z$-EAt6u36XW~(qDcoe;U)u^1CTW@M%fhuf{1bG0%!Ph~LnF_dnZ!6NGk#8}4vgm76 z9~2ki9Y&l=Lg1`+Ed-y64I-9nvk|@SXhMb&;Sk^>YN_!eW?uFXKU0fDamRI>{ydCo zJO%}YEo_+|u`7)H+}q}Z#c=(7f~5rw^au&A)01OS8O#SMpo%=L%+257HWO`~F7Q=iG6G%U&J_L3%h?{oDNS!NXh{I}D85Xy0f zs~t(wC@g&Vfqdm^n)XO<^mJt}jqpce(%a}HWJ>09!pM;2L_&DM>mIysy_)LCM>R5YCmZlJ9Z(r7v{dr4(=f*C3C*I#4 z;0E41`;aWEA<6+u^O{uy`Rmv97851A>3xesD~|bsL9TtU5DhjbqhO=T;BtD`%PQ%w zxI6-_q6k7W36jXCcKj@gq5=W*!a6gowabRh@Jw?#$M1U1QO`{dP+Z=&X;|Tvd+Sj+ z={WkDB^Pgu#oVAXw7F6GT=)|M%U@?{`hW`uPq{GQ)6kL`(1^N!U6Yf4pQ(>dv<1 z9w>wXH3B#hBMjRu97tZkhd_@9GyjMmderGisp}fqm*?Zb|8EXfyz=%*BK#*ALD^GZjzUHx#NYqEPl z%%1>BU6OL;P{+F>!-~^IUxTgMox&%u-M!%O6JQS8V_5*F|3$ndh;d?6Cmj|`=MbD$ zJ5c9!XJRllIL_|?j`OQjpT_^e`wM@K^T&bX{3+l#zZCSE5bp2qKn(r>O_!3?3BpHE zb$v^3A-bQ2xa<~eoW*h_TOakbswr_8y)Ba&|c6#f`@JgzLnyN0usSJ$aj z4%W>8lhFV3nD$Ame}U(~Czq!;uPdCpUVQgQ7VCS6j*iB|FBh863|gs9;fv}Od$-%> zx~yI8lw_3d=kZpvppzGpOkNO%ah2G3!qiKr*C(-@Zlc+w>A~;uZ1V3sR0B@2fLHyQ{dr{E8 zXNLau;y=+cfeUqfU%;XLB8b2kY>gRqTm;%qYIqdU(61b1^%2l@5|m{Dx8R%LQ9Po2!#H~1?*SYDGA1CGU}(MzQ5DZZAu-M^+j%h}d9HeCFA@B8P^MV0o( z8=xaKmyIRhk%X0`6-#s`v-XJS={?QrVZXN3lj_=GK@?eEX=Y@hSIZ-zAv~CwQ-j-p@QBlW$RP50;aV~)aBm2ukgv6`C3NtSVtd$>&;3r z-?ZR;u-+4xv_k&mnz-KWRR1*5Z%VJPTgA#lv_kI%o`7zj!OZTtdkc8(vUzT+A$f@3srzi#@Xv zCEQUgQ`k@yOHUffiY?`x+bN1S_ zcHDD1tQNI0U(ZcZ#!H_AeMX1lyHq`RehmHM4E57w|GBr&JJ5H^-e*80j$ zMlu<5C9~!zd*0_kbF_DNe-EStfQCgnHNGhZTf(k2N|?#&SvBU<3f;=BvPTZz_&WRns3Ci;o<;4D(u`e(Dp^~DX{ zqli0_d*H^2r*oX!UnjusBe0$;3JEZ&3Pp!~Pk%m>5wZ;O-mfg)jy(~>PdNQwe9W>e zSS#o0k~b0*&sSz#ijqC>UxD>98FnTO2z&|a6aQ5(`hVE_#~@*%u3HeSr)=A{ZQHh< zvTfV8ZQFRtwr$(4>UzKFn0s%>*E1dSWBznUA~G^F&Pl}Cx!2CU*J5smP>BQNCANNT z1N1NR?wiTg%>CR|JKk!ZAWg4zH&tpXf2_gWKi)d~*0#3#hnI-mItZKhtg0{?I*0lj z1m~4a!Cz6uEUwyVg^!^??LDkZQac0swXUbXdFhEhEO$h4~22diUZ0XgW1Y&q#vIJrrdGKnCUkMXu1a_ zxRDCQxDm;Xoc@ZcD#;6g24SmsIdID+3|?W!*O?tE30A=f06XEIgz+)`2XJvmA6I5Z zYmH`Pz9A`OPbQrCq%J!RlaHl*0 z$-kE~KC#Wav-tRna({_Ha+EQgvj5`dB37wh{&^;K{!)+q0%!XzUz5&}`F(;@Lf?0* zul{&r8lEE?@2UUCj~MH0?+G9uli{+{m~$=5o9>Qe#4!h5Nkhi-{;p^hl!v{9Ibfr8 zv$p;n5c#vJ6sm}quOjBlFPa!Hncvko!)$7m!2%~r#}9`>5ExrF2gkdV_0|>vj|eOP zDCk*}>hX%0)JTsR3MV_8mU%mPGiN*AIcjM?%#;!3p?v?!%jthMrh(5`S4mdoZ?>Ts z4)Rx64lR)+jf|}rI51=|-WGb`_K|yF3F%~M-0HI-mdFzXLP>^1Y+f&Dp7{-cOf&y3 z)NhIRwo~u9nV$>CFiZ+4vq%nvTXYv50)s`D192?;UvlGx! zMPig|XMDd7R;H$D)}uJH43TVSOYDV&qochGk0^tvb zAKUMvP;&61=_l0h_vb0Dg2Ju=BE_yEK^p)Jz3|g{kozx9&N9SC^jIP!+2iL`_1l=^ z9tr?%jN+^;^JG63cGOInNGL)VxK^17X~r%CG1!0GmA(xqOIt*W(^j0Nm=v&oKFIVt zm)=deURN`kK2m5j)ZEk+>5gv~RnoGi+B-_% zNwMUR7gbVtwcovGUfJU9RS8=9^dTJJhoF+x|JX?nA{b=w=)*?Vi8}0yNokluC*Zq! ziNL7=98iuafHUfVprTDs8X%DbXBA5`xa$Wc1BT8yq-o)2fF#;!GHUwSqZ%ic*n3RK zi~p0Bl5$2++Ao4I=m$lmTBUwQ?5ac;Zy1~|AZaDcDWHl9<`#Z;YY8j%W1>jo$Alko z4e-IHc~VOCjzuuqGQDJjNwWwqLu-enC`SDP~)NyuoA`=(=@b$8~x zL`DoMd?(oulcLBRxQ*X!#G|MKl?y}k@OpE%?eIgP6?mDE`)n5QQ3CQ$)AqU5r0pYM zd$bL$$)?=^3H9V^tkTaAd@y+Qd$nt=>vMpci^prbCA@2m+eWB83wFcxLoXxqOI$<2 zw3rf%*GvM!VwGZ#o$5$sRQwH+OQa$JZ1YbJH*8IV=l9w7!pV!4IJ7!WOON(sJo{|h*V3#no(8GOi!#B0(`~-Lheq@q@!^2t#4P4@7-(HKQ-TF z+^>dI0^_bAp9%w2Zf@%b;IhQ`Gd%f77#;?0SJwT5$FHRC+~z&`RN35_GjgxTSmT^1x+!6GA;v z@f&*jAbL&M!eFFh02;@IU1xI+&o6Uac)Vf|0(*>JK*_(Be!@mr#aLELB6tjLsqB~x zBu?3H2NQ*CGjhoVZ)$#j0E!X}LH}F(#PUDvlbpk^>b<#*DY=BvFD1UYlRE{SqOqyD zqmzR>`G2w1Q~VO$+u2zeTmOeKpZHKaj{a}2^?wqe|Nqnr1JnP`&d-3)!pQo+$hKXoxmzKx;Cy$pUFiIq zz|GO|>~rgW!Mv^CUKrNT93Vu8PRm&B%VJ%PtPkV>N|OgOD!`wPAdgFma6l=@^8>V) zhtyPoEV9U_F{h%pMj1!I6c-0C4l-=r!5T)f7ymH*8RPc;nCf!BdGTo-84ME_I~t^z z-Rx)xx4m{ieA932VAr z4vu!@9Ikf50z42_Y_gt?aB)c&-TQL?H`n8)>#5yY4|QWoRT|NVQN6+Zc134`5&Dy; zSbUL|zhk4pDt?}Dfh7+S1sw6m7-tuh(oX5m=|F5oCX z(b3(P^4KLm?^qY6R}N*dxtlBYs(5>s|Q_d}YLBI-*TW3v26v}e-MiR1XdQlV}ANk@Lk?`SD~py>Ev#qzXJ z>DX1UZf`4!?QAkQ%VOv*s$mXM2_e>H8Bmu{=}YA4T)5>0vpTSoSb49VP{|-VzdJu1 z@xP?6G$5R(iM}Tn4TJYhh)+7(VNCt025|Yxw9g$_X zzt$aLqviKWy;dTNpOWaJ`VbaOOYXQ|w!=1P-^7Z;s(j}7xg zpKf*7bOs;D{%MR#UT}1>QMoIZW*w*)y_}{bd*#_7p%g}|{hSAFF3Kzhaw$U>!@n=C>so>|?k z%;`la=q+fa^sWi!zG*O8L6{=-IA^I@DeF@g)Nu;yIFmZoPgDom81h-L)HYmk)6-qG zOsX^l9mez##xd>XAhvfe4xeW{V~IMLH#hbwn={kSaFBr#Ej^~0(uk58e_e4s=)C*E zC_{LRJjD5i{qY0M4=kuG5(zv*t)?wZ8e-(7(@Ag6=TZfooLaaL8&)YgN`Z*^* zYcYqJp6H!Xw#M93L~r{@oLTetyZt_cgxY#7rGkRV%fsCNOW4>_1=}Ga(RCFqE|AV! z!%fN5boIssrB2<093A2#q(PhVmy9Lf?}csh)UFqC+&EN;o#_$d6s(-nKG zE5EABnwKlR%;%;X#d;MXNEYI9G$>lN^txUG{L(PHprV-ZgV?@LTl4YcsQPVU6Zc^J zyx-1d`ZUg75_6}=Ao9xTqoT>jJM-{1Yx5fu1FtSDax|A&ok|JSNGOH{^lX$MbtWj& zid3LMq(0Sqo7tz)Imvf!f0|Cs_B!KL`#CruuAK+|7$~aN^5}Kwxv$VPjsFH~7VF1!6roh*ej*;yWmoisgg5UXG#1xml?3eG2;M0!dDJ*wZVXm^<-mo- zb)W&H=cTimQIV7g*csNj7B4CDVx#S}htDzA&05hnHo1o8s%?Rrfq6vok048}6e zzRIg=g@uuk<<3Dzhn*D@=KwkX6Tik=187c9&XJ9n?43FXsf&o zaE+q#%O;yTnD{}qWUHOvp>a|Lg|BiK3~$NV>DFu?7>kCo6kxk!CN@5Jw9@&=F%1{e znPip!TE$-L=NM7M3jv6e3wF@T_~O9aRjDn=EU)&i6 z97m=3EW*v}b zPm^PjiP)9Wabm+Fs?w#FaoOsFZEFc}K^R%&WD1%|edIeN-%E3yk=`&_GLPoSFw%e^ zLWP2m5YhsknWoH70U``y{tq7mY0YBr+^#H=Mq4Emk|ANZs5U0E&w!$gdRR%}%aq?> zaHNK`!%q9lrt5b5P4pBgV&iOVs!{uMb3DeG-oM#DPu+o@TC~rI#K`zqTI6FLC6@WD zzaHl-*2KTJ0N=;7L$`G_2b(RQN@uES^i{1qmDo6d78QhR0f7mebDfC9J-Gq_h&ka-76b*5wglT>|Q~{lhWLc zCX`9(@YDHKf6Gt}Cle|3GYZ?S`1K+`(DDLMtzhy5nRBgfpPF%bjw|+~hj-uA=WH00 zW5BN!j5tj5Oy?;e>q9KLE&vd2X0Vlw39Nt>;-e)jKtLw*5SDQs1HT&KB&9f8*2el` zzH?e_q0civ<^?cxUg(umzb=6_AL;^kGVH06LvqLfPLA`ukTjT8UbLmT! zm8~pjD-cH$qBVrfq;W(Hf!4aGKr3RM#`*#DgZ>;~*){c-0~bTCo&y6cz&w^0EP}-f zCj-_}*Agh*M9RWd?jWdP?VITXS%#O^rS#i?j{Fn!HxES-5rCo;dnY6N!w1N~q#1>Z zpkQbF*p-C9xu-j3`N8(WXH0xCVL$>_pu>gy!MOs8RDGB{iWrJ|X(=TICYE2alqonI zb=8m|TEmTXbNg?z#CY(WsAf?>_u#o4hPzDZ3-$S!!B*mruoKQ*d*L)k6gsr8HMh;f!eiq! zY?Uk%(=ptsdR&yATIIgC!GNpB2#&Se5_eL+9tXZ1kOHd&{&%mhNwhPRf|qLC3Sr~I z!XoJ}^K$|IiX^FYeUBPYDxD$pDd{`#4VTLr7x|o1@z^$_`!kan=wi78ym1JukAIm8 zm4d(=ch63X@8@2yekpExjStNHJJCOYu$s@m|1H8{|G$Ex7+C-3SW(9Rf}Q@`RyqDJ zwaUW4!SMeEPOJIp zDe&^SVP4>%3_;{e%0g+<2WUvm=;g%`hBQK142i3M&%vD6i^{7d&dsFH^Qwh3e_x=Y zut*= z8|y9Lq#VGi#>zNEdMVClH8ElHnLT;-1xb-p$n?# z-}13wZz8`0A`ud*RHBM;C}om5?ovWP10_Z9Plx!?LQx8th+zyq~2 zlbd)Pm;bRjg7W&p#S_H&_IBUflqc!Aux54beD6B!K+gPmLg~Q#^S0A3WLE9X*S_P! zS7aM(`SK&;Lqij zE{#;mKy?q@;~; zoy31IPNSw?BL;3*suOJ^3X|9}b+$^UVS!?RJaN=6LE<=Ga2+fTp#W&ggZhhS zd7CiC&Zgi;LE&KBL**GvwPV?S^oe_xz-si@WmB zDW|c&M}qV7svVu;cs)C2P~4CxV>DK$J@GJ6v|*?DEx&e9eSn?_eY;Tj{Y9Q}H5r$L z$qfuA&xqR46>M)E=p%FGHRjf@UCAQ{!`I<#kX=qM z3aRw8RiMn)J4uvin)k1COJ|enqD8d}+fio{#vwcf36wbP)!!n8MFyLYeVnTT@4h1v z1)0vsOCf_MSdxV%7vZy$*->X!&KszGBSN1W!7HJ`i)L~CD2f$E zg}kG6wDidFX6B@Y+HsYi+D&`x!mSeQiC35Sk3j|xj2uKX28o#gw{GmhCsTG;NSBMD z-}>~4s;^x#{+FFcWwNbcP8NH3t|5i-gNhrR z1d@0Ap5A#@fgx~nKGBLHw-%*Q3*$;6mHvSis0|s!?hwoMA&&FKPIWwIy%i0^S=+o^ zeZ6M`ZvVEZ)}xP6kQ19aRQtU)EMlEnVVa}NcTq8iT$c+7ZYez;#+k7LQI3Qx%zoJv zj;V6AOQZC}Ef0e9qDoGEq~Z7b?zZ4!+3S{Wi%IsmfKMzr%*^c^f^UPlKE!^+H7Wmxlng zdw--(lw-SQ*YIQb;ZUASDhh#k1Hc<|=H&>Z0_`_TG zG>vE>6OsplInMgblVyUM{P>g4Vi&kjuT!GBG>1ar?4!ntoFtZXpbNHz{np3D7?EYG znmy$CDLY`_Okh^~|k;C=!v$;9zYftS2;$o6o?9CIr=waXQ`;j&V+Nzlm12Z7PkI zr$r%%V=L-I*JE^JFV*2Jy)bfjr0@;$df=yv(Ssgg22l|Vq_=6R%>jZY zrk{n-Jg>+%v7h1z4l&F%@I>HaJI6l>BCL*m(!9B2OgZKED%Ioela>W3-=b|R2*-N# zlUd<_2OP)s-!?UvGKA$~obKXF=!`Q#g}MFVQwRkhccN4m@WVESpdT@RcM8Q>GH1??xIpt&tB_OA&{YuLdjGLjif9 z;Knp+E}Rvm`DEjGWq1?5ty9SP`0%fIDH7^)UyCiJ^rv45-d4#(*jf_cyb|uz7h(;P z%2u8sRpD(snID8Bgq=2ilH5%4c;VMO*uN8grrj|zyUUgx?fs1uQk$1Wy%Aj!*hbA0 zesl8O2)#qU_xE@nxg|B)%^Tvbij$0uD4){z2`tY&-qVFp(=CDx?ZcwZw!&Js z3uj!IP?zntO3%oy$Vn&ACk##>&Z1In!wo;Z8U0_(0C!L-wzmXX6P*t!nnUEjzrl^Y zV2-|EMNq%$g4)Ris|>w#27Zx%`tUo-W2@wLAFzGcw{udDdaPALY84U4VQ0-_v!hY2 z*fQGO#gmNt=KCP{GH$C%b+bBOh|%|S_|gSM?Z3)i5x&kSr}Ofto3(eCdyWQo6A+`f zA12iu|FbHw(ld!Pk)L$o{v@xMg8~F03^@bOt=j2n103cZFaM&yJ%70pdRi*5q~?$L?+M=;g9yz0^~sj!^jpah`)@Wu%2~IO zkX0V{g-oi|ebJ0Q&WS1G<~z#~D0yhRo$LGuV_G#YOFW#nRDW886}-&&L^LXh5188_n-hx?P9L8`J7RSPom8rs!laekL(VzPOvzhRYfM%>Gzei*Qxj9ryn+H;K9RMs7c}I8PA(;XQ>nMmh4e%Kd&VQoSbBxModu z*{*bm)(kjjnJ0T%uBl2Mrz^n0BS*)(NB6Q{FbGIG*t7mLhb3`aW z6ff00*-kfIoEv&beq6RN9`goX|Egi$?^wLgy9cdy`d}muw&at9*Ei7;+n}IXhT{Y` z`4)849(TJokwltv5VcY@qHV`Lk-JJqd|B)mg-8tQ|QBgFN z+7Eo8Z#~MAU>kn^EJ={TwxUS&g~CD7N;RB{9Z7|RY|6xI-sp`7&nRL4#XgNgKva}% z9BZV}atD3fltdDk4I9r@)4x+)=S3(*Gj`K_djdRxYzq0HtN*hbJAm7pe4~EFs0ma^-X?Y%-<$x1v=umCxE7uy*QB~Umjo-6XNblofV0At}On6H*>_c&3DSxe>~r=z%3a~hl$sPIZP*98a=X7Hq$=% zg%M=k$i5;~O!SqKwxqb@nr2zmbZ3)-x5{!f)NCXk?wZ6H%(<`2;W2itz+#BV=58O| z4h4Jm6Y(#VGP~a)_IwJwc?PBEa>iozLa-b2L`+jtCIiw_u4MX$J=9xB6R2HA0j6@5 z{e#;bx}{~4h-YhR{-5Pg&*P3)YWoJmjnpU9G#zW&`SxI|pm|lt#08{#Lw5Z~MM=|vqUp+Q26WRG zZ)%sD4ZKs3qXOQ8y(1kV{T9>|9u~hkfO+ig#6vHnYrf)F_r*X-cSmhtbq>O>8FOZ&Nq(e{mnf7%lp%1zBkrS58 z0GyWS( z5$qdKU1OW+sl~+~t_I_X(?e$I2Pu1|U?6(#QbvDV%iGOqSzy=aw+LD4K@WvqKuwwH z9!BP#2`x9L(gsfT@IkK4K3O>uUYJb}WSVpcZVc5Ah+Z$nFkG_^ORQ5eJi6OMX^Mw` zuzCs=E#?>D;^;VUfPt*1UGeNWCvOM~vgU8_ymq^%*KdrywGLy*<&0ud=2kV)J_szW z%Px$)Qy=DdGh!KM^xKk7J>@$DVIGn;+EFKvYt?c4N0#*i!>^T};g1{3-za?QdOk3I z*hqWn@sM(l3!C5b>UooRx1k*hQ;Qf}uS!koirgK|f7H%~J?UJFrR~7RphMla{zPp^ zSWFVP8=Y{BwT1y*I+H(9XirzuT6P8 z*3CN;BgDI~S5Xy)wDHY?9X+t6!Y7|`>%F-)`*YT+hz0d;Lrfz$wkE-S8`&(Vx0{z8 zr-jPC_i&mn4(pbLMsNr5MQKF}NGe4+z1|^_1OKd~+sW~p|I6s&p+e9M?{Z{h?Pf60 zz!!5eeywK|*-Kj$(f;R!-jH_^r}pob7(i|)-$a$lYnpE%!^z!tm#CdPpaN%z8N3_fsP`vk?m?!Im986VFBW?#qsc)gAC!;fT-a zdCq6jgRXxsP+43@48yB}<;+4{CZnR)aty9j>Jy_sovpXrvE7fvF}11Ezj9ZLb3K|P zx2j`=_nF5P4Bb(?QA_E1yQxIk?KnU0GMtVIWh}i(f=ijImLZ%A7LziHz{Y0N@vA09 z?r>MPIZ^xvFif4Nu6$1~6J|;TJXs8k6V$1UwSKH4)FLL>9!Jhim4bRsvS5x`cNuY( z$fMd$8lEt?wy@yt=a4wE| zQk$UjWG#v{PbT>|_kk8(f*l*+!-fMPNlI-GhoZ+y$9m-H^WHxt&z~cocgxbudEs7aZjNHicFv5b=io@_B_88=WKv0zP{&?k=wA?otC|JSZbE4RT zu4{5hLISa6eLrV88U_d&;QSUF`aE1+uYFx#b9C-}^S$f9aNX+Ac)x;DTXzhZddla zOu{_&S@n=mW!dF@KAD)j68QcFC{o7OZ}SrtqFo%rI`$CHSs}A6m{^9!(CaG(y?|F2 z^?Lr#(W|xm9~BB+MKCUCwI*^SCBVv8&fgr7Exo4o0@w)>2sv++?kl zHuq$!1&T%#)`tCK4I8^dDo8>yQOqB02@l@dl0f4;)9xol(4Ej)l!#lPEBzcx1C_Z- z5Q-I?@M?-hS??O__04z7=C$AMvpp)_)R}d1e^p-;hvH8G2wSb36cETKH-f$2E2NDtcR20WsmTYd<;n})s;Pji^p$x8h37tG)spy=QZzT}#? zlzg;3D&rIIDqP?^>p%Y9Ie-=>@>HWWI?g3BMA6d5QEa-JyHFfi&h5U8yA|rqS!_LV zv+3bEh8nEM-yAN%wIO^psGYR4`(IsC$L<|-{n-8QM`Ak=PNq5-V7^pYA1h@$FV=&|{7$NPwYUo0YTT^>CvmXp@xyn#~Blm^!a8)vGp(XhOu^vI$A+iF0Cb}%} zr8;1Z1_>f*)dq#<ekHxDJbj$0eA*(}J7AVd_=F(b@Pyr6 zguM;?*IdFI^{Oc7ovO^y875;ppkOeo@%zLfe1Sdlu^1)@V>E1BUYYjQ$=;ust+^e; z4Z~N<2g_-$r*ZZjoDGWUNqax2BkzZ1yr|zAD-{Tz5 zcHnTFYaZ*Vh$UuHMLN*u&)D?#Va(DfO~LKW>A%0HeiYcgAAvOvy`rMGR>H6UIBPbX zDjCrmrb$t)&tIuVF%*p*qGxP^XFJH?ARY4FXHb4L>hnxWyd*&{tLeW819cOXb848d zhZ9a$KQV1XLii7^`;`^e3vH}1@a)~Qk9j8TY5vG>YZa?{H2Qn ze5hH(Jpv@& zJi~U;uwCHJE7}&_ln!?IuciV827?(dpehK6^nurA)Tmpw{b|!Y*hG^r{a)hu6^wM~Y{8nOE=pVP%eK zQCL+g&pQ@E{AeE`P$7#jmM<;R6xn*n$1ImmS48lT8P+U)-9f!oFAH=xkuj7*8;^aA z^vYfGdf^*~d8Z6#cfG}FohWN#$to7g8UR78hjVb@l&atewa+$g<579P7&#Q}O!NLi zI7n%HF_p+)kQ`p+I^^us)NNv$$W>9kPxFO%PF)U{=eh_oqp@LWU&PH-U*p-wj@Nl- zeOwZBPrXSj7MlR*PS4uoX%FGB7S2VhfOBhIFeWv%V|U*IW{FOt4}5)SI>b7^FTsy( z$cYyOYL_zk^e7L{q}tfeiCdFxmv#}YD@i^iQsT(irpO^?KcDs0K4wG5#_qT})83UBd{2DRDdtvM8+qO|B0uK%eoz^7W7P@p z+~GQ03rBL%`Fza>1mQ{YnP>M`c{C$gIr>0u13 zy4Zowh0wm&kHzz54HxB|-br2e_FZ+>G$^VSH+k>3RJd=QtgtJKDaErT6z#QVY!mCi zh~i2r-1=BIy)=eCR3vHOPyL&AtlmBvgJt{4W!w|FYCRbt8;2v zvvsg%U~&4rq&nON^fUK$x5Q@tje}l>*AwnNa$=_-Pj#xY9?|lGR{PUaI$9tgZ7G?s z1zbOBk1_8v@(mA~O+Y8D;YwfaUrgy5H9CJmFJ*n(+jg_J#p#!Dn-InEKp-FV)#^XC zpx97psJHt6?Bb zNN-10*VF&Zfc02RJk5lTGyptYPXJ1K(Xu|2XJgmV<3Hy%?h&{VzxD?CdH?BqpUUDk zxG6Rkn&b-aL99bV$MR;`1-OJ;e+GF1^8fkzbIc>`O)L9Y&a>ypFh}O`+*_3jVW)D0 zGBVS-Atg>jQN++-#4iqLEb|W#Q5=*27@EbZK^Rg{6h2O01s=FLyBr^+zA3=j&?sGg zopCh+KSAHW0AJCJBCm-slDdVc_&g7B`|4)%rTZ0HE2t}d$LnS5!}>&P`VDUYjpa4B z&#PCSPV(tvAVeqR3-nqW#r3*BhFpbiS>GDwcmUcBBd1_W5y$$(S>E4QiJjYP<~{);&Ue=jmHNnT`5GaLO+?5><0_|&qF zVxa4|iIX{OeWNtytryVy8|b4VQqxZDB`za-%p*USiIk@+tC85=mP1-ONk=0#Ot5T- zh}qGwKPfHTD@Vp-U6Ux8%e<*;gVQ}GQtRAsg>Bf}(RNav4eDFqbNeP1z=7Lz3h`1s zq(}9p4uaVeHPf}=(_3_@{1oLD`D6<12#LCt@qWd|H?zx&|GymgN_7T24 zYl!>UWwQv#7lg-I)MfZ=zv(Ot%mchSrZBbNVRb<~gGuNpgg5%Cj&bNRxpgJyvxsUf zvuzaX$95F@%>JmRweB;HL1_@}DXt(WHhgC+7(i0pJ;q4z32F?M?&n>-6%Z-qP_+@( z9V}Z=#I*9GSTpY&_0{?!5iIh}uPp^<{m|>+_;nyF^;4x4PU|5fiG($gWG7@Lv+P~? z1wrBBbh4(1T+cKkZhY6^P9x77OE5R51z`V5)YQu864f=tV;Qp*S*>%;$fmxU<%W zjV4~dp%HOrYicKk7l&uoE3l*V<^2p`RRK&hBL(Ie#5c-&;}}8|dbP1C)Rg6{iAz{3 z0bzYVqlNcwdP+7crAy^dRC5@8)y`IU=9m}NZ+bK|t$aCI$BN=%(%rbohi|^8?4>JM zM~3qH?J!+_On)>D_U%*J*-LL9q`sGU7+&i*{9@NLU57V?P9`!xR|(~>epOihX=EUs zyd|A-yNS}#C$10ng~-3~TB`tN?DIEvY~(tMuwpG)@%vMoj%BYKn|`TM@kAk_}nr5=wX3TQo*OCp4Fx^rHr{)8#UAG@_t2S5AFwf#D|XTfL7 z^Xx`ty{0gi4#SpW#`k1Hbe!(2Fyh1NGI9;Qk;%let6P<6Y@Pwx67&wh^GT<sj(gK*YMHg>75b(6Gy!o>*0Nqa&DuIW==jC(G6#Fk51h9e?0*`cNN}Zr-RYIaH5u zxpOCZ=M={|GK>dK!oDp+`yOtykeaXm3(1zE=Ky723&qtO+x5t7H&*Iy4&_=-J#$3E zGdgvHc;T=GCF7*QCFQuq2GQj)spOiGPcSC^JRw+LPty(cX*)jryp*|QXnas~Fc|ca zPq|GhK6(x7Bc9bUo|im;{pynbXge&C z%Lk#4XkLR3u0x~zi-C-#yW~@?Owq#W$ah~ncunZ#}^gsKif|a>G7{x9{i0Sp`+jK)YR;6 z&r1;ZDJu64(TG0PCKSz{Pa3pgUf^HCLm^vqNbfy`Q{!`(5P?}-P(gx6rYEQ|0S5zw z$+hvfbTO@aHNf=&05hm7zs)&0e}e(c zhi*8df7NkNQd6GRJK$lt@!YQ@GcW~)z)z=4{pPoK*5BL;{`t^Oq? zcvrQh<)vShv=z2IVRSi^J5W>cUh3Ex=Xk+H++9hI(9WN>cd&* z0G%w~51JevIC)*Qk$WXxdz|TLGy}Nrw)R;Jb5ilMoUMs3Qvz-YpKoBU z2T!tgxYSn=>^JXN@}CC(S=(3rU`o-(HfB7h_APf2!6 z)j|wEWgu_n4+z4jKN1m43dxBqDtrMA(*$%nn=+z4+WnQ|IWvCk;2$^3$NRw5yTWmj zsMVraoKR%xD^stRT|wPmePoDBAAtQ?o$;?9yDVR zEu3Wf_s8as+k(d$ZjzQ?2a@-6J^G&s!KKj)cYoa~YG5qIn_KfZ8h+A=m7T{!o?VV;QL3p8^tagT37!!2`VKS$S&o~Y44 zwFJ*jj`?mJ<1P>G>RV9tzq*27S4-VZvVLrTt{=^yJVRHBdh~w4W<}B0h2qs){I*<4 zr`O!DXfRyXDO$r@?IOZG_u04{B zLokL4prj}~2>i=_r10{+4X~! zruGuqi^1_Sa{CdPx}!*I3@WZf`&Vc6on8V^93--E7+>f;qS&c`n_4#R2p);2C7F?I zlk`|7Ice^$j~>Hmww3dh$PPX8Kb<4lt0;<&fDIVU3hbQk#va0Y>NLKMM|sC>n$K9X z!o7CeZEWu-du;lHar?XzW<^iswcp@$+Y(n1BW{|y1lqSPOoEjwn9A@=`le0cm4Nj> zwhA4UZ1UWQE_T3*pTRZ04LtIveY=!x&hsRJUH3h8BH7DvdP04JT)UOu!zKaR_10gP zpdI3%DC$_*kTAjm1dbAqQn{?D!|DWT|9L}B18pd}1mdz8Flo?X9T~kx%7bqLLn8ht z;sbgFkPC3St!aw_xk4v1#sNuqQVe-tPERzCftYEI^NHn~3AcLwm)L7XWnA_Hpe>(lO!*V8&n*c;|X}F(-Xp zb_{|A1UQMs;992OSrvp@uRE^ml;vrTjpTpt1}5=%()V zziFcwF^T##iE2+L`w5SW6~EOPHo&r+1mH0+1U1ec(x**>Mh$>f@J{)HKlt?a4c!|0 ziTRwSqyUjn@XkKkREu2JFTPX*pQ~M4_KF1x6bR4;dTK3N4pCkKa|`^N;h(fN7JJ{BOoPe09?@vLr5b24ROy(*5n5rCy96$w5NOxez;WyymO9# zzYqj5ZXgO&7Ye)CX`!2_L8`+SE9K)sH$=QGU8u{+fpnK3y`d2d?|~sI6(8Y_eCW5# zi@aD|Go}W<)zi0+#55T#m>gy_E{>=yV(lgRV(%@>8}Y7LPwaa~&A#Qv6&zp|G-(@Q zO*5KaTMWUuBrynbSbY0KRZw!<6jwuI{+1}q>{D)S&RqkJH9bexFLe;ChG`h7@JFC) zM`?=%W`SwDa>ocPvNeCABUW%Jio1DWWG+TA9Q6|SJ{2_;tjSHN+x#P9DWk~{uNLGT z2k1cXF{jKkIWo6N?-StuGqfdN$m7RhIf?VMeuLcx6vmX)`}DQcvN+KM>7SN3Hs|Oz zPJ<8K-$i#3GdWFXs4mwL@_*f5`Ta=Hh z_MT-)EZ=Y+moPDi8G>;IN;BklAcq-+J`!!K)wnNOZ zr;8pnxoyzhSCtY=9|U-O3k7{9I*JG7F&|SjebT5J30@kdIrU$HE{JahlCS-6r&)8S z5RYj;^dx5#OOb9&!X>`vb~}c1mJTKsuIJ*N3WxVTnVjjKlZPqy59fEk#BR`b`c;Wy zx>kWZf_PC-<10k*BDYB<0(?$=$W;m{LPoYi{WC#QV>gmXgzB+~41lgtP zFHqi=*X68UxEA6x;XT0Mmf)Y>-psMryvjGd!cmS?IhZLmc`}bQhq-_2VlP)h|08}t zW%K#)jp-`IGC#UP>qyV9L#^TXz0Vi9RPPXpQVh`&_e9i;H8+wTa!)JJ@S?n#)x3zVJ59j@kSh?GA`6wU?Z;-?~s8aZOHj z%!#p0U(~LX?iuw2eSDHo+|4TEVkk3IcDQB5Y-tXT-%CR;@4h037nd&)GTwvnsl(dl8mjD(s4;b zA5X051t|o@2zw9T9dB>!y83@-@pHjiAUtN+zJ2?+qkId|okuvT*2@sCx!IY=eDI*R zle~{RUgH>8BD94XyE{%{--{5=REJ76WAP$5REOEu_E4DMnRv(I9a%3jk~O$$8-g;> z^`Hd`u9sL=x@VZ-p^m(0PA1s2^Mda&VRQ{>IcX+a-6LaU996d3)WkJB%NcI`lpo>% z@J&^){En)C$!Vt^jm`>L=RPg7yyV2@_*%cm9l1v$+mYNDk8@zKvgvA)q+D;JM(J?z zEFmMKJjYqRi)8u-bCptp6KM<)elkfY!Vnrft~pO=V*=!B;TtsE+cBJhJ^@pYH1U*< z1lbe_Ut5FQG{=_vy3=&)lP0Tmivb)tT$oi|m�(nS=IEb^yZ@s@e7WdN49~1u+Q- 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

  • 96~3C9~uop)z?#ABVxxv#{bY6y$;9_j?R4u653h47h==&F*=f_)? zZIK#5I^M*m1mkrj#mj=URCUHCuqw#&+;*)YURH&pQ+hW3ofQ_rVUXQ#%z|Zoj z!8Q6^usNv0e_F_(Gk|^j3(C5sASue!K(h83=TqjM&b0Ll{6%Z~qGdd9es@js^j8Nu zTazl8~pTk$3DslgZa7-VCEk@fyt0=xUG`$_dmjG?QeP0MQso1S&j!3;YtB^iIcw3F#3 zT9Y_2Ar+v&xzw9AY2x1UPrQzP-jcgVQR#W;CYY}4Ri0)Y>HODXQfCtx*h|CpNwfap zUe#KrCMlgrt#xlVULjMrDz~9QxJ+@s=q|CP!n3-jo2HSNQ~AQtr$t%*#m}QW4Z-1f>OXgPD*zVJXe9sJp+95x7)zyLQ2O(ld zV+i8adJa`siF(3bWSJzBT1M z({v*gNlzHnzBg5_evoH|HO+lhiAKA3b)R8YmbwMX+g-BNgm#>*oN3Xd*_4cYHNth2 z&eKQ+y4K2Ge%m&2T5fzlM3D)tY(Ghbq&-|4vxf zlEE#&>V)i`#;ualb!7oZP37km-xCshjV&pSxMGzYufu&kTPat)*R~GIVCaWZUfE8~ zWyjPWAAAf|H+CH?r9Pr7HS<(^sZzn{)r|e1t4m@9XbMUq)4!ZO+_q@wx+pw(%>W~H zg548%v4>xt6W2`~iWy>dtM;~`TZ|4fPW9Q^#=E>zZ!BBOCjRW=CbB?xA#e+O3vhJM zxk1INm^B}P-Q~Ej(G%@w-f{)P_l#m0@MfJM$3QH8Ewf<~TEGlsg~{ne`G8|_nrWJQu6Yb_V_;rV2GwxKsh3B>BvK;_*lzdK z4nN3IZLSGU;xUbxU<^ufV$tDPOD@o%+HPJx9Tlx)p6OahxwgFQn9K!Q1Cw0Vnj7MS zmlnGod0UhkXfCOp-dWh@n$j?vVXxg<4*d?&`O{oT$*#`lC-^?ggH6%v+h11JSN5Kr zOXXB%7eDi>Fo3^$w}v8IGuxBlsNY1Xn?)})F&DwU?AkD8p0eEAec?BiZT9e#l?>JA zvEtPs)r-Q*^*YV+0vq?7gFYipFA;Oc?nk$1Xz&(F588IHSxc&rp4-SMGlZXK_cZGt zMaMbjo@Yxx*B!BQPaasc;_>Y*v+3G42uchlt??*krhdSkK8H_g2aRF&$n%V#DiJ^x zAV)?`4TRQs{?`EY5k%Q0H?fl*s z2TOYK7`XAmS1(nwzPtqlaGrfw3_M8J$Tu4u+Lac!+gNfOz8{?qKhqN#xPQ!P_s}iS z$?4K3CS}_vZI=|a>8!+0m#*xIOd3qt~Ve zlUtaZ{zh0wGmPnNRvvfN-Xo#bvb3kx%+07`N_!)jy_0H6SU=P`cyP+Mi&cA-TS-vd zX}kbhn3r<&aXohip3>ARA2+!#r0I@WO!!k(J}-pm>2)i&$6E)Jz0f1dHXd6+J4=D3 zM^9}co}Gn^e8sxH4eb&?>3n`w`KGaR676fur?2}x`m%>DaV{mtN(>vx7pz%{wDcnD zR3ljhBkU|xXSM7$!>j$w9V-UrJJ*Awr@rhF7#_xRCMR@hbnjBgq>LJ)1Kv61h-GM6 z99qO8&ingv3l_x7$}hFfe$w>sMoN7Gbt5(R;9V9#U^GpWP{42-L?OCeZae z^}DlmCP>wiFJN`b2##Ir4L6s2R<{3K;Xhzocy-@7Hi$;+TY#hITn-1TWan zpRlo9d->X)s~TuYA59c(m33}S2L@bYxAaucdG!c=I_Tq{%fNWixx9AVKyK5hB#msC z9&%#j{dLKELDv@uZVlKvC*R{c;u6uG&_Uu#gN27KtExMt_eZ?OD`oDc;tJF$9&=+~ zkGvM7Ij(F6O3hwUi|LTIWCKm_i;)5!cYTIeV_@fg14J6HUav;!-!qndix+)38(&YA zg0FguD*RIR{lh(|R5DJRmB`61X5*dO2i?JXPfqgqIw?_{X{nT>v%eC_cfcoJ+4?3%*@PF9(3nJ zuV}hEH=Xnu0iT*No@HcnK&`fPw|Tbp(}f?o5L=7~DdPT~ueh)EwafX(BWtybj<;Sm z$8tJ4$9}Aj5>BlBrZl|}w`B=-`8={1!#&mw3wF*n&#^kL?fd43zCJGBt^GO-)Ldq~ zPEmP~<@h>==&%fZk`qc|l%5)jHsN_3o_!+j|mvz(G~yilt@vbbB$@31~* zKQE=-d0u6J6eQ(y9ofMqj!29tdqzl|URi9IyV($2T{`f}%Qx-ToWZ=zsc|?B^WGSW zsj@={9rYWO<+7O4F)5|U0keGa5$pc5>hsa_YWTISXY-dwZ=kvxZb{F3bzxB&uV1a6 z0w<=Om@W~CwV1B-L}V8IXvJB(tlIUfqkC2Vob8k89rA*ag`E3oW5tWuqfs(DpKkiqP7R%PXhO7pTI zDgRoZxeF`$BRlt%FmNcB*t^k<4h2lT0y}QMInc!efkc&VZ_~S%D&72!4QFMqOr0?^ zFrF$zFkg1OtQ`@0`1Gi7^CeATaAwsO=c!u>eJQRMYbjC8s)nu^XO??7MBs5}9k2A} z$@#-&veY=yb1#uKg(6-Q9XFKkUc|tAqZ$t1b|I1$Ys8UG|7SRi``$v%dRDz6 z28VvHX@)ClPiaHEIWL=z_JEwvS_NBwI!3!4vy5DsXq%L)Em(yaXN)uz3oFRq@BlG& z`W<)geXLIJw>yGU-BY}|StOlXRByht*j^^AUC|BdYYji0$?Gyum}F@i3Z{Btt6C#L zM|A1Pn`k)ojIeF(gF{zai|(nr#q#7*w2j#^Q>70YN-C)swCBZ__bo^13#lX87Smj- zMVXFrQBR}f`q;y7oiMrInG=LmWd-=YMjYu^D0h0TYtzzoe}>RM$cpeUT|%Ln@-jhwRYxf8Rkd_%z-B?FS9iF4 zK009Q<8J8q^uj%*sj*mw^j$~3vt1ZVTfCkeNf|OWb7*-laV<26#3TNp^BVYbXU2#H z%;Pwgs6zXAzxA$A(>0-2{!@N@nl~p-7_+|SzFJZ_GOLA`seZ!*yn$XXVzVKSy@EXq zJ@DQsiKUQ1Ud;n1n+DdUKV#b$`V=#bkN&VmqwTA9rQ3SG`P& z_Yu=>NIt!q?0X$oCdZ#u5DAa9X-d5mJl7Y%&oKrquU>%`_MKdthuqA{RG3GH)MJDe zVdwc zT4}4}tNxge%q)@zA1X-s+Hq_y#s@y*H;7(o+ix>)^BchPHGY-7#-epNajH9Y9%eFD zr@T<@ZI#^;$u%L_7(F>2$DPX?K^BaDZ4$uh5_Bu>pF{Z3NXEvK}bxDotF77_*C ze(M2Rd*SKj+a(V407aZ&`07+ImGVbMNm9)plagWe6pijGrE-vSiop-;E8B}deG+)% ztI#>>X%`psQK=4!Y4#XpdF7ahpZ6B1GsSPKckoT^e&5*Pp|94p0u~Eru5*D`v#~|t z*mCgQ=X4Vl=T472X$;QnbR^?d2@m};zVInoP=t`1ZeS9Ay80q26zM)Qcv-s|MY%0C z&Og3Zp?M^IxR>B=ThvLGX-hh{JGVOP)D_oO(~oNrLBia(Gmnl~_0iYnTv5d!|Lw%7 zmZ{~*1G;QMS)`W0ER*O!cUoJNmE6A(3SVc230YrtT7E0@zB>H+?$o2!(dnzymK~9A zZzxW*T0Kx)*&?G0zD!)W>h+wHx~i=MMl<-H`^=NH?4T0U4BMF_4s4~;MwaDo9;DpCG zImW$vIhM@gc7O$J*tng05W9NXx3hR<@qE_V7CB z%BZJ;#3@PB{v_2@cQ=^#$Std5DOg@N8+)h(2rz>7j0o@-5{st)n%dU4w{DSDDnqO5 z=a<-av{%qc1~yMJ_0>)`9y7F=-K*$iFU3Jn%>H)vXX)#dOsvrCr+uvc(b%X*rp=N^ z#OG4|*P2H#`Gf0#v5LwfRF9NE>EUp1j4f4d|&#%UT~^`k?J#pT757b{d$d-5KL z;7hW%t2G9P&yP$ugd;nt%Uo;j(VRusYN&s zAs>uEz7{I=m}u!NderHz_n2mAc(hF!J{7n4^&CzLt=1^LYgsFw08X6fJh-Ydy^a7J zCEpIvt%UyWRr0{r+K7FywT?-E!Aaa3PV-M}qz|8XC@JlV*?%#KOn+30@bEZy)4ss%ztMu%24A_T>v- z^uqWShd^3RvZY>&PxHb6>HHcL{opcb{B;HYeo;(|Vsp2mK{N3+L+eNfbf>T8D0*R? ztcoRPRdftE&m}DbYM#+Ae&KzYUXn8%Xc$u*;ky?WNyDS#wUE?#`E0*G>v)2{;`0@6 z@)IjMuTcBT{Wk(qCLN8$229%_SF;H*T{BnGQ|uf5H4i4@0rqkrXsp4Sqs>D^9AjXjoH{!&{3Rk&=( z`Y^BXXuxYN!-o>9LPsR0)s@6{PSb2`ajp3h<>GBj2B$@4{^@n6U%}4q9g8rtaR<4^ zkBN=%XQf@d2-Zx9B(hgSR+_x<%belbC`VKhKAvW;obkDmFOzb0Dsi6z`6)NQ_p7`% zX%Mr_&hvLt?yWQ4A~DE(Y$7C(1LLo|XYxXkF4QlxnYZ;HrSuZD#6KN&^()%^auHQo zRb-v++T^Zqd%OVhzFVNF3NfC6Ds``hiHZKJtm31RXvg$3-{DzOb8~B#!_k%25`Kiw z7bU%={P~}^qOK6nW&1F))6!??|3+vr6xwGBP#e2->{mlxT6+$yK`hx0J^F0ox4=rK zcUhQIyk_^dWeVuEd@Quj5};GhcOf)|H5NDZII0C7h?VuE0qtL0W!Anb!m@lS*>=aLAC2jeSd#lVS_C?)%>Jl7`H&3EijH!edKK4X*?BWJv6tO$w9oJ(8xQ zM#uEZ>S#py1y%=7`Hdf(P%*!ekn(C-61W9w9BE-58Cl(FI;fnV@4Q=IU*4=AfE=$c zghVJLD}>!IqEDNJ!WQi#OqqWgGqweBb2k&ER2k1$utzsIP~w}#$}(+VS1PZ;Vz!7s zPw2}sINWV?NO?5IQZC-oLziUCa^1$omQUZC5{RRB-8a;$lW0upr*cF(2sDg-&IY!F ztwC!b68wTILhuXV!&i2Nj(kTi7Vh{B*!n*18O-rDvmBD)r3Ctc=&*H+Bgx#Hxj>(* zXkE-%#yZ|M?$}DM6J9#A#RFgMt0Mzl9f<8ayqiNi(-!_B&#Ifn%yAz`R%sDi%o(a4 z+|#4)T=E6Gz$($sIVA-nLYuF)D+lFATpv^QWvSI1ny0ybTs^PjnqpLovPyG@`<%;LG;;v23DQ$(6(oxx1J93>r@Yo?&pmSZ&O`C?DUA|P( zMJZ}t_!Rn;S&_$-&@IZKxW({NUq+&RAZVrkejgESOr^+Lre8n)rg7<3gV$q;;;B#V zPAT^=SR9h~9hnbjI=e26ID|EHuEbg+X&8_sv<7!rIc+W!$WmxA2>A^Nf(Ow;LRu*( z>YgnvojFWC^324I#mGkcsvtiY<-ZXrh4KJzmUTb(3(G=imDnh1XyO}P>9>M|K-dO) z*_5Go)V`3lx7#(+ly<+>Yz0!D@?{u;(1LY;Q(XJyYPVd++>ykLS1d}f5J`;lHaSq> zq+`Wlu0^pKq?N}0Ldjt9i;((Jif@onmepZL%sLj^Hkt0E?R9;jwX%=y#r??$Y>kxG zB!5P4Y%H4^LPZ9&5EO%)&hl1fp4(yLM;js}p?kj6zNXg()_%!kc4JM26`ESF3~94V zZquh|R%*BNB0m0vKMygys;QJn8?V23 z?{cbS)Ey4c8A*MO#j9)V6*;$EkKlR}%-1{T-a9fq-B=PYz4%;RhYa>roQ-2etC(Ur z+Cegpvhn(_-rDuWQ?p8(b&s+uSlAO>qbj8^x0R`+4~Q-*iEc}qE_~N|FZv%3>VN!* z%}`~PdmwN%1HoeSG}b;UPK8x5Ti_RcQ5|19w#~arDsCH*o{Q6vW8cYH)v1Z}7!W&~ z&kQkhdwOyax(QT%crsN!Mx+Ds>RSe{T9SoQE~HjN66&()HyLT7KR_;m^EX514|fL{ zieuF;<>v&cBoM772tS$+IGaG&Cf-F%J!pi=kqWXEaV@-y_evQ1@{WnY9bNxQ5xaAY zjOV=dTq4KU-qZV7vs6w%_Pq@4^D4;~YqjaZxg`bBjxC@q8ec}tNW2s}iHc-2?Mr9R zeM^D#)*0=mJVzVu`(Yc#my^r;*+OjmQ|Gpoy*cN#a^A$@+3?2{kVlUxV|^ZZN{A3w zJ!kPqy7$VB*GJC@OKN%T^4$z+P}$ClEHX!_d4Av+yC>rdwzaIv)LuY1MbXH!+x4z{ zC8xi*8`bV3Sy#gQ5D+n)X=mJ)LpT!^whxmRIf;j6-^`h{uDXXYhyhDhB+kTF`>-mN zf4%(MVZprW)6IqMSL1b*FSJez;xZP+^$qSr86%5X^*q{Fg#0#~zY5iy5M5@GWpO1m zyx&lQ2(OS=WXF-mUI?v{MNV=l;a9Eu=!4S->foZ1{=uBDUc3e&-B#?ULg<&JET)(y z&1F8i^z;-`@TS>G7W~lp>Rbu>X2tZ8qqI}P#_;s>$|oRW_nK828;nGe%4G*H65m85 zk-UZ8(vKFawB&31G@izTykZ;rHp46@@7M=n0e^8A$CetXGT@eVT<*)sr3YJGi;>vB;m^>{5E_?k7NX#mklPa3MnkDfSX z|60FH+OA0mpUqf4J}bGkY_qWW)f#AC22yFSfmB*PE5tmP-*4B2YF4mxaDlD*kRXje zW^ItZpPJ0XGflp%Vu?RS4TePNp_>LLYhh36HsUkM0&m)zv0e?FtoD zwEN63_NGcX>CIu8nNIYEJZMSDPTlh{I0u4b+?MN^UKVWpC+_44u$tDa0ov#zsgBCmXrOF?b;|FX_2x(#0rNrGUvkx2K-baZ@LblGdCO)UW~QS=>D8g2j%2olQ*?92WHJdxE``88rqt&Y5$LtYaBd~e+JV@hwZeoe;f1mA?Lbu z-SA${&Ahk&ZLf9!@9qp{=YX(j{_A~$fWB zqVE^kw~Gq6fYq3pnUVgVF7Wpp0x|;j&HTeugwtCHln96*L?jT>_xXoFf))`O>DE6w zhk}fF`wlA7t#9)W0dqk{`R7*vPkx_&hyaW~M7W1xM*QQBn_q%ZaPH$0P_T=r8#*S8 zQc^)gRn(lG1;oV<149Gx@E;HoQPa@U(K9eYVH})X+&sKu;u4Zl(lWBD8k$<#I=V*2 zCZ=ZQ7M51dF0O9w9-dy$1A~G?Lc_unlafBXhxmDRQNjm@pk2Zu+;C#PS|&Vl)d-^VFww2AVGgR&K%z0+z>rfCU1Kf5B?hg(^*c78n;IS&sMQ~ zSin@n^zpHSSw+S>4$i3Lq6&OhI8-%KKVSSwKs0t4B_W1?kSOCvBISO<=NK}`$-
    Gxz3zYoc+y@eDrOrK0Eyjas!OhBp-#j0Q6{C+g+o#szR# zs0)kb#?po#6NldwMKtmclreca5)cq9J`@VU#S23(l@_)J=jbVdhj(|Y2gYk9{*qN~ zOdu<5$4f-V?D*_r=4QpnbG+Hvlsj|?r!(W6W*5C%5=NnmU6PJo=y>ZPT>cGx17RU% zM6QMg}!$T+Z8AK}m$+^t<+3YpS0$ z+5?C~KSuDre(GS=I+d5k0m-CI$Qv;AN?X8C7a!U%U#k)krNG{xN3TJbMbDMN6CaA1 zG7^>mi7TK7R9FaWNo!zZD;uEF)8$do=e}HqvAC3}4!?4y?-|=hbzFaIc0JaYNthd8 z9l5u3p%YQ?AoiT=zMsxOxJVj(x12SPMv_XOMiCEHVth~|h}cQ3XTOXmE;J>ByIDk( zirQ)@#5Q&VyK?|^B3ud2NtH#Sj}N^nyf3V~5r22__HFocM|Camh%z=dHYV)^2Z0A) z#b)WCUJ}t=jI@x-k0nPpl(Y!iQO|5nip-aA!F7#9TdH zoWfpGzZNB~RZ&dR{AboveaMGkx;B|^1TlMK~Fdfnq0hsChmBQABt zB#4Gl%G1^SH11sn%D+pWiAg|ESaUd3~aQsrvpKnn99!^n9 ziMVKz(SBLXMsVU0Uu`^rfY~7p<&Zv;De3E*1My_)Or$OZw}NXsMcsp=S30hN|9q>p zOw)$2wAK^{I{>O0T@o7V`1;(U`;N_GU#S=ii6yUE0ORN;3XLw#Fl|sA10xj;Xgn@% zobX;nkNk%h>BPE<$torNu!=DmhON->+B-%KTw+u%jBha_D3rTgaBoXk;URv0F2e#^ z0+Hs0(u*^4isgb}jOg&Um;wIqVBrEm1Hn)_;?4oJwgI<0pGMqQam5$AnuaR{;;Xe( zM#CGoEn^y^9mK`e@Ko>|#xx(_wicxgrL<5-L&~c(RDP`&m@4Ja3kbw&*Q8v!MxP1WSEtK_8r zzDpf5p=XYN3hSY>79 z@CsCV^KRd$Ar*C88T5dN917)JX*@yk|NNZ4*ayjnDDt6IA|#$rRVv9?DFyB3HNRC zZSqAe3JQweGL~hE^RS4izPjd_DSP8jLiwV}V0=loJ;uAvhI~w0-6Gu%>=lnGi8zr^_cqy zsxp4;Tg-Q$krE$hJ6?#*6E@XkS@F@}bVZ*zld4QT=tl7Sdn5@^=xV3C{4R7J7JKsc z%THo;mUH<&N;w((eOsM`S4TPFZv2*7M2*Y^rG#cX9h6gTLN%OW>r_T#8Y3J;qgqgh zI?YFT|7bcYqQVDvEz|a4e0ePd`%V-Qhec6^y0z|@m2RS$6_mFY3a#LIPZG@{3ag4> zk$ip9P|Mq)_}Y+Kt9#Uy45QB`BD|f5F>?4|QI>a9O*7bsG2{i=J)9UJO47SVwlkHL zMsVPn53Z7_0y2#4w)s({O(b5OlOa7ba$U@+XG;c1&s>-QQd z(`CmaEcpEKMRz(OSz4V~d9jIt4q=?yE3~{ih4ferS>`*|VkB1TL(XLJI|>J-6uVuK zVM-Wu4RmluUir{bYfVk#2n0UqjnkdWo&JM<_@jq5@sBt|c_Hdj`jP@4Xhh^It||PiX)a_ZdwiL=Vknw()sb@2$};#fiDOWA83jHKu0x)8nGA z7kkau7kgFX=ibjTv@mDLN(46K&iY)%d`jjyV;_7kmt8-VFgkpM(`W+G(Fl}MlDXA9 z>1m!WH`Z8S!Zf*w=_-o$%+d6o~3Y=hGpO zM=+YS;V@hyI0sQ^851pnOot0vP3E%0=>|_!#&$#KE)jJH>b!{!M}e-onBG)e8u2J+ zMEYBd=tP~x_$QcM(LBtnWq~oyveQK`1i7e+_S#(3c*->M%hG5@CUu%Wgz8kdEExEnDP!<4s+YvubWpNhALHRYSUd$#!rqoN!w$|O|M z1^HQ|^ouGmE;KOI3&!$fWu2jv05MSfDFG4cOjie!k{KGoW)?IxBh`uicwAGSTCM%^p2rO7CNy>?@$TmeuN{TxtX(u4?G0>Va3#xy-kK+JkY@jk@ zOiwhaDT8H^m27u8N}X0&hcTQ7u@0ZtIqtSwx~bvv7fww*m#8u7SCAfw=VgHL_P|b` zM=CR4k4NTuhQwu3AFmarTbJx4rZX%i8sz{PS=G;1G35fQ4qwJW_a#H+IQynmq(_n% z?&0L+<0u-b$UAE&J-qe2-RMAC9fz2KB0^5P_KLZ6EkTATy;`tO}W3rJGXl{4P;YPTc@%Z#clY$lPOm+Dt3uNa+-f4 zF@n7vfdCyf$Hwr`A}?GMDv4T-H+?cBM!M1vSka0JuZxAT0aB9&)B-1j_bI2nscBgB5 z7p*8E`GAqdDK&0-M1CE?G1(TsJ5RS3Dm#>?s;*IB;v5$}@PXX%Ac&`pS*;Ae)2c2$ z<}K+H(&#u`n*i)^4%>&u!Fh>uU?k^%3@}{c-qftv>)_@+BRd4-WzYB;om0&=BC%QB z8l&Na+gu!+`8e`nGV+tM>dphzJ@EU9oE(Fg)5{1-2;SLx^A!8GFZ73OW*Y0%XhV5d zsn^&y5xhy~vdCs~r^eHP9BQ5{VJ(&Jn7N*~TI%Z|TM9f{(j=U)I1+_Twl}35Y1$*CR*=}@#oHW159^A zS>$KQ9|$nqmQ>ZQ6H}usf6-0rjQJ8>0f}&??R9YUo-l?ip<%CEVtF32mhH(fo;OQe zI<9sUt^??kjvtK^o^ayc2O}Rhl~*n*SNPhTWi|bL?J(2qmpZ( zL!_Pr>N7EM{p+pgEpL_P{KsgOxt>6J-_U8SAi(TMQ9*< zP3An|Jd?ubZRxIcDyk|flHKXxu5C{7kQ4&Ve$=8u)M;o|Ur%J-M+9apHfEB!X@REl zG|sqY|a91F5u?RaUVIy%v>I&#?_a_9Fkd@i!u z85=U!mSr3(FOQ6j^e3zlRpCqkO?sr$aY9GznR~vNl$NwxX%w1?RYww6dp+r2oiiCu zFrmrkC@N1(!d)>9U>}b&$njDiea+FG(mpUX7L}iyX_ZDeC;dTwor$Uj!C6M{dW4Xc z(!NhId^jOrRzX5nRz(HJgGzn5;(1L?5y=P(9p%F{8a~avD4f2kGRl1L%N4N-Ny!9Q zNn~WSF%=vFsYGzH!d&_Kq9C#SMIM1#Ev=k~MPIa)a-0OE_^K9rdbl@*ud9oz(~}f) zPU$D@w;?*UG8D*~;^N}GXTQP^@hsXIuivQ>x0709|-`x5jEnR}vwWeB5NqSn-8yPC?p<@yv% zq|JHQpY%mB>%>j;Nt&6N>zg@8r6TrkkPSY0^uc~MN3RX??1(a%fvU>MPGVH4kKiF- zhva{cFOB2NMIdM%-Hl~rPQgd0SR(+pARygRF?sXyw*AAmNO|3kPtfNLo$h`{0CW7j z)!FK2o_g!KJt_bigbcPMeU7YX)arj0uBOa^7o3-Dx1A$dj=dlarZX zpp}$*PO(DGm;;8_<&Zi$#o^RniKlp|bt`#uh`f{}EyNDtNr2rWBBIz+dnFs6KviV7 zW+r7)Kdzxehsl@^I;CI4k}2J+QFiTODmAx|(4-6!&5hk)3{^+Q#l?MKgoFe~VV5*{%8x^0OG`$z7~yX^JiKPbIep?TZa|Hk)SA0#1AVu-l)0v*`WA#? z#8sm{#DpFJ!GBDLl^O9-AFD_TYAh~=h>_xf6c>)1xmjS{+}eFYO$Iu5CX8Y#K=(Y@ zb*I_IB+L8b#{x^``?@6)ATLJD)Z+SFHAJ0ROBlJ!dZpVkVNg#ivsZg%yi}awyyH3B zIQdmnaNemdRbj-5&bJhdB$_t!hDtIJo?T8qi{AMr1^iBVIgdu04l22Vp+c zD4z1_2>Wh7lEeZJ6yaDUY~LhOpMf+!Z1lT`i{)rxIBeq+wlJCXbO_}bLRWGiwrqzneh^GhWF+2y%nDhM~91QDH zmQmhtK~_Kw76X2kQ>I)(52(&xP9fDd0$7{85R! zi;Ake3|mBM!~FwuXfAda(A=+rf(};+;BQV}IZ-PlL7 z$a|;r_~RyHgR-zxNdqZ7lu}xqu{<$ZnIh3D8O-5&nr@s2Wb+>3XDixUFEx#6BXv0 zF+p|e)z_pSr)8d=(MW*OS*vs7Tr@EJfxu`O9&AccD1nH09qfp}!|@bO%drW|V-=^&J;46Q|{i{o<#T zwd}7xtU?1N&1^9U22lcN%iJi~r2wj`05EFqO%nrD|B^qg__!SY}{LxcR- z(i{u=TNc(N!yYHWSleivUS^_m^+i@ScFXEsro1 z{X_G|tcJpJlf@~K*^h#A>TFHOgKwo89gS6wsA6VVhxUHEiS_hu&ct5QqsZBbz zRj-q07Bni~nU^L3(varIO9JJ3K7Rp8bkJ;9KTga%Fez}S7MoYmHcVVO?Uo!=uG0hPR=PL5T#v_x_l>YaRcN+*6&VlB!3oSr4L}KSr(QYtdUb?c#>G z(KCd?<1<7lD_Sv`&soUQL51D%+R0UqCz?1^2xK`4QeLNYB$kaza6CSd;-5%rLq+W- z$xj5N$1Mg2OT*zM9Bs&V;0R*W=@3b2bzDY>cr138C85e~M5Km*fC({GOrN{aJQC>~ zX8V#f5T33Sbt}b6CWekBZu!grfMeZP_Zy)igHk#q+X|jIR*_v@&W08-AZi7W1YSuz zp`L%qh(8=2;Qt&fBEpUwErnXcEv~WA0J@DDtRbQ)#YF?N`tcEgn34J;`2FHDoZ;?@Wjvh8zhPI7JSl{>*oJF~`@!1Ba zwSsEAsgb&*nfUi{5z!2wag2BzY zt=_Ae&tmr)B1JS2s@hecM!z$aNl{F-dsx=bS-=y4F)dvX9d}3y5>Xe;RW5<5x5=%r}@{oDx!5#AOi#T{Kn>Bo6{OnnqB+%g+_{M|NFs!?~Ph~}(X zweV4c*N|HAq(U2*hk^m&gFj+dpgxdHB-)Ima*2{GnYxX58#OEkoEuBA!l#3RKJ-ja z7y*H$_vL>f5Fk4E9||@&|H=J}*}K}gfH{EVJCN4|vguskv+44N-#>7F|Df*aV9KWC z>SAMQXX-2{c+b?%#NNp2-p#$=-hD3y__hoS1gH^kcA=NzV1=+h&YPUQqNxV}5_$s?C@V1ih6&^wa;j|VmM%7?^z5u)HBV<3Q(G{wuCbG)gNvoT z9mDT9d_(b195{dAzzyI4E=HP-s8Q5-!-x2;-DA0!xCg+U+wVkq>6svDs~ZzOy}FXV>6UufM3^#^xvq<@1Jj1?^6;9z5E{GF@+ z5v$)0K>o#HD1-~B2l>fijvpX?sL>xHgaI5@votre_W-Lq8QM7;n_9Y=I{m@i-_8m6 z1pxApn+FD{&rd3Tta(EP?04qESh>Lx9^Xq}0PdQZ0;+6m3YItYw0Cv+1I%xwQa6^z z@>5;8zMBjOCm_rpwg~-JqVv;apf}|GFqs?u{E62Ollj5j8*{qhE)=jI2UBB9vmg9* z{sWocN`P)?zQKVL!t;}Xh4U7K;`VaQsnD0+AIl&lP5Ev@iKNUrcP#tfK2`yc6?*pANIxhT_jvw96#CdgZ`fg{lWJip7RDF z2;eg;?Z9I8c6O%5|LQKFzxk|Rdj|PBlo|f>EArlHOc5;1Nk3NP_UY^%s&kLU4dZVWbg;aZUoB74!DRPn))wRd80r-K>hAz zp{yLgS&ovkv84@A8fgEAk-~nPr2E5qVE+(umJ?<1elM_f5YbQu>O@105%*DzStc+x@>=qf3nU&vt&QbC1 z{rNq-y+4odate|-LjIN4LOxPB=*ds@Ixk{&oR z;rnGa49o+Z$w0Cwa5xzm?%VrH2BtSU);a|5%^22TiZH1*dw)Wm zd4EaEpHKfO0}ia^etClwYoab@NTma$WNBn%3zD@lJh+N~NE~1^ZUi?R@{4ETe~~x` zM%yn&kwQS&)XWy7re|T~XbVy@y5)$}^bV}gA7TeJ9v2tlmsSNw&)_F15A2aWl^QJ& zkm5i_kwZ*ENeMYGN8m*K>stLGe$cGR&HHqigVi!nl|X+_6rsy-viperHwP_ zx}A-&8IT9Re`bh%@&tV#A=e5R>i1ghq5u1#_7f7hQZmRx_PQSOI6q($qioH;ozgE^ ziGuQ51v)ULJu4Z_fym|hY5mIT*;)L>CPi8G2Zlmg{%`~W`b!w?+4{exv|m&}08zB> z47g@#V_*S@Ks`VtTOFX>K8f)cvk5sK1jf71Y(k)S7utkC96WpXlytPz+H*;uCkg>3 zDi0WiPG&~V2ZsJbeC*8-@rxaK&@A_JhIv0KcO9Ud2QZecft%5wgzao>|Kq(tE|;0!fBbZh6JdMj0dTUQ z*8=btiUETl-G)$IE)Z;Q`oIMQ1lyx7Z~+A_(Bc)i;0AH;*{Q$<4EO{+72pC6;@*>A zzy$)hKy62$o(Pb^21-T_TAbHK#5k1!U!xB2M@1V4K+OYjaVp!HS^Y=kD3|ATU@?EY z8j<56S5XUfoPtm)0|+H*KqwglLNO-@#Wy-XeWb{H_m*A=n6471ilQX)i^bHz!Iqzs z)7jY>Y;R=cU}RtlHn6ec1Qw6;niA?xB~BEGYc_UzTi zf%(9EfZm1xBc&~pUMT4d2F@82I5+@?3ow5Wfa1i*%L9VLdBA|X2XOKtulWuEZlCo1 zhv0ybzz{Adf)}i;r*CP5+CS)D!Q2S&o{NTKqi2=d3nGP z9uRQQfDwlx={^7Xq^-MOXu9f%7a{Q5R?l7 z=Hr0_`+^tD!^;Z-SO9>0nC0Im<&S`sn~R&98!U#jBlbv){v8a1fl*>gA5c=jqADs% zVB|@sCL{+|u?MCF2F?I+2{-U{A$u@bTuw;|3|2GS;~KyY00SyA1fV(}Kz$e#kj`9u zAP5hz3Vet|FwMS`;|(9P z-qc-C?2uAVU$MDP&1&}4K$`s9&mrbrZL^W`W(wb>ACzjzwQ!8tu+9`!c`mOErAb9+ zD)ujUzS6ApfDh+6I1V}QE^cNr7iYO~d%EtNjSs0zKL2*U!hNgk(vRvB@0RpJv_>5| zLbb>PuDINGykh>2Om;@Di7;NxiM-lw!9)r06!$(a` z^L?}yU&!2ZHNWKf-WvFjZlwF&qloPsXORc%L-{_zgwnWqMZ{3kr22X&k1d_H$p&PI zmPsM=U2;fa{RERjzMJl=hpcgoH}xXbo=RRHsVY4wv~J0v`RMFO)#O`_);Pkj%Q3na zq_|*qGvRpvs?6tefjjjCQt3h z%dIvgSL}gw#a80k++jCS4e{7-BC@kfZzJVvUX9DAb7KxV$UWnBD!FU&olbv1hZ*bp zHx*8e++$N745R*z13MeVTY_g3C#5Z(RS}jA7>&eUdKPY~S@&46Zu{FM6J6n;dx6;S z)u0%rC=TO!a;-O~<-RVaMGP^wbSkAcy)h-mS(oy>RhOLnj9p(Lv`~JAthkT3Ai!+l zTMo~Y$3&_!i8y&Pe#&YOZnXu5Xu1c+_Tf$>j7H$Bq~E+zQ$d|H`O4$6L=kD>8O9P zs6{1u!~r*ts^Yt|)|O{%RZnxrL?e?071N`fN68nPCr>lk*W5}=#?rPU6A2Gyhs^t5 zoC~|mTzx+9;@kSI&3mJhVHb`1uYIt`mbxiIig#DV(Tr2g9eg~0<;95Atkgx}cNW>^ zf(|cg<6S${So-0X#VbWG6R%~A1Wv}Tq+Ck2sP0!wVck-BB1yXFn6d8g$V+YRIm3>s z%Qv{=NyVJ8vpL-~4({m{PUU^|{A8um8mbCDeHSaPr+pEBXk6jR&&8(xGT+olvZ^MO z$Lbry>^Yr!=HoFkF{+t1PA4AvP-x5r+#rxGH$6v)*dkYW*$6$u(YbL!HeB@dnT&RQ z@BXyUK_=r7VP`|@y;iD5u%nOtW08E;ae?pw!v^UU|4 zSaXYvSKOquaSE2a{c0mYZ0+O@X-REeStSZB-tV7M!|Y1#8qFsyNn5pEuX)^9>Eb0W zEQc$byQ1z>2v?0oWMjcqZ{+&9Iay`ZZ+aSVAilqn%bqsBiOVa9vAWV!yEcMBJjN|{ zru9~VsU%DK`2js9>9Olq6bz+zN};{%sl?f(i}Bzpk=r|iu7r+XhFq(Da81GKDE+^I zl8@2GE8`HeFDnP95-79hE}62mH0`_YF0v*Q>)Jjaal)cg5QzCh~{fu&C9HKNwxHrjGsY_$&KcQ_(a5I(9; zxHqn(yq2`mm*l>*e*8>U7~*NqmCIpF$II=tzH+d4YTqkTBo&-CPR5#AqttreNo%ea zUHQV{-q#|5Dvm=%_g$9 zcTJshLl#9hV){bAakf)7HenO~^0F^`A5n42CEo`tP+Tv610 z4-l_IuxNnNS-q_L3*rQe+U%f|z;M1$l8-NIOulHON6WHb`KXY@()&bpsuT3goY}3h zO}-C=Gl@9Z47)86de#}tO6jHdANtM-<10yVm7epKeFIH3!C>7GXT5|Q2_tEj%$O-B zmh_KQ3u9Y|EXAy*6E?eI2ocCIqARWM(%gKkUVEOm!eq+!{ES*{hi0)A(YB|~Nd-cM z)k2fWs$e>6J7@xa|J|C(7Vj*@sL~0BwxJKs_A7GER+S$@#TK}f zp8E>Ao=e9ReAVd}{Oz@fDqa|izj9z_;=4!na+CG4OO}I<1+gB(9@ZTWx8-R}$kW-l zHe^Rh`l22=wW@~PofhkK+kx$tY=IrQ05an=SAIjYMNX-H;&$04QHs2sH2MC zZ6lE3UYa?Xx?b5tEyvVof8Zlm{nYfS>fH3oBxY=X!y$&UhoRS#pA_@I@e(Iwm`tMQ zSuDtG8H`F=Wel-dVc8C2Vev5#^y;I=yg?DYHV0>o=n! zABhp)kx%GcG5g^>-{tC_8Td-&vr854Fzh8bFi~hTB_iCOdS)S|P>+l5e70378_o$l zqA#&TAU;UmmoEi5inkrEO2JPEX3kv~FBWcQw{3nMd3(d9@4JgTiBXo2Ip>GP9-HbZ zJ~@dWFV_r%cIBHwqJzu|S$Xc|@)}cqH2nOSw(C}^V0)`je_CZB>#1b&{yMQ9tW@fu zCpq1@40W52DOb>4#yP zLDtP=9?kmMf`>(Ur0Q6u);IzG>DrO&5(&j0WjY#P(Pd+%DfSwEy$2H^`>v`hoPH}Q z2pf7=j()QuHNBkmw#SMZYr5?HM3EiZDzZ@}tb7Ph?DN)*r#CsVUM4VKu6*~tcZcOp z)>~UF%=Owk%H^v~#NZoZrj!ab4p|E4cOap6t!&pVd3*c(2WvHE7nrO@t9lli_-Dg! zm4*lDTzrznZ|D`#68x|di>tJi?!<7&vC_PEV|5-jT%X9i!f*RzObqe+O@bzxUkLd$ z^@yfmQD=$>+A8P^O#1lH^Asn3rx&BkH3%OV$d5X4jji~5RH(OXJFatTWdPSXiB>@t zH_;WSDTz6Wy5$@d?aXXbMdkd2kP)`(9kE@ydv6Uq5(qOC@4Re1wV1`5pJmSOjWzkE z`%8!YEO~{SE62Tg=9;b9sc~?E=Vr^72MfDDHU@I<{FqvA>)72%NZ6Sr-}h~q$UUX*eU1qmMVn89(%I66d-#6QD5~x=6NIW5I$}v--k1*tFf>CuZ zbOoi9-PcpK=5JU`eJFF4;o-zgY#XhfSG}X+LU2lZv-aaDtb|jxSkF9d&b@kK7mzY; zOFXOt33`yO?xHava-4*ikoC(iWYDX3!8f(+oC`Ia5$Hq^Zo&(^^#iT4gKC_0+f}?tJhD}#nwpV$KSykWER^Wtlx&w1lf_ydRAYagIvmYFCy)Mdv9#JBP?mX*|g8WQm`uesNznPN|wS3PcmX! z(}zU8WqNw|+c{qjPNHuG^_ZR3;g_*Nlet9l6caQQcjJv(nsa{2ZR8}ir8|1_L zm@=t_?CI*W9Z+?Rt2(O}2LvitF(=L~E0pea{2mv_au6(4tV%fYx%-bAilCh$_M9=v zd>i7hI}Ixq4Gur-8;;|1&DY4tjE)^2eL$%7!i|DGefeHYdI`zq4eYbU-+~MjSr#Tc zm1&6{rU|S(GVP|koUr)W(aOyje-u*A@fqi-YWLvOrCmykA#7J#wKkUwRd%e9E`X^o}xsby@!usHhM_mYIn@^*Ml^~544-xH5> z14gc#VYG=7}LT2?UaNxR~ul*r=KJJ~j$~mWnYAl1*GR5y$sL?M2847LH>s>M_`5QfCKz!xH@{ zAX21#pT%`<(0#X_ST>Pc&ykKf*%kXyz~^f;JHhv#yZYWA5f5y>tBiTiGwN>E$@`xd zy+D|DKKGM%;_qJ(nD4h5cEnkxn1U0~v)fjz*Z0ga$$`KsLYX8kdM5K{9>`uK44WmC zgE->GGDMl2e=T=LI3v>J(RG-)e4(uC+qdaG&GZ=)fmUyzVi`?E4~>0qjLc*(7ih(6 zBscg9n!c{?9BI^vtsoo0ZaP+UmT^MZv5+8e`hmgw%8qZ!12#?N&-^c8J81SPr}Q)h z*giBF@fEJMbX>bHmk=_bHrxEZlZ>`hqQj%mv`{@bHw8A?&}Po2;V3WIr_tEsYS%Qv zHFmc?Y1ZwdTMqjp?z>yAM#YLIWBNI^4l8{`8862|E9v)NBiiV;14Ax8=2k+WaHBtSE!Qsjiarc#?pHIR>-P|;n znQqIBboH08PECMt5p*^zuGl+z>TWWv3G za45J$pL3-@?3QxY@naV-y@}M9cPN5glEQ_^##D=kRoSp*CCdo!<=c8+z%Al%Fc>>o zZO8iggvc{-HTKhRe`NzB@{h;Q+-@L^b+3Gw6uOk(>=0e?q~=w2nF7w_0*i#RtN7FQDI1@m7))#q z0bcz1ba%~1WOD645#wU&s!C(_c;wG{SKYT%OtDBfHtLw6lbIgNk}m-J9!>y5Q!0^f zK*)v_(>e7!ar8sW#oUIt59~xdZ(bU~mTgS=PJ2wYSDSUYF@wYdTTKV@?GXkAl~wBl z0h-A2eufJwV{A^q+isIvKA&%$!jp^PTx$?H*M+}gpPrHm%A~q;c8hBvFb`oT#qQSl zgW;wcwl6U?b-!1CMxiV$Q%u{fq*yWKJn>%7qY( z!83Yeooaz)uO%G7{TL#8M7d(Ccq!LfnpaMI_&D>0VqQ_SCOACpGYD~ALJBG}LF)Je zdV(dU@~y+S??ftLqhRW7`IQl$b^h16635&5dAyo8OIRq`IkT_&+kG6#x=`=WnXri; zs>e9n79Z0ie|oi&t3#i~!EmvNJ5xhys+F$?a3YllKPeHZbZ!@*f{qa+gDc$l%daGF zhdsEBF+<35jFYZ6J{3GIiI+2Xd#gBYS>UvP$+0L^ieUP7YRx7$yapyoFi&uH4JfYM zr!4nE^5B_;#_yzl=2>0%;UB9Mn8stSeTu%#)WTBOxBg~+L`Z9V@lC1~_T+PuaY^XtoA=2{H#e2uez(}WI! z3}pNakDvoo5Ol!jkP)iA#Et*y5Y>M@5QQQUDiQ@j#lS!ibVLe*4oN}KF)6^!421ds zmoxeYDl7#=1yEP$2pJI1KwqJs0!2Rs-A)e#ysZcM@8}FypbsPZ3jGvxf&dUY&{ycEpc4c^=yW|0I&cR!X3$SE6wc3=q0U03p_2ltISsp@#yqFK6Q) z9uvsSZ!S2hgy$d_E?}1dXXsBbFg{?H!FUg|Oh=c@XfUW+o&#WDe84UPPS0OpfM7R* z>rh~hoQxrk27^lSKL~~wU|_C&U=RQULk|V!=#m+gppUL@I0yy-Fffox^oufZfPrC$ z$x=b-M@M5|6c}{j%0VzdjtnsPZ!ka_2#^YQ7@j%0WJXg4Rkd;u3^%~QKnlh$%0Q8N z&Y{5UlgvlhO?!0|2f;v*dd@yD5P*RXbC~Zhpgal|bmhoFEIKE;BY4`5%&A=U_` zH2({RLD&5pfWplOa4+Q75^?ha%*%ZkC`T8*0J{K*IjCx{gHU(@-UX)klU=xZ0N#Zk ziZ1_3`2MUVJO~EJ00Rd94Fi4a7*M4RWKACg1LVU3gZ~BtWY;0M54D?)E_{(?B6}6h2f#r200sui zs(vmL6v&H1zz+rH=)xBX27#_wIS7UqU|_(o{so2yU|`5$!0ay;{fE*Q1qNNPa}W#< zz`%ed{R<2nU|{aUSiC5A!_jsV0On_{(m^nAfPn$O$1gB2fPvwM0&{c;41oDrhjtJQ zkPZwC{u>N8z`&40fjPPaMuI_B;a?0T>t%roX^&A+?7?fjPSRi(V#FP1Zp$TuAldHy8*nQhzuUn4?SJJut{(z5`$& zKpHm!nVq_qqz$AG1C%<9`Mtje_MdOh|VJrR0u|L&5ExPiE7{w|N{?*Yzl5OsuZD@Hyn_KK-$Io4!{P$4Y!y zNGs-B;%0_fp2o``^Q$59$(NdM41HZqfQ^TEKd0bs{XVeS^=$m4ctRGnM+jXz_h4g_ z;O@A-fM>I8s0OMi*1m+P&x{*0kB$Nb%=h zBmwbage!u<7%$7tU3#ksrs8%L;(KQp!*e_$rG}O;FAFd2;#O!~`*5wnW6sY$kd9pW zU5~&al511lP?{laE+wNO)^tb?!X5>-_RN z_$2oC{Au#i(}bUgz1(x(HLgz$6r2pZd*aSBpNpI{Sr(XC)Og+R&CK6R%1o|wJp8g| zq|dG$)cb@39bu|ZD>AFhiF-}mnBsU4%n*K?C%AmEP=>BLs|E~H%M z-H|eTLzg`jZF&z%b9JjApI(5^p{a<+Z~a8o>1lVCm(+PVCq2)sZey++V>nQpAmlb; zVrp%{rxdxIHvND=e35aJ`4hnaLj^;%>~_%d<8|ofTo14Ni4;;{v1j)ugf7;@nA+BS zwS?~{l~={dCp@<4t_dmvgF5-#WA%wxiCJhp#)c5GwB~OwadvE;$ke5F)AQ2O*m6*FiGxO z2E_O%T2?59J%mg3-Bm1ns$B*Sxm-?PDKbPg|7rC`=$ApKp20V-j9pJ<`1jIPr21-)#uLMrwdya(_3&rF>NxUQNw5qEr;ouO#DedeN+d{0oU zVK9VpQ!jXUMF}@g;nsGdp4F3!vji3?&J#^(sXp@WdP5P_1bR=Q9Vxo%df8I5^)}ss zIBFzzH;i=5c3w!H|4jevT1Tf&rUE=T$hJRRR8Es5ukRYpN>jnXv#t(&mx%?WyS%#@^K(MD@IKn($!0N?Un)F$F zk!$kpF}Lg&k3WcOV#A16#d@usa7|&R1F?XsOk85YcxR}x1%49q(U*@pUGraJxmU5+ zZToD^cFk~!g&pV3`&8?X4K`+;YC8R}WaHswMd)f;m8)=-c3W(D3$+3GoK*T$%y2Z} zG<%_T^w$_mDR}aSHy3ehhc$K69jaC*g`(B_Lj7`GJWg*se}7Cx^n6^URl)PON|YBR z3(IKpnO0}SRlTTSh%yQ=s)He#GB^FJHAE{i+v6+ZRxgBsntUN^1@Z0k&h7{yli|Hqm5xF z;`$Q>(U_cU(@cIcX3m=PIH@^nRw=n(#f)a!m)+HFoTiKjD^MeSnMep^f^ERnnutjK zV)}(A2Qhu4CL&}ML@s_e?`V0%_BJDo11~fARQt)u0mhWI-gotGoq22R70cAQKIaV- zuD$IU##>$?ODeC+K7QfM0O@=1g(Rw_W-;a^36jVxk<%rWIA;AB3W(?ApI>V~+<9B` zp*8zXp{FL3)kIN$M>VNcKU2M8-TI>@pJmEhIXOv}2}R;&MVubng5}@_r-b^HrWP9m09vVzSQ z!^e*?YUe_kvDg{a zy{tdV+2AuYlTCwbYd$XK2?3v2*LazQMK%WkCYv~m?o4#{!&6xr8%*S z(V+E2z3g4WtVg3#->;~}GjCLX<=!9nT}FH4 zZGBBn9>Pu2VuYPhl8cyp)!7cBpZ`9YTZK{MV^8BIggjVO(d!K+E*Zb7cGf5xDTT~c z%bhD$EAbwkuyQ`O0awk7qMzh1aWHDUYk%a`e3i(A97q@9mGq8(%Rm(4t1zf2EH+yH z?sm!>?D@eqH!+=#m?;HESq}~J{D7jbB(GZ989k*}sVw?^*9dadwsgkUbjIr6)mH@f zy*MNM{G+=pBlLm_mLwn7?YpY}7j`Vkyi`mS+=;b|*t^*4+2|GG3rej6t8*|@OUP{9 z5RLJ%Sr{6Fc-KoGlsDPpL{BSJGTHwydh;Ge@3Pc_-=o>BM{%*L@(jM*J?2+V*?8au zDTTh3y6;RFp2W%b&ZPAp8>A%kk0bchH>uvbtmNZTEarHvs@2y&RVG+oX0+~%ih|C-wZgIKhM?4K=@>kOf&Cg2pRlKVb(4t^J*;>cjnHrQ#V0yOI zQ0R5tV)hfMa+&jdkxF4r+oihlZ1Y=_%s1YJUsbLw(iT#87JFk=r^uipL0T~uDmc&y z=a~+46G0GVw1|W-ok{LH-;naatpPzwd(UPmMXNQGv6_r@j?|5VzsBpv^VG->(^W$x zjI^whbjo9wKbo-;yKUWCi=$ez-Nc_2uT~a_EWWN2%RY23yyeUp@{4DeXeag`)0BWFU$I}uiM#kX)Q}0@?(*CvF+c(fJHoFzmrF~6Iw8WLntcuy5MO1i za(t8gqXS2OU|`#K_Nr9#5*--9GZ~&Rf{-Wrr#Ic^(pHCZ5fXAubCMwAK6vZQhOb1X z)~#q4U+qGDnQSR~vHP+QLn)d0>)Bx(j`%n7?bwT%cYQrH;$#)|Y0t{*2%nhZ^R&Xe+%;Zei$*eFkh(-}Tjv@FYfPYJ%zk)e3% zdh=;)jS5u}+0(#mLZZ1qb>}Jk{PXVEdh_PXOj&1h)E4x0M0DCKB-oBO?+)drT;S28 zAgEms5CsdKz#p!~=hh&T>)c96EioK|8urXvo+0>@#O;6uJ+bv+!1NJ6p%FMxayO=d zFw9U}TAFRse2qarG017~D_DD$xX@oO|BM@3L!39Adf3j0q|LbG$#~Tbi<_}#G$yA6 zN4xL5lz-M5Fz4e#5W5@_LT1bmas^irZp9M+5`Q)bMk2&24)X>$v+l%GKuW#ka?_ zSnr$;FB=T>P&bmZh%GH{^Z!)%G&m?-Tw{UQK&dIJ{M0E1;}-Iv@}{TG$`VqSLH7=>)HpTUmI}#T9-7$t zjKVvvHVYoP!xMI$I%Vxet#{M`>Ihj(oHCxFi~Z6tTLDsEyUte@POuK2=Hxdl3zLbd7ofB8BZsdOIW4B?El}MoQqyC*(?mfB8MVGmx zS@KRkt*=t#Vo_et%)QN%9;|v~oPQAjCcZ&?-r+U8h?HLyYd4_N9Pm3!9k=2Uv?Hig zQ#x%%@RC$ez7QnQKahN1GaJK8QGXHmUXPxteL2%RUbUiOI!BeB@v8!5T+STnVwG zaR`IZ6ceO0E^EfV&0MC^*kRpN-_m%WMBV$bF{manB=Mc#&BmU|F0%_#B_*C?<`)yH zxW%51`g_qD4}!jZrlJ|dTXFRv_kuPwPe4ut4oT2@`1Wd6+d%td zpsm5#m5XPGhs=i>!URs}<6WF=v`Og@D*Ymtrs7gsx>1z)H7AeDYSfwyf@$qY1uf~Q4Z> z8%{m2JIO_}VikXSc6Ch5>XbRY8aV--*RV{2M&Og_oXX}qerk6J2CWpcKG1Ua+Va7o zW|cpkYq>I>`JDeHNqPTJa8TeKk(6?Z)3aX-y;p)2Rt1XIi$y%OSGSepdfYaO#@6x@ zznaP8@6NnlljSUy&jm|Z>J=L$i)6ohrczIu^d;x{T-qJG)r9%kWvlsZJw8t2S3^70 z3Tv$RkDo;TUu{RSzunkB^M9ejd3)`&4mbvR zkWqRdwe+WF8-WbdBMuW=L{W8*Vn+jmY?gEY3<4Ra2e|bY7-XOxdYA~>{#Ip20)uLy zb`T6QQV(SQ{sM!H)WZ%1=3n`5f3~egf%)k&MIa;fKM@gnV9{SHNQ$F6mcuZw2U_1nO7nUz>pENcfiny9cV2P;oAH%~E zc+xH0rp01|H@|yTxxlbPTro^~e9kY=Qq{o1P%?t{(&@Iv(V-plQ&FGVLW5J~5z}d5 z_FVUs{UsIlH0p5)wOAS%J6~AM&p_gau(~_j&8M)qL`6-;pcmjAy zW9Jm4m86MJ))9I+bd=3-BW#U=%S(nwenuvV(sg{Ze8V?W7c^V_IWlBfPWSwn_`8O7 zhAsN(SKl8Go(a*3vba3f#WQlQYa8?Ukkl#t}L)qsbjl;oQ6M(SQ33noYo;U(TfT-o5=&=yzT7!vH;ZP^w*kyOIki%`)z z%ZvZHlsV=@$JILoS=ZV$tUrk?4S+*>`&-DV@-Ia@z9hKXJ+-?MjmX=IYcz?8W(e*Y zk5V0{zf^ZxwI=4Wm>XT;_m_{<#_1y9CqaTKbQo-|_lvAqimWvrHC2A`Wge;9Y}Cli zJ^NvTLFw#TuS1t?ItEq8rts-*ml6tg(C znOl+V3DFnHh_BbB2}Y*_AK|IF_hz1rwTRLg(qc~#ooC`pj$f)Ccpa}gdh*ol^7qPA zs+nHHy5&A3_-e|;fU&&EqHJqcD}Ws^h`{^PzBF&pdYbrtN?ZdG-& z9i8wEar39A-N5dCt8ZK0U()4E{8~V;i^qjQN3PEIH+m1M)!WY_8C?Ri*YNF-dJhk( z9D?(IM(_E%E^YsHB?v_#B-OOg9SA|_&VV2UGWM$oLidvcp*zWe&^_cp=!S719&~{U z4>FFW2m(50p?)Bv>59Mfr2Xv`|7X+5QByveYO>eIk>{^zPe(6jK@t9FV2~Ny5N-(a zodX9|6J$F1{@NO3I{Be$Yf#I7G%(2A2xOca=*)N!3^M0*KNw`r>7gn}_IH020`Z_2 z^dGkP1Ii#!)kZu(d>aO+I=}QIpidgm`1BAe2x5t~$^ z&fq;#{bvmy0)s*iCPhd;xPVmgcP`GHR&HDxCc@jKZ01O!_b0NG7!(}DHRRmbdSuk% zw5seBF?ZR`I3_8rFM>n5U_0F0dCa|VpeEy(-?G2#Ig%fZucBESO;1iervMR7RknAG zv!%*QQSwwCGs;_jktSDk0Wr@xwZLOeq1SEAyL+SGUn88c|7~cEP87#3t%m~NRsVn| zPX11k_dne$6raT;DR#LQ;}cVGBU)qIuAP%*OWa# zYjH{Ga?=x?#oNcRJ(sHcCtplzmz9UK>ROw|umpsVBuXnu88=HkBqoKH8a12A&^>>< zXd5ufDHCjt2WCI(XLg31bqnu<`Z<#;Tvywk=s-xArr!7_6{{DjG2%L<=WV&QY7;76 zu01P#!TPFnq5<)l&G!~_MN(UqZ~9N^Iaz9+@g5FexO;NYsjD zWo)~;rIa<5{(``WRUE!c>E)fxK~Ku7(Guq`Z;WBI@0xwbKZU8IlxWdf!qkc9H~#f{ zuSwAqA z{2|41kuf3$d_H!Va~0s6Orc@Dh7c-kg2!I>7v-G-u{S@}#qDYfBw0_qUO?bn&#S+R zrNPr!LHjnr&*{XH@;BT7ldy%i7|&}+K-MDuN90@BF# zZ;3LU4-(pl+8+hh-;cN=zG-|ZRsEsF#g4b2re@!};82~c1#N;0n@W9;$3MnAR2lbf z`9YR<%#vDAFHlkU6Ydk)r7+vtTcTs$-i_oFV%aHz7D9y|yr`RAn@mXx)V|$@YLqdbsI&%4rVMLOXL%))Q)61pmxpE= zVwWV(4qvP#tZ-1H)rA$5=cIl}8gW_kpd1oxFpC@({Qgvc%}Nn1X~5T^P?s7WckK)BZcA$# z49)GFeEEi?k=Dn8Eh%zx@V1+C)sH-DhpHGVP-u*F=+ni*JBZMyV4>E9+>wlMxezz_ zps|8Pv_I5AyfD$wBX|D7trTHX1oi5&KtWDKaOv$^vMm*F^ImQiSvM~PR5lIQ+kabo z26mn0SdIWuD}Ass;W!;dVqf~jpSE^*#0t-79@}OoVAR;(G~kNlHHxoAVz1B0@Lm@z zJ1t5QcNs?&XWY5wWXbdH;|niO=$a+`5UY1f?eu(?Czk!;N&j`13na81v3Ia3Fz!ox z6a9^4hEmn`gt`ceII6M_4roDQfa6q*MM)h3Mabydnpx`_{Cdd~*w)aP6=?D-qvvRC zUoI%EVmi9)#N8&chHj2M{FZ1ln(BE@=^POkS{S=@BG3pBa6h8D*_|Q)Q5<8KfLAcP5fpDRp1LS`0 z{T=nBUv?!5>>f+~&)StJQtpBLXTDT%bTKX*xl0b{eMh$|Q44xBFvwa)pcwa0BlpO5 zC5o+%3xBa;OAhBWR!Ozk5ZmVk5qsAc4P zRk*x=Es+CCpbpw{hc+xxa^~oi;pT=S-xqXX8y(%S=ym-fwavE4L42as#h#_!kU}K=5%v{>s2` zWQ`}_D*Wj)f+K4@57h?&bqf9)%JA|1!NABuu>D|=ggSck)MWasq%h1U~p zdhU_xKf67laBkq$MfMJMMtWAK@PN0H{Hz7@#H+qrgsKIr7+u0FXXFYMCi)n96YKb; zV+D~OI~)b-+XW*60@lSwrClHL*%cbu3T9s45ukIvI^TFz<8|c+&dPj)MII+jLhExX zX{%hCSDaqkRMzYUKyKZXnUQq1ZSU)SkW{8T3B9`W`di1P{#AFPic3GJ$4k%Ft38P+ z+wFH!{N`-4_z3ZsrcQX3z^T6RJ{`sAMJv85O!9SSPs%TPFflJlo7C{WU60W1mg)OW zaf^mkhwO20sSBRil_ZW8^%txS=NaE(jtiKK5IlF!n9K>XNJ@jU^~kX-GjeQGVD?(u z$~pbe3wl%uL*#~%<+(2kHVso$rpv5a=Dq93KhY)v_Ukht5-*PTtbZdS{In~SrYIsf zDOJ5%tqdo_GjYu<(t!W^(jqU>`L3-1%=vG1UYtygv}0$QsFlpFUHM`fLw5Xmy$wEf zl!@=?r+W`3pBp9Scab~dKssKJ0+m`ZakE)xM@!@T$;{h)bZb8Ab`#hUIKQ8GOs%&) zUa{*XMBDnr9~xY80!*>+WA)V2jhgWvh0DS>`9Dh!)ve}!sqil->3&6{sDnfrlx z$6}L!I?A24%ftmrQFd~dI@V(<1!H@9f?Q|)#Mr?3%^TV#;|U?Ir+6_>z2xiC9$#@!F(ur20^JILuC%D7JSgI7 z7D&*A=p`k;V9#>qGN$X}=v8Ghgg$O+Ow+t6=MAC5{3__6lL$;KV|KEhU*Mh467j@B zFBii~{>_KGIwMK%n{@At9TO~{@m%nB4Cohi-p+2jEtnQ~OYrg+nf#OPdHI1Zm$A-V zfB%&$`c|WDQ4M4Eu36s7M9Ol9a8@(qDZ@xR<7V;!GB=e@x&EHBynVBWwYEG5h{z^3uEfTQnEsa&)(Y zJTI5xm|N>o-05#&p0bx_Sa?RuTJs^5zdExm7QZ7i37;pDxzj|3gWr}hGm<~AtMQ$& zI=PmI#k8a1mh@*qP7yEu*y-YqB~2+wwuRTIGd8Uq+bDQOOry)&5E|`UQX~jGeH)4vYY~@}{ zUL&EZ^xKk%ItfD%^{?4a@au7ub2RWiV=53CsRFjjWkxM zqt@od;@9-Jg_UO&38R>Oudg{4J>k!DWNp-vQizOYA@+J=b6nMzaxJLtRg28JoGqgx z&XpBhGSUoXpEplk6d4{qXi2=7*33|ZS?F+_EI|H3glt0r+}lD+(PH9-xrv-6#a#`Y z{!O8crWSouOimM>GIGj2|nkAJ2EUPmugIOe}U-2G+eoq)@7w)pskoA zigu+n!0yuP4i1&R^I6V5*cI2R5pp(~iQm@{Pcc>1|!l|*WG zJDy5+R19C$@Ub^ulK8;U+&fK{7-@0!Wt!*qNTh;!kjN+r}ND2Slr_yS@pMqQOTbSltpJyJP$<)bZliMa) zzM@*0OtsiA{J|+Ko4;R<4!?WmJNtks%M~r%)G)dEo6#Dw>jMsQ!>ItPe zFkCk72wYWISrSxuz~Cio(aY!)rWq5*7ko=kmr*OlQBK)ECpi8r^OXN1-mY+jM#Cp> zXaKI;jhIe-_AhLYJQvd?kxrpMkW9D#W|n zWI{XMJ@qxySt-c)^sM%S{R?9q(mP$6*CE$vX5D9>1*W8C6gN9wCS4WZ5#=nSX$q0f zoW~zOrP8)WZlQxS(r;aT&#LZ}et^~TRNlNT{ctU12ahogjcLbS@L2LJuIz;yW(ZOF z2XYq_Y$&ha(AmywHd}ea_el6WIdu#{x9YU*v{OKkZX^aZTdlbII1O3z#jc1|69ycT z6{vUF5GTWn@IE@0-1`jtM3w@seVzB(7+S2E?P-nHaAqUw@Sfe4wak{}8@o=(%N>q) zd?V~txQ#LP`&V^^SRAh!yi~E8C3&i8Ge_Hu^J;{{S4dox?Nq*~)*RDe#Y|=#!#iC~ za{0AVGOIXy&Ko6eKdUjkAy1Y}I-Eg8^?~MNwWQ~@d&~H*C~K}O=Q*?a(^jtPsorbVvXr_? z;d9anvkpT@lIV#&gOUr?75ndC;;7C3Gmji21A^1o-t|v|S$OTpISa3AVVLAL$$om{ zSnHTZ`onRvOlvT^{UgToHS%{kWd0|AyyE?6GeP9p(!i}DX0c>B#cZL3V6w?!Ytt-bvm|EOMEw!iHa8P;Uq#_!O9GvO9pBX#f&pL^l5go> zKq-H(PX6Of@&6c>!pQJXQ{jJrDU4rp>aP|0|1z5Lmm;;|{~5u#97lI_F z^yPc{7y6_4wf$v#`q$#+7c%s3&ZrDu&Zx|PDRZBqZYGpaJ-OD zs0icNWb%Kw==1-}T!P_HnX-DdV4D)GDWrKdDK zIy?7kMt@uK!}eLiIwe-L{h*5K{L)MG(yn#~=q4~`1lNQbu!+vC$kJG4#3Q}aYz;SG zq<`L#V&+c22{CT0CpkPQ^ka{6m5kczZcItf5z0(e?^aP&zT0MZOqQKP_fC;5XK6qJ zT+cdky%DvgO4gPu`r9$aeli@ddx>>La?q>{yHiOZkj{=-TjsC$;OA?hl_0WKZjipmupOfF9E{p42!!#v9G3YZjz20 zn07*yYYti^x6*uw4zrgN5tUgHk-@H%4GwUy^0|ADvEwm$u%DDK9hjLSQeE49vw)0@ zI5Cb_?@9LVuu;%L7K$+#@U~`MN>4->Ae)0=nYUJ1r#PMWi&Y>bKc@hGi(G68@S#mT zpNvFIm(k|e1r$Iojg|b_MzvmcPCtUxg09<9-iK7rJnj4`56s7yhgK>eT~QKlL08G> z6nAW$CzC3}y&!;%Eqy=IT*yt+Sl10@;jG2Zfw*?KLbi&KW&uYK$dkcQIB$mZi>j4L zgJj|EV3!(2;r)2$Omnu{App~+z3T!Ljj*^#VlmpHKsn18xRG_uyaaEs{ozmtaXFog zQw9?L{f*J6C??-KuzKRWSc`NH=7u*A!LW?6NF$1tfoWRVRKcpiE(BiNVWuYi#{4wq z+o9nT=;Hdb#`)r>paS>a%eE%g#hX{UgNIKrBx2rV zvG*|#=^4yt^l&L^2oq1iW4>FP%vwJ$U7pD~VNc}QG|#2+QOLIwK{5#UZfdOPPn@@lV$#GO5BqugMgz<2iN$r2B}^? zf!d#gP(6!1BKs8Pv2Z&HixYqs&`>Zsa7@iGR3Ii>z@<6Y&!V*;FW(^9nc0;bR@~va zbFExv2=#Tac+dSHv%7yz*(yC+XJPBSCe>eue)B0SX_A*+DO3JPela8je2GkfUO|u; zc_82krwm}}zTY3+CB7iQBFJrYy`pa+pqYYz)zEdOGeo1)$lGUjlcBpvk@Ovboyu7b@c*$RZK|5!pV&pg@w00 zi|SqW%8<$7d5Uwt^g*Kb^|hedQ*I@+(-5kEq|-r!R5Q_eTL5AR&7I&_yKs(3zkqkU z#EEZdml&IT0BniCC2bQaSxSr(n{lQ)c)2Jcn95Esa1)I!hArcFa)F++WVYTW04pS9 z@)$z12=t^V^YujCh4@>GjKp!n=py zRON~^fZF3G;BDMD$<8o>FF6xj2-FWqxekEMzaEag^m)zw>8$3=?A9y2&VN@mA=l6S zUH`cbcJt%ubbZxi2B-I0b`Fr_jGz^7sk>1V#SXwTAkp+_#S~9?5#hQ|=Gq@B`Kjh3 zk`#vW$Ffw&^Dk^E^==vPkfv^3`m5C|y}T^P6A{@Cn1S>I6*WJ)?}}$8^cvQU&FnQT z_AVgPK#&{!2DlLu%<#}$(?F&SP;?Yy@%oDX{&;&HCA#;Y>k-yKNf(iO{=f5bEW4u4 z$_;s}U`jmr2WDEuF%3;SWPH*(H^bsym+JMpeT#N%FWc}))w|Cz53g6!#h^L2!=&(N zrk>w(6-+Fm?#9H)c;1Q%FlAW70yy_R#a?~6SKLmJg!vX^G(E5ovCv-NIqxb7yrDIJ zRbHx2jGmr;*gD1$(`Y|b>;765f?fij%1$vdONxf0^Pdd>oizDA414@^=Jc8Mq`Mb2 z`6$dj&!q1v%jrZhv5VjYBhp_ojcMuY=`=bn3tbNmggj-BRHtra8>8GZKEJ>3qbEQI zJ7f%j#+T910(4uP%&LfMB~>%I{cNJouuT++8cO7ENX_f!G%2%-DgCTZg>CwdH0fps zEaJ3-Q)|`39%LHD+?=*61yG^A#&SJW4rd!{hVRYU3|tQ!-EGi&+tsnu z*_B+pTghWq9Rt?B7K6SaF$4>6l1f}B%cQVUxkJvVJXq zEw5ze{_u_!4EhUUp3y_uTSj8zd8wz4_A{FIPfTN2L}-tHm2953spNql)Q$g~WlfyRUC>KCuDTPDky z7brYpc^fgCc&ooo*)<=qA@mb$fYTMWeY|hpmKPTf(br6#NmMR_GF{sp`{5M|7Eq~j zWH#y>4p<4pX8rGn??>MUyqmMd*x`=O-9qsks51qu9(;s|F3eh`;1@J~eM$k)4SO(A zD(dZe0d}U$Xw_(9U!cu*p6FJ*7^q5#Z)_fF@JYkf|aH~P(SyEEB zVVQ*M(u~hr^A3Xqzxcrqe-w@c4IGs!Sh4=u7t5 z2Gf_GbZ!-wLrT=A(2Tbh-)cF^RW;KE#4J7EdC6Q2*5p4_t@@Dgp1&K2Uz>V; zgTg%r$gZSXIAmn3v5S+z>5lAf!U3@YIXn6x?DzK4{y1baZYD}SI%G`GY{cFcvFYXY zc!_;`>8khiIlbo1rt7R$kqmIVIDRj}eT7sQ{eJlp8F|+J22ya|>RT>$hbIFBeejG9 zd)X+}IHeBStk#>e^leZl)?y@%W?de0f%k%O6HYN75mO0B1A`XKlC#U7mlL6bt;+@u$d#`q9yG3czr;sC zx*_^{du0O?R61cPP2qkY4~PIEFDW`4wQ90*SJ@jC$=B`=rc<9iS2tW`^@#{7qJ)s@ zkUqpJLZNWSO_3D;G#XX1tubE-ZKU#94KWi)M14Ueo5HH5QF{+wAF0Vt^1lenF*No~ z=+N~o>hbkToH(?*I|~f%42^lu60P#G8mEh{&k)_HvDSA_XERzQaO0~xmt(i`)RtGbz5dyXEi%h(y5j zDEaZ(-AdwYN}BzS_4{*<4Ql6dG5ZC!SN949t#hGkRSClu(|Yfo#tz3g`;4BWlqNG0 zPLqp4;=<8!#`Io3(Ut6T7TyUGA`*Px8xr2Ww;*yaw`<*QYV={lMZQO%K+h`WJQ|M* zvwS~5#u`VhY0-gl9DPNQ^1L~D1@QGbD&cu>@(w`rX0STP7NA;cy=#04l3@@&wNS15 zM_LLO5S)6Q8Al=tyWe4yyE%TF{h3xw+Y0t=#4;N|`NSC4%>7%`H?%jxx|tPDY5(?) zUOVi>`VDNHtXNdTyN?=PYLA7x(@p)NZc8`)fR4^pDgRxbJ{E&fn=1Ce=HUsSoaML z1jDT6QjTjwaim2;7|K_j`Kd9FCkc%UQa=RK)g~S>eMj<0&raNtn&CdCK}rHhlF+Us z+EZ|nl8^>}t5t!Dm|Gn)YJjL6$=laX&c=4djxvPx@hMg4NN%r&m$XNEO2QB}Wx5(Z z+jk1JbgaTE`~(em{*JLMHEmbXo|TXR<2$c26*w_UJrc|KkRcd&V$shs1B%iyB2$AC z8LA&xI!QSr`J?X9&Xak0ZDqU|5^*b3vfqzTI28BBR2f3zag5 zLTFF)_azsvz$?Dg3Y%-P9r>YVA;I1uie7=@F#+t6`bPtXQu@ScRAAcq3By<12aV(6 z;w6QLKTEyVl~7Ft_2tY`wUS&%Ser$R6_E-{a3)0ZfluYov?r8(s9fdoOA?WrE3|E! z@g1n(RAbswlaibr7R@=VzJWSVXQIf?{-8`6)_<%+6zka95<)FYpyHk5 z@?4bX|1BIPQ?5tW-0w_azW^_1I0=4n_G; zJN9!wWIh;tN^T3VZ!ne0+|9#DfUrssdRXFzpC7DDh#m^{+vTOlqwAM^Ui2Z^XoRo> z9J!}-d!W07-$K~){&LO!2H0cM8S&9W{e94upIM!*{r5rbo|#q?9kz26PjT9cOek=^ z0A|Z=Hmhi?dhxIboKeqO{daQ(5dmz8^{tAG4DA@~xkede#~7rB1LFjPJ$#ZYBPP>sV2kZ*Efg@S}1W%wnwZ@sO=#Ht>sV)p(reFx!yi+Bq+i4{ibNnC!)*wZWgxpPbq(3|*} zCZvKAM>i!b-q6A>g063u_GF=5&xv8df3VyJN}R7vg=1Fk6WR!7tqcCx+Ih~WIq@7jesd3QzybO zHMZGMsOp#8Y$CsL5<>P1KPtjP2K{E0rBwCApQc2Gj;f*0Cl!2ij>o_wFUjaKv|rSY zNsVA`UpnvV(h|)~_ad)s_HAUru_R!l%$_?@E_1B50<1pHxC}tKmzZOexH))^g0YZH zF-cM7o->}vDK+nET7fj!si!$3liHBgw&#R5`qqXK^af$sd?!^Wf<{w4>NdV^hI|-h z?TR!I99EZ>$=QJTeXFe>DR-0E7jYFNMac>t2R2Rbp!C%7UQ&p3D$TMiHDtEsXT!Qw z+IHEtG?UUtwcMR@45*YD|2-Y`ZX5%ToNZ9DyGmlQ)46=;PiSV_$u`#>ru+44DeiH! zxMKNr&!Z^oQ3utd<{J*N+r<9VZFlC2`D2w1Peh8{1I%UsmRBN{>b!FT)8&RN=EiKj zb`ovbiez$zd1ft1C;w{{Rn~LRM2vJ|S?a>U2BOL81O4JT6=Hm=is7EY1 zOoyYb$2HvTqXB5%D2c*agL^|KGqISOEbu$1EEH)Plkgt$`RQ=0XSLiP@MCOXH4q%x zaPfv))HMqd2F1PfzHawRO*oHsPd6~%dN}6tSOJ(kA%%N-h$A%|IvmXm+&x3>qF{Z9 zvg(sn=tt}9jGg_?xn-s~y%?2|>_m&B`=I*qyr} z+tH5w?Eo@BV&@AO%X8`uUJD9tZPI%mF8+>)!AH0SEx`HH=H%WCx(G7 zF^>Y}=xh?k*6|uL5Ohv+B{59%Bv}{BIy;#+Kg&B%rlbEFSB6epVyFeMRdlIpRMv>O zcUK2$zJ?Wu5;N66ZqP$X=;I%UQN%!1kpdbbf;s*`Rhk(wI45mPbZo*`8A;b)glY7NoFI zh~ecW564d-xLrZl-OLwvf(b1C8^r8 z?01Dh{1iizjbpH)lJO1yLwSQp)oz$ij;=lbgp619%ex;!4QW5TJW2g-u3nUa>Vl>| zZ6FcC)8b)fS!e8W+A=0Bh!BJj_qum^TUq(3eGK!DeESg{fH5fKi4`i`(X{aGH5-&* zeYB;nAC?S`bNx^fZVm~vcIq~mKa9?PfQsMrdNHOklLC66(BYKuHS@g6GBXfOn?*q0 z$xF8oA`2dgQmOQSUrJJoaEU%Cqt0Ni0uQ8Z@9bd;pc5({facn;W+hl6FpvOe9^dVO`o))8R*@!FLD<9S2!!)Q zt?`pVtd%xO$UA^=k>n~XVLWyQJ?(2C5-sZ~OZT*INzlw{0eelgOeZU9;BhkGjNGnh z`Psr2jaqRWfI}|W5sn~Az+YI_Sc9SkWd0nYV@$PQU;f15Te@ti+YM{n18I^{ zZW!I;_3ya3IMB*pvhKL2dE;+LRyOO-ZIZ4`p~z4(Vl4!)!|OT7~Oq1-OI1Xr%ywq>!T0;$VMvY#gwaL%=S! zDhrkBcZtcL=vLBF$KwQR6QrF z2x3Dqyd|hn8eAwt^E1#PlFH8~=1tOS)?|U@p?g6O(j>V|?!*`*soTWzrx)R?9hZb= zW{1(Ub{=P9BGCu@XtQ`!17)6fysat`9ANM5USzIuS;P*dLPnv`OmOnb`I(M1=F7>` zN=X&p(2erqixYFt{%risQPd)PX9H%oTKacRUh(rPkdY~EEV}MgXT;=C6YGO9t%k-Q z10E~il!W$;sP-TebK_}6NdR|?)~TTADf*_Y?1d=rE7c#8j>B`2cz%zqXr{R0kfX1r zR-Zd&buI0!#t>ona8m(M2) z;s-01Eas9#3Nt?@a{r!=hq}j%{wZob7hB!yx%6Ol3a;Yx8;uduI*84udHEY8hF`^2 z;Vh^F>b0u~<xFW+@K@q{M+NhB1*jU=qVS zX)luX5eAO7)s;~zeFP#4*|sCTb+qvxZnsr7uJIT0fP4n+gYb_>a7jE+3T|pmUDB=- zLKt{Xm-1l{z1tnVBCXysP*ivhwz}~+aA_`Tt{rC^$pG_wR~u|dM}@%a`%LRq;v4*D z9lTI7F`<`P^s~76xox4xaKSXc@Q!3tj%6F4KM48c03os9E^$SgP@qPI2w8z)@%Dve zDNBllUqMYts!F_s$cpTW#Ey|*}Qt{a(w3FBNeZY zs#)KaOup5eh7KK5H<(vx-8zRnhlJOFJENQfcux;?G`gI*ey;I&Sosi5u63-xK{iSH z9;{5rS{?-?U;5G#1O_=ig1KU7$KFpnuPW46%Q8|)Uxr`RDmCf)-oBzYPZg|FulO}- zL(r$k{i+Q+RU>V`>0^(6;!;d6qu2CDTlI4VCH z^+6UoNKG?`YImVKG#%&R`8ue?jJUlrj)V_f;ny7iWNn7LF(0QR8pjAOKo|dfW6lpi z?X+6`ns@V9jIK*JT$FNac8Lf9SR!gBhVW)43^w;GUDs5k>Ot!Q1Y8qKr4LTXDNZ;2 z99bVh9#H`B%yoQ){L*H_+Y2_b?c5qa9Q6BVxft0y%x>E`zemOC;-l?B#l7Q<2NiJ^ z@N?fa)`IU@_T4uOwXe6u^T^|i(c4@OztL`_0Gz$2Hy$?1-@V9ZgOE>%FSL@->R2Ux zH`ygNe!pfzhC6yFOo0(l%~Eo!o9TcYegp1o)Dfw@3Phr3d2_b0b8PgPXGN*5oIG4d zUc%oX&OsZM<+}JSt9LL#**=z9vNNvxwLLr8-MgET6zXj1#F4qU$0e*rD`o(q0Fhcu zjmp&VPM_{_)~SmVX`=C3KmIJnjZS>;D|Tu}<1Q@7jYe~$hScY<1L_1y$KmUuR}f$G zD6o>ZE_-$TIV67HAE z6wwiNyq17$_K>RIueSKsGZ@1V!wJ(3^jYL1D+-HQ_87_GagYP>Y zy&dAw$#@StYSz5YkgR8kR=G1ZnnoPJ_c5B|p@Gk?htD@ltrSuGZWl*Pg#ozo zUQED@=@}p>bwE2%M&M!)kG%d_sQa+opT{wT&`UC$(r&c13UK`_Y#zaB5>xk|~KtMd~Q7-zrsS4p&9)|(M zbMP<8fd`AtL|67pzRn8;Q@urN{O8&C{lL`Bfi>&UjjO}sSN6nlsrxHDI~eldAWJ}O zT)#^O$?G;PXC4B36RxTUR`&pH(DtSbVT!ekUvHJ4@I zSPOmw(7Lg>d&pHL2b*$Ff zi$0_q?&!3|OD~sMYiV^~(tcQUE~}CN0gKZK--@02%z5q~JCvzN>&H0Ys!<Vw*A%ai&A4I(k~QDOsUEw`vLytk;}C+!)yBvtce}S=dVFoGO)Lzl+_A$D z(rnHF7^Mk8_2)ZfyTm{VQgkjgLB@f==h+4f+euKsFGWv0Mb@u>iH~mmaGa@YwNinhrD=9-iEhu1x-`g4{cKmAlfgcsH`FUOPp*~sgyBmrn&Oh2ecAK*oT~u}O z$JijgQt{t3rv0`DTF zlu)E1;gqE<;UddW&jgqxHduJ&C``8~ekPPb2gU(zJ){CX2El#pDBii0&w5%3w^Fx~ z5ll*v_jl_7!c@?Ts5B^win9#82oQY&Ajrf8nz=;t%cBq^Di2Jqb#F&cm{-Mt z=boFF1^Hd)opL7c9|7l0zb}FFHw82K6PG?~`u&$T&wYTOt@s}z1Jt_&c{=fxu<5)v z-9Fl$RR%8ka~MAD-YLmnURktuE}1L5Bh~I~<38wImNwhPHu^~ChzP7c&VX7J;TLXH z21n^7eAV8rPTH>4`+5GEWqyRO_}&o1wyk@Vl%x!l_2?0Ss7H|K<+6LbRr$Q*+G%Rco_c}9YchQ>Bd zU&44y^j{rE$=K7R;evu;e?Zl1EP0hXzGPBcvGyeNTS|)ZzdQmHVQ%3@}zqpG+ zLbh&Nw2WU`!?aASe`thX{^Xp0Tt)wM!ml1{%=~BHUswFIM^@k3m|k2$N?QIKwT!!i zxxUdC6OmTY*wood-+^AjN#DxcP|(KI%9wzGUdhSWTAYA|^{)-pFXdzUKb`uo3;zkn z!Q9Tt)`4FAA5pThemzaF#$WOqU-Uyp#(&Nr)pz__618#q&+q%E>h6wTx0A3jvHc=M z$~hPrJDA&;Qb-tmwT`)y`=1mqQ}eH^CU*+KuNxSD$$mK7*;yG||7pChd?)&EYJX%Q z82%r@eO3K0Nr->>aQ=I3=+(5Cn6y~Gd^L6cxeEW3cPjjqPW4q;3i)rM(ga^=vtAQv}^Ved6oK7H0ZG zmi=2q@=xK5o%^?x(uo`E8-4wkFwuP_?Ed>RL#JqLV(egSV`xmLWN2n=t?y)R z_>V-=FY8-6hA#_i#xG?7mM@1+<}WwFFP&`0Kf(h4B^%1GXYs2q{3GuFm$wE^*8e~c z?o%6$AZVVoOmd|7Pb=SkZ)P(x2?^4)rTW_Xf-2&L^ZkLYzrK=-#HV#F7+jt5*j~J(meDpR-krQjm;kn@57ju^Q9Eo85h%n9Tr^A`Yzr03! z@~#~yVm+RRu;uM1!doe4`F^xY4DM47H{iYQ+Vw-^W}0tw11;}}zzcy(u6p`$4%eB{ zGNuI{BPd*^Vw!zREM+30#_1~Al!v04@nt(Vt-7BIM(7Crx6Ci;{qBB4b7Bu9TX1fV zo={vXi5<`PkwORcIOuc4V>37i?a(e}lA3j><%Uz)3rP$&`emex`NPGgVqbn#mh;yH z*CsE|2u9#W#rM<2hZMQ;%@gbuFAs&sPxohZ95ZTZ>tM>P22Inmn@8Ik)erB_{ZcFc zQ)z#PkVD=5XP}x1pHTK9PdZN@pBLh{SKk#FNR$lrdQ8;?kjt&l9~tK^jspC(EB%ux zAe)1*)@qaE4~I=?z>z^#oW9Uxulbo>8kEHRRfpH;NB=7Iv2C0V=>mYQy|IF9A z8v&gP$h4k!Y+|q9(jBX@ldNXTPVmWFft|nyba0+C3sZ{j`|^&APy##Zo9xj#IJ(aXP99ad zo|-l4x>solmj1=o9j27;YkPf1%dIc^ql0x`i*yyT)<(Eea1EKUKVBWICsJH~uQwYR z5QBu)B00G{^g4tqaS_*|j_k4#TtHp z%{c<<+`+d`avC@4F-?2B>>S!MYB~V;B%8z9=IR&}Vn|FMt{4Goc0iZ2Wq1Ol)LW}t z7?P2L0LN8^IkoPb)U7i)agw!9)H=*TLVk{7=S>?;w_@Di)rsNTboE;Fbwx<62CX;T z`rqr@2b$&1fo%%8wr`@|3A*;R@EJy1--*Q+Nq^%iJsj)hT!Ld_NKrp{^OQ5gRvs-< z+e^QfuRE@srd#R(?XRmi?Tc;nJa~{%T|q6g6cIG$r({cgagx(bnlH)0Bn+lpszpaA1m zYNd*01+l*p3ki<|>&NH8HJgNxpQ_@w8CMCVYbIS>0)x{?9>`%pir8mO662AiA82Rg zxChXFUxzoB9>b=~p5lV`vwKErI0OW@ z&PNi&cWQaSWUfYZY8rels@>~Obtm9=Eigcb=YkL08|T({Y#M29m9bVurei; z0!bMGqVp2`167buA^TDq82m;}jGdhMK?1@6eM?=zw1 zMQ8S|t)m;A=T0v)>=3XQr|nr1^|9&&A|aU#zk?3}7_!WdxrWAL9z0q!l&nhtfWR%` z$i^PTIqPK0>!M#|LJN&N6Ha5ol;gb#ge>_i1|%oiXh>j{grRkcIL{d3 zJO<)mkm18JR9sI#G%NXLY`Q?1r3W#_UWx}&!JU&f3gTiCu>3kz<*Of&y}cT8LmqDY zc&?fkz%2Chv~!S##iHz0nTrv(8`M@pBnO%@W^QJIIblOY6^hI}u>)n94-Ajrk}%00 zl%&XOH?cuQ{kgC~L(=vWjb76#V+U)+0x{%Z-*Fx&7tNq{Y3y5M`zh5_AV2n5!jh0; z3lc%O<8z0iegmn$c*QrLh6z785RJO-YD>wn_(NBFa`0-6!_WYW7#8<+U5?{J?xyw7cF>q zdh-Y-0ns+!6*}=%a=B}@RA>aaqU2KR-Eg8_R-|p(1(>abYsNCUEum%g7 zqW>_+Q_NA>KIC3BS~IssqmCiRW*<-(BYWtoXC%pc6Tw6bscY6L!y_wXK5BUeDMc!2 zx3yA$B?hD`vQJJ7iY1-0+jJ+I-zs)gS#J8!0y!VB5r6@sp_o|E%SJsw2hYw6h-@z@ zr%@ZVW2f=5w}fsI>ZvUk9#n%wW189L0qzwzDv2lUrYe@i za=0MfVcwEW(rr5}^ttan>w&1c;0OBM2CH!V3b(71SBkcH+ zXepznw)uttL@$LNCPY|_44*ab#cB&E;uffq0-lPFh=ATE?|3k*mu4SHn@-}3gnF(8 zwsjPhjzxzc&)slsf3!@zdPP?&*Vl$&r^4K)z3kh(5 zjXgcOXMN0({Q^398Jw*X3WQMRIg=S+ww#}Zq;kuRT z$Q!R49F>cBCot|o`CU;xr&UB1X~$*nr-INjshC*!NV>i=^*a*qXNyc=m>_%k7?(!^ zbHj#Kxr_@^u9Tdmz-jXtn7-j^ig)6Z`}5f6&AYMt<76t)u6X3lg>4Q-p9rpl={gcb zyhdwrj1Zd*pnW;Rs;)YwKCs+q3El-RsOZFF9-I=Zh=2{l9MgRkOl5Kx!;SY|KX4S9 z5}_1lg$;|a98D>qSPyQ)sT6BoW>Qq{rU_z|m*nhAP|gDy7ARVPYC#M~m^c?}2pM5? z^=|L#OIdOOI%rEGf$XF=Du2kiKYvJ7^2gpECO2J1!j`ZChYdP!Hr50|n6cOpBbr+Q zSX!>DONFj-6@YjgsunRqu-l6qNytj-i6W`ugth5tY2RH{v zTgP`Ya&k}zvfJ9$pH;$MKZ+cv4ZvyVZIZesp+od=c-41p<;=D8f& z&X>k~5kcfr;5UGpx4R?Y!d4y(|K&9>H~omn%4P4m0vZ;UY03#E zC;i^K@EA0S_=ZqSQ*Zan7lUkmy6m1Jp~YE9j$!us2i~?8<;###LN>+BOp^oX0M@Hp zzPwo@N7)7~&nAi%I_hNEjth5z>MTps9n%&O)W@h-!`?tN@U*w=Hgr9q0!rZq%NEB4 z@)$1An8QUEhGNBJxk2gqgb`Ss9YQ^cPucelxgyr$dWl{Nw556V04Ry6JFd&}>0EPP z?6?M-a)I(^&=u!xoh(S0lJmU^jTp_#?|b#YIB2|*M%&+*1^i{|v$ukR){6U4X?WfB!T=hRIbJ&^e@dDcpRIZC5} z;oM4W+Orb$7F-%L;Hj8ufgezS2EK^HLPU=+7tfJE*GugO&pwxeh zdZ2Vq>w<{K66&~N$>T5`e{+}@lL1N#FNlzI%?R}hs(I^G8C-T_nu5@YlQhP-7JfT8 z3g2)QNO^ZQ*YKVZ%NbbW;mNO;aAQ?0M+r04gnc;o$2MU06bcN!pNHj*U{lF%KsbzF zx-M8ebY!>kPiNJZZ~hjR=iY&2b&>oINi^*x;-5Mo_*J2y4^(Fx+zukW-$2tk7?w1R z9o$EZfZrR|)~H!LOHB;=pN=|I5HM6TU?BG7bxEvrkaXF6MyWwmjX7>v4UEm0ac9Ot zBFbbddRyHApc2g=_QZIGm=iB-t@2=X6vcO1ssY`$p}v4=P^P*^7zlj4GvHNjMspqR zLY6SXu?MsXm+1jd&w@que&OH{KpTB2E{RdF+p@*}8c?=t#C*9-D)LtUtx(`R_O4Pr z4(sa8nU1U77q1H-_cM`Gry&M#@?%Q!0sxO)lmL~P=IuNXm*wZ8VG z&Xgx^9j(B}NjW(#jz!PzLnyn1Ng^Bxu(IdUb#=xg)m)u*l-zan=~m2GfO1K6eUSUH z*6y!|!oVUA4;R#=2i&aW$xl-QnXfQm8(u#+5Gj_C;SO+5N{{+az=*CQ?HQBOKdTl` zXYK7HcMPbKpfAXn4jhQD4o9e}xqahbd{btKK|{vg2YMWeP#t`qA+p}HZuT)!9m*IP zbh=Q0Q(`^eUpbaMc`xeMQeM<5r+C#c+Hd8HU5k32-@TQE@&O0U#6e3_r;*@!Tqj$6 zFOh+3x(BNB3mhUUBTfz21%KI;lA;2|%TdR_0X9NhrB~=#aG(#dn86|buKs1$P>bLN zbpjXv%Ym^^7}+~}#M+x^r#yyvF+$5+B>$Gmm`#jxfDbeK_j<0R z7gHYsZ}2x0AsI8U10dWD*^AiCA70pH;5_U#dX_wM)hbvuS^Si_c8ol{@kgGgilHO-;z_JYDh7a*D~|g_(g+r?5R@>M|XbyWZ<{Y|Wj? zE(3!Jd4IWw32P z^z`2D(}*Axb3D*GY4kw~-iHaE;Hn8vN#7w9i&O})L&m~_HCpHll(0{`-nqq`#XjUW z+y{SXe8`~0IV;@34H1~90>gsqu`bzV)sw@93R-qCpU{ay7$3R0F8B-%00~6}#%;B= z%b`kca5>+%2|VefB;8>#Y|_8mGE`YQrdyx0R_^c-i!yeS;{=XLQKBjgHHfzpC8q9C zHHGA7(`C^09uJCI0DcRLR&ImJK=2E^q|(E6Pe&$Rg7B4wbv#qzLY=n{T3d{z+tE`J zfr3Tp=IOoZ8LB6*UndsAGsw>nn)1W?6&AJ)u;x!;9+49An8pE9nfn4#b$B-==ttet z!&v(r%+)rk^ci=^rcqSMIq;IWkQv=8Yto_sQ=#A(-gXZ6XKO!^se|$@1k!V2FO(7l?_&ysLoZ3JW>WI|+esuK20)d%c2y^~i&fRELVBa=1Rib??z6cJ{dh@;y z<01)*gb(JKv_nHb4-O5p2QxP2iK0%N?Ww{f_Z5VQfOwmuy`&0;7YES-W|)(KE=#GC z-y4O!Kd74!dF+h?RB4>=HcIChr(T+>?C{_#?{6HZFh^?}9b^(6i%}YJrv(Aicl!iq zc0BaSwlwSS?EIeyU}L^Nz);W&KT}?@FgHjI;xbQ>Cj0bT9#P2Hs;OyDi_(DC6dW*H zDw4QfCqMBv&nf*@c=WVzKL?kE`e|Shv|48@F1~b-$=n%MKuG zhEOJELE2Dd+ZVF3sh5G^LuS!Db>p70XN2}KWpaM$l+&G&{ubpx3CN3j8EqL|Sw z`4A}lz)N56im8-&74-KX&Y!n>AFfLan3;Upz*l-$FpOO1&1R7_cA3)B7j4$^yxTdH_2FS{1ao-%YYy8u70x zkAT>VZ+x+kwya%=8rl3r_ds|x;yhO#O+91}TtV5Czx$jK6}IP;rM()F4D4E@$K>8y zZiP(3K!&ZSnRqI%5S;2l|02f3Ah-I3mxG9zJzmZ#u8@Q;#DZrE7N`pIqe+cQ+`3@H zka&$^Rw9i|xz?f=3BpO9mZwV++Qp%MgLq{}qqbD4*WBBObZ^Ww7qoJh$&o$GGoOVi zixq{TSb2V^gi!~&xdnV}G~VvCi}O&_3s!d1*7wc5z3#y^Mvbg7vUKf$8s#Y=ej{Q` zpq60ZlZwe}*IvBBMxvet$6VANM`cOr5J?+;k+zI7XUEwkVi&sH zE_exYUxo?{qLK6N$Hz8+%py9ExTV!#nX_;;d7}_K-H?cAVgR;3hkY~jeDTa3X?d0# zX66D(=5MZvXim58jn%X=2?>uvq{huQ#1iPs!gsC4lrA41tup$dwwUgho<S}l`-9&cUdp(3kq*fyQ`aDz<)kL#__+xTbcez=l8#jEdCp! z`DNSrUs%)skDfCYdIG>u0AHWa&%eHa0U(+FWrY8Pmj35>0TVk11O4C3g)eza29Ey# z9*n73+95BZ^0}(bR2c{Eqc?{Ip+@3HRLb8%93T=~4KFQ_Ea`=PJ62DyHu&COh>3+~ zoC(xlAybA@9LoYSn+!rhrj{z-%K%$w{~P99-Xx)(62?i9%Q`JCO4sHWb~ggrm(yNsUVXe)Vk1znR1a;+Cp zgZKH%fZN8a`z5k5slKPT&fBo6bDAZT0Y6yO0_=EY6 zAI1OC4XJ@W!McI=dBpk)!@=5gJ4eWl?!@|v@5ayP3Buf!hI4au$V5!1pKQO>htAY< zZ)I|Q!p_ZmGIMscR<3DAvP$@ zV(h^kvTj!po&^>GMQQJv37b3A1XifGOkxqPDpgFE5N>(#E1?>p7k7wWwefKpF{`;j z$Ma}Zm>pry_FiA`$AOEN?t~X_z)q40&BdYd(U0e&WqCnGyq6b#7*}V)z1N;9ZG@cQ zfmP*eJ0mu5(sBCaw9`$bwteFO(m<%&t5kc<%FzWo)?2D;pIBR@z1rBuCOm)6pdV#C zYy;uGLAGDf4QaDz4+MANz;2fqD8WPT*zbKimpm)$l~1pW z$Ub)>+2+G@r+tSfiPuR=+e*eMk<|XSINZTGwX!80PDxJ@`xANtqYbwe%~4a@LpNuH z6MTge4F5u>6dArHG785iA_9ica$Pz2BOC@slp%^fYX2!LBHHv)2UI#Qin=O{Zv&Yr z53JMlBxe{=$M9WN0q2Nw} zk$I?6BE5zjg`mAW)KT6y)GB&E0&Pn$VMtACh`uxzOD!Ul+h-3sZ+c_Y2qWJ(wQ&{br|z+Zo3}pC z)4pd2*|I|eMLc$4W?;*b<3?RN3a63E;M3Zk0YZ<{{S)musJ$wN-sxH=%%5#Ec0~)M z<5Og@&tW}bUwITQ136%)`nQAE0Ap)kOUUFGh0xC_uh9}L_Eb^_G)Y+vl*o$GFQc90q5;f#HhzL1A&;H-Bi&tdjel=O;D z)lx|3O^2#ob@(*;o4QC*qQYrZEB+jl4;*_hS(>OwY?_mACk`n7 zJ$7F_m3oMHh(J7{7bxtP$|ui!@uSH{2Y}Q^x7T@QehKh z5*i!0)yEn*?MGp$Y0KxUF4oh+AX{A$cQV$!VHcOWMO(6RE|#*vur5(Ehd9Z6s5mj) zBvwQD49+qhR)1wUStEf{HdX{SYDCg)rIV^?SEYbI1 zrDmrY%+y~-F@ZO?+Lqnssns+-%%&P~-Kq5zPp2-_($E8(`VUj2MDGt>=SQEdE9%6{ z6%qLk6j93M(W_2%JJkk6gx^p!84(G#@}8KwI6c z`de7k>o|Uuua@?n>ocd5*(1RAXd>cS4Lj3CZW8zrRJmLQR6+N^5}U8slOP^ht;Lc@ z3G_@jo0DdTvX7Bm5iE=MNA;)&_@3cio->W3c_6QlLJxi;o8};loTBI_^-m}Dz%9Jr z+!E}E*;hF6T+N02Q3@7Zah+nUIdSsJPO0=8C$%4Crig7(8FrF@J$p$6ITrDjqzllL zN$y26nS;*-+IjPw538t>66nJ+_@!7nCK%eFYc#&-KNBRKQVHPdnJ>9D`kaZ3pM;i< z3eDe1v`tso?4@Hl#8B?Ef zmVaTstxJoBN9|<5Q$W8i$FetMdq36Oc_rBWXrBvu%-hi)!w<>?tQAl#ieqM# zJ6FGyNF7~~yvJ(7>o^0*Aw|w1&5Q0n{k>HJPmd!7VQPVjC5;;_M1ZB*(gJ*c$~Nd! zZ=z1y3F}Tv*P0x8vLU|VP+Pf0%T~xZLD5?NMOlG`tbbmFvnviOEW@vUWR1*FE{dOpl2L;v(Fif}{0u{{liJ+mt z=Bb4b{2e;T8vFy7)_8lCJt`NWk1c&@_JduE=TA?4dDYW0|&2T(~Zv5jBhw{aB!7oIqC z9RV7jDsh^i?-gu3MdH?4V$6$6DKshGLSzQwH3I?L-JmYRgEoVH+Q9=KSZSfLN>;QN zXeH0aaph&&MBxs2#&(^x4zgwgdu;kBn;=%0FH*F%-oFp%uw6v~=6e$Zun}mbf1kWy zU)Md58#937tMJo4PfUZjUY?E&-6jpcJV>$QzOlAo4cV{-0i$|H9_| z4WaxrP5n27^7mc+9}r4rroSMR^nXVvnSS$vzY$8hPk!=~yrgGjX2<{hslV})dX}aZ zu4JF4V#a?UME0L{WS`91A5A|GecNm?ho6lZpR?vt(uXD`)vhd;Xg$5%@RG@;^rM4}9fk z$1nU(DyNJ@sk`&4NB zL8tx$CCBgwMfabQQVfjjeYJ$>+AX*@6UH0>fEvcfg?fcTLPumuHB3q<9SI&%5kuvlufoEhd$ z1390h+nAX`lveV0hOv=q>p9!z475*IoknvgI09T>nulqukS53K3h!j2GTVahF~ECD-4FA*VV@qQM$^Lt}RUz1Hb ztsFntUSDS<-S877Yon9Qto1`8620_~V(vrWFZP&)|EpT8QMMlXr^o*<(=q=(dX@3N zXJp6tCm8!bVPyX2Y5Er!+5G{4GUNXZ^ZcKOIe+iiA7}6L(EesjKcD{J|I5EaoWIxq zc0B+04om$V_1~iW-v@F2K575?BhF6$56}3i%1|iL5Muj*N-w-2nTIntVst_R`J^-< zKRCfYK0U0yo9=V&M$(ghn7fNn(&4mlaA9jMp-0W$Qk}7N)XYWYbCIRo$j*U+Cd(r_ zy74?po>sbs!8co15AL_Shn2#M6{FR2G#6zj-V-IRwZ_hg4lg?|c=IQdjm_0-gcJC39u0KGxA4SxTF8#sx3r}xngOR0w~dEW z2Cs{G)+l$`D>=>SPWj#Nmf5<}zUA{iJcDEE%a?MNJr&wc4i|HEs#Tiy>z#l=Jo?KT zw!`VvSheec{k+y`^~mTM1RjVC)2gzI4$kAB6-6g5=2Z5MEGxEF#z8eOj2Pp5hCjE)2v+MJlcKq|{R zfr+B615QhsnVrdDnj4q&FZvEvc5YY5RTLy7#oV6U>}dEbpRN~ZUz4uNLiVIJJY3}F zKfXVq!YLL=DUr`O&6nO5CU$xUSU+tv(H8v#sa&xm2X}c7$ej50>D}v6=Q!ayHc`ae z)%j647R^`7uAH_ta=eA}dI7vrnG5!8+ehQUKjSV)fL{m!HT!^89uc4ajphLd-D5h+c6jRqjg+s z_p1O?DWW6_4}!7W>a7V_(hE-0*H?Nvw(29njnK@(FwHnJdur5cjbGMLf7*T&L+oaq zVo@F~_`1H@rxILkPk6IgB2;=)?^!5J%#ZA>Y*KT~ z`|6Q$(n>6yblI!y%S=YxsNUg=f1Ge!$GvvKe`c3mk5z6by>=$SdY$moM^T_1V!eUV z-j1n21k7E-ESpR<`Rvo699JaCBC(xt}f*G(BEKA?vgz9_U3iggG(4wX`qWDtyKY6h*A~8u*J( zLl(s=mK5p7xm~y)pDr`~i=1U3bEQ@I!h7SDuI(V~OW7^$&kH>O=O7O$d>k4UXM(&} zK6TCswhcHONeA?U)UN$W)(~Eba00i??yV$raqCcj!lABYaC1t~c%oFC1Vd2*+?>${ z{HR@v#^nqSTKXAXp)Z`fd3Y60niHsCE{t)8Bw|4zDPPg`(E?L2*6kJ$L7?z*mZB|w z&gE&*6q6>3U;Jtm1CwK#3<$3f36a8wVc3J9!ww~<)FCoJ!y>$ zUPP^>ovtF%?rjgZ&gWybh2T<#QNuau%SE^0hjgI4W?K2IMIvr?b%Q30=9)`pGMQuM zU|`u%9~hqL*e(Y+HrFsF*RdnXID1_vB&`_e;?y>SxOD)N!&BNpp{3-|+}qv;mtor# zsb8|~z&aqY2uf#2P8m)ao(&vBA!wd8NQa=dFim5~_vQoJG!b%S+)BE1V`u^&;6{pj zLbMiBaG>V}VeG(CNe2>z-ba16F*;_$6-PKR@h@p$n<<0$IbrfcVhvZmH%1fTO=mwL zopB9;e3MC_NO9SWJQ9IRJU4a19QuL%HW>BRsm2sYTypj&4SEG8>Ym9BFT4yyZKT z*ufzL3X{E;7PYs1>?(W;nz*(?Je{1mEo{$DaYf|6#b0u|2u3(un6vO+)FcN>r{Nr1 z!Py&;w>F5Jw1#7$5~S3XxG%(BXm1SA%#w$p1#1G0vZyAkOn8Ai;coaJ#840 zpsbl7oH{iQ@N`F;u8axrMA1n?xf17~V^RhPsYH@0RPdGKSTLwwFl->iVL1C~MFI=G zQt>UC4xYQQG6B6)acc%GLFI$)>F|VZeGtT+>gJNAbOsW|C={tvyrz8((@1|hVc7kb z;?S_$U@OA0{-bV9Z>}y5hfB%N4!gFsIKs?XvQ+jdKk5PB7&)Z+ea4&@`Nmmzo`SYn z#8@Nb2vKwPUI+-@6rI~eb`pxl0}@V6M$#>-R7~~;+GJcZLicHHrnAus8UJ>tYe`PW z8>fZAME1qxNoGN)-s4`8w70`6r-fLL#oU%CSlqxFBBTzHqipXR*tG)ORs6bEGGJzNo{?CzR2V1s|fL=`^9G#**8 ztYS10$}l)z2^uaIY8?K$;u8&yU4GWFXw-dKH*A)S5yaz8#?i702PG^-zMy_r0qU1g z01k*x0LWqKDkY0%X?fH|gd4G6oI-g1Jwuj!|C_@-rf+6z@oXTpwD#jshrQI8QroF*a;^$z>Nm9SITEMl<<}d}xONO@YYnO_V>Vd(JsH(S7xPR^ zdDKRvFJTiCwOAUHJ!9&t_vi|c3WWG;>C4w$vixp6Gx=EZ!l8;oT^Y~=zKE!`$U9(^ zn&n29egg??ZIsV;Lsfza=2xbHE^A~6YKVO*>b|qOpMRy&p|6P9@=N9Ol5q4C)asc9C6Gfw#xEZ0(spBK5?Qa= zxCSeBnw)OR9+n7YaYFVqp`I+cZ$NztFsA+9HnlIFRN{HcXEhqf6|Bu#?RksD?hXT8E$PeMQuwj(@u@3v+i9(BE{J2gAtlves9`lOX_;nb zm489fLG=mO_7C*8aOPMHMW)TMf>#CTdD#x9lGBtH8XBMu+n?39P>BBlfb> zj>Qm8&S@~n{#nvwo7(gVS3{2!WM?7syjecYgX!>DKMgGDv}grCQzL)$I)19C->q@~B3qAtMc3fJKwJJj zLG`IEf2TY?cjb5MZ#BTABZuW%_+0|I*|7`>6e=;*yb>mG!R)q*IjALe$}XMS4H9O3cX_Gj!-)hs>-yxGcFb#0K+Aq*^@gLxdIgjLsT_uU0l(lA~k zb7JgIaq>wi-TV7joc)o2Y7lO>d4o%i zrwqFAJLPbC2=!hG6)F1)>i2#g9i6QAHz9WB3BlB}%R6#^e@wKeD%v|*bWzqTWKY+d z9~&Ptm$Y06Y-&DnN)uRJSQzj3s1Ju5RUa<7DwiCBkQp{_1?95It3zXty$;Z?1w6|t+(qd#3$XP--=m|C-Z0!NLMh``+DUpt15yF^MU#3Kw7l2b+^!k)q(E4w~ll`$APTX58l<-7oZuC zzLSq6%4fG^w(cwUV6k3+2PBJJhaZhnB*`xXjcTF@3Y#^>=gxl`(7%o`z`m4x7U3lh z!h}%+FdE?#=pu+$39UTJo`kpTyg+%;W@!6Hh+;!<_2s#;jb#+z{ZN>PqO*#9{&w(Q zg-&`g@lqdOxQ4XdP-v5VFz-q8BzEdH@&|2BR&3=HEh83XNdo$V#JApDguJly@nm-P zpbR|eRCaDh*IN9gvd)##o3B*extln`u+sKk=7|1N8YSdEIkt|psVK_)%;b~#on3bM zZZnhRl7FIuhEBl;c%&i>FyRPfR?pdPy~HME!Lp;PNPK)9t-v!tKs&s)oP}QWXzg>V z6f?Wym>GO>MQ}h7h&hwO*FwSoROoW1u;lsr$vV%RROcO2g7`q_rjdR0dVIV)yZ| z0rqnjZldietBtMsz}0nnagxXQG&}Ez4(993rND>GxYDyu91xUc9DEha?z)bN*iA+Q zE^Q8#maH|NUj2omDzO zbAb5mx>^_=RFI5vjnkr_pvVo!xJ!qe0|W3YBn$ys~S8}86xZI^D|ab0iWej(maShE7cqk zvUR#PhURJ|Zd4OJt1XM9I^qknkpuwSKSwx)h6pGDZrp~+_iE3UjMNfKH-;i%@?iqX zJb_RUN|vY`+pOW4;^H6(5 z);14vl(CH^gL9-O7bT37J2~fUtK-r{_9%6D$HVmy(!MLdT9m06i}dCLh=ZgOzt8&A zP-Q6D9$kN2F*ZkC&xQjk5Z<{t!*3D+m~kb zih;txmv`LIzJxI9Yj%;KNj&ri>u5dqvr4jaieAOSPdtg(dx4lT?MtdsxOovpHnUw? zn*jB4ddHA5d{c&!*uEp{u2($`-AX^TV0S6M|FfK@YQmIOZY2YinHx@+nygaSL{I@A zl+2m=-YnQyB}{s@IjlSgjUvd;6~?QNVug%*OPAT;p_Oj;f02%RbADE<-aG#}v>y10U0){p{ z*a2i*Lau62Fv{)DnG{mcoFlb>vwu}$nh(;ucjR@XyC~@csJEf7MIkVPN+huLpzg zH)4chn))uoEi$9`6(rTmW_++>MJW%`8PouCZk+S14hGj@x|H)a80@%UDXT(zhP?sR zb2U$ZW^XN9iF3JSVLnKo%|ZDtukybhVlR@jC$dKdfw|4EmilKFecN79i85{V? zyr%v&v38L2{L#HGb%S&zQeoUNt=#)- z*$n~zyBXi|e$Zp`bC5K#MM{>!actN@yF~1WGh6vrAACx7BDy?gH;J2&mMij6;5&qw zV?#-8_7KuSR13}hZiWk?BnH(si23A6Rh74^Cy-lHRs(A!!h?u4SP6v;$yGshkgMlG ztt3_TE=aT8IFxPC-4D{Q9|F_EK}z3#DPy*UUh`iz6p>Dv*gwG0!9L`zEv~jX4DV5w zXG?E&o+fcduJ9oU*=P8oN7H4!OCRcHd{Nbf*Zmc<@Z%z@S!6dDr9{hJ4;&Q(2m*Ex zGocl)tt`pie7sWGIFhiVe~i05?RpCR;p`1(;{NKsle1A}jyVC`z6onsV`d!Q%l50) z&FXU#>zdYi)BWs|2ljLFM8FrKg}i+xgm$+`_U5A-h(3WjS|;9laM3O3F}KN4hW1rv@9K}m23;rqHP7r%={5+NERf^V)Kg5t_QKO2HlLSQZ;)CdJu zV<=>34!G16UymY|itIxTs~d*gCx)#nir_;X7;DKtJ1+##2qHI>2vnN~8AHf+RgK0K zY6iR%3hs&mIw&$mB5#Pl5~BT10ieN%xYz)6nwauy9AE`nPNNJ`*PH;T4dj=V5CH{P zbvx2hLoR>@rJM>S2HwD5d}TOMfk; z|My%>|2w0Uexn!iw7F@1a9`rW-b;pS5*KeY<+J z8(1PEFm4WCg>7I1RFUDzP<4inzLLX)_PQlnFRs zr5q$NU<6+`C@sHBVlbPME6GVBHH7NMont+sc95McC}!9t*VlEXx4xeY2B>PWM;%B5b;%A=8uVm{2IYt{W}Z*MX<6kEB(9>zA`+jvZGBvXl(2I}@~ zlMEH#s;*woJmEZJ<3_=F2}tUpE-}6|#NSE?=m?SHD)>?w1sHxUkWv0slnwm!@dO;X zg~zE^El*#7I?9L%BOp&7vSZIc(OV+}#(b~CR-Sd~K4emHsQ&V!VO%cVJ)#tFhCF7P zV<(0ZO%IQ*VqcVLDb^*sH(bvFCBQ*Sy*ZuRl#D7nOg9I7JvOr2lom_~UnVbf6#ucN z($_*V7r(5F9+w?ae`#?9mrGtsTZy}Ycq%|N5Yh)L&Y_Cq(j=5x<7T3;7YQE8v|q5t zS2T!j2s7%u%TK+;%!FpbS+6FWk8mLJsLR)%d3bixLfm1CzyE8IWv_wzrOhp(ieDYU zBrd697tOgm*+=N;}NNW&zkmv1!r) zOB01by|Sgp1gS&rww6YMirB=$Ih~an_$UJIsm&|R#1%CQP^{|LgBkheph}`kd!s3T zU?FMQR+bB=`z4p>C_xt{s#-WifHMhGtatZ0GGKuvt?4LdD2-Y^d=!eI=b{1@gDKWD zNdu-5YDFhv^C{O^H>&6XO2;^UYr$4Dmah7tfe55lq11PcMr;D1=ynM?9*aC^Mol~4 zsK&354?P`=7HoDvdCwBUFPU3Ln4V3|?t95i)CIMAy!+j23F)Zxv^ga=)(o2YQQ)CM z%(G07_r?dRxo~iXtp`i9UD#OU8}I_skshqMm?KskJ3p+vWPT89IsTB@;Y4{hPzm`p z(SCKma>QE-`fPQBkXv&PmV83p#Ar5H3Gp@4eKnYK>-x9Dt=*Q?#0d}KZse=f0u*#q_E3Ws2@Xt(V<|z5;P7e}F z2k2)(3#NDEZRr=lU^3hE?a_AqsHN>|YHd}G1*&r@Ia`ER-WZA$+{Z;GEJfE9$N{2k z<45dzbXFCZp=NTY7qi&17q=3$-(gQFlc4pl8CQje*k*m0PKc#Qjd+>|B#= z$9l$zLjm(Q5eTuWDf3*7HA5xU?1;l3%0>>GBrq+>+ne;@lsOSVj`ab)x7m9dx~P#D z8giq#8Teh&GFgiFRwocfxl^u9R+^5 z%EI8+;Yi4UAKPgIw36u`t;jX6ze)uQIS84Jw#l%qdA0UMi6Y1Y@39tx z^1i#beVNK~c#kYZv{{jczJ?}E;liDf(nL&XE~o6|V@Mgg;Huh`8wsLy+F)tC@(z5o z-VBl)JUT{BhMZlC+Zo!cs}jbC5|q2I3Fr5DCCcb3l1m-R4bgh`M=IjtxomzMRYE^~ zcRnkK=wEF2H~q&aV>y9FFc`ueNYlLts2kY@rSpm{#sw}veBTj<=;WsF2d)g*OcT2RX4ASqFO~^I7Ror` z4SSB?g?-LXz_*#{jMywgRM@wP67dA@T~tGaj^{XFl>0>L>(lAtF+$Hj){dkZMD*Piu2q~7IF25kZ#hj-?N-S z-nSH5K$NbqHzFC*{DfDem5xRf^E4huM*H5OBQjC(v&kXX9q?1e!` z`!(nE#^)y7#roLM;#1vp`#94GW$mLtwdRVGrnsk39y9@pVrG^P)f1V7aVe-it;ftU zG`|Pi;EXn95aLjKn>Ic~a%2fF;51;;DX$FK*dM!)H|V?BApp8*!zNai6e^#m$XqDu}|9F#`I5^mF(9$|PJJZA#QK-@^MpccC*eu`>OmQXGr?+5+40LVgUYiFikgPq*pR2E5zI59|)%i_MY@ipB_i zs(t;OLnNQ8cs=S+NUFxPnpj2@%8NG?C0kX*yLQVv2ext&q_B$e-Zz%Q-DkpjaF2m+ zRh1><{ZS|XEKI&bW7WgFIHGo;Z#q)j`8IH(BWuRlQ@M8kT;;~2smQH=2728DjV}2K zYIoSZBi&z5^W?172H*L$P0WK9SE42M@NlAKn`Z#xS}w?8+HeW9L_x z?+-aAaB>EScEYsUS=^b5JH^`ME?>g{&y`@sj^!yWv@>2bG!&8G!&n`Qzn}<1%}SE* zAY`JhqO`SzR0W|7wa$+Mj#4?V3g?!gbo43py+N}Ax!AqgR9xlfXIzv8g%UlC20~MY z>AVs_SGLpUNF;`Hg=M7r$0h4?*zE4Me5+1OB^U0d4!`#uhNTF<&yDdgV66-!9)$}d zzkuxt#5QfjX|R#@icr4{`+Apy$Hi#omSBbdBiP1~R3+`zAj8oDimBk)7LhHovARf{ ziKek1e>IXr?zw9W{(LCr*rkf++_eV4D%r;uX`d^*8Y&zjc<&3RyrH6o42ue!FQbF5 zjcj8<^B~;C6922NzsSe~RbcjF1-~sUQNlN0p@HAgPd>aNPz0~;`Ocd!lw?8@9xNVA=1nWW&((HerjatnZD5EDi8 z8pu^grWcC4rbiGKC1vCA0guZlYkrsGut>)oRBZ(%*E721yO}^++b8aCF|k;ZjA9g3 zf92}UX=x#zby1qq4}FU7(FuOChu$J{9g?dw>^nr2;a*ie(R=v~ZjOY&AU~;#wGDZyxpYe8TQ*(~JXJM$uL+nv zkKJROv6JoEF zAx_emPVYRR=+#U`N2T`4mVgO)C@<7pt*2w`spGgbF~ooKSEnL&%9oF(JJCs90G2U9) z%9u5fV&~R!}m)c-F!U4MDn(WIo)LRL^1SEzrw2d;#k%OU;GjT)fMzE7aU=QWNyc%?@0cC{|bxHL4OZ3NPkh(kUILLKsrHesh z1UDXnD+ob}o#^CCgrRM0vRXb6%#dnN1fm%l-5ul6sS#>-iKjp4l%Fd z(DYH7a&+~ri$L^byIt+>!&@9xp{Hm7NYla%ia@oA#Kz1-jGPcv%AmAfuJ2`ie#fmmFbYifb8kNscl~of zj1VXRSKOT%4N!K0v;iJ|6+fYaUU5GXf^5KEi(9IxzCCwRDb%w0#Y>FU_GU8_-7&v# z&uPQU$q{S$7lNa+nIT{&fm`MGyP7xowqaf+o!JLotIln;;!2uKnL$de+qMNZgtyzH zd<03}13$8rf;CjLrcFe#lW=Pm!BBUg6L=B9)6X=-$X(D3 ziqqz$0b!F~A-7=_KFj(XWxsAB=_3sOrL6=ig5#4&76!OGOh2|qx42NWw@w{lE^Wy` z*Sk?&xkMZ6JBC@gT40)Tl)`J693M_-Nt;C1h*xzElc@)=ac)?y=^d1KoJ7EZ8bK@op2AjrOm~%bXe!72 zb~)f{+Tb2YW+2F~lK0PkGq8yZd#zO9Xr@-u?O-G?jh|R~uw}+5DeWriCmAi2~lVKG{qYEr3E?Sj7le!9HfmZG#`cGy4-XevU4 zb$p{A9(^sA@(`$_%zG*#@xEg<>eZ%XKi{Z$DjI%Xak_>mir)2Yo#_+i}x z(&*}DzAPrf`K(jCA;_e98Gg6X%FP*XA&*Z$g9CTwU>~h|VcFSS!Mbz<1Rv*n7L?(S zT{(ivd7O@lN?Y^At)U@*Na2%UGp`@hJBHg)Z+UNXZ1-u7?NgfH7$+g6K$Q3--5+TV zA!Gq?c_X={K$2qKp`w_kpW@_{H(-90E!^5Z-LFkUyfr(;jyo91`q7k8!<~S0a`K_) zZs6a!&_z1oxCh7u*HCmxtNPf@n7;YtX`L$`ZK#+QQCx(O4c3I~DN+#sRDvx;*e^20 z5>&-~&fb!H^gA-OTJnv&!oQt5AWGZGE`UYMJYbPMVu10teVX^pv~|`SdF#-D6T~Sh zwX}c+!b(*BfOXRir*wNP-L}LF2*vhqw>c8OfHH)jk%jQho*y<;Nru7AlY`5Pr!9%T z1k+0&uG1LSA0)gOyUyi5l{B>u3#VX*m=AB0ipGji_gH3-+3OfvZ8Qj_TO6Y*0TecX zHEU!^*jTHQz3fINuau)qod4p50VQg==he=Z9#d(^BK0WeS2ck*>*+?kE}eG|gvkFj zU1hoQ$=G9OgVYWo(Wzs~&YLgmH8tWD4l-Dql%*;!19_3{Qqn14E=6BahSgpz^7%N8 zkVU)1FWS6$YUJ3_QRACjyWe@kgGNiST0cyP&WO>SbdaLi^)n8coeD`g>hleWX&RH4 z7)}D3m7cI?Ri3b{`g9)}LI}o8e}y1l4vaw3gxWIn`@;=hz&D`n?pxLBJb>u$Ke9s_ zOEfBxc2Zs)bm0<@PTIknz{2$ci9h|HlK7QME#4^?XqR0Ne$eZ>?lS`Gq>kjFo`CO8 zL9wibjn!92YVJj?#ycO#ee+iRY11R8VB_vC&gSx@FPR-?Be{GHpzIO7)Bf~n;W%i- z-T&Mag~~;yu0PNr&VbUM~* z2<7tZ;lZFFSw(7|M4L$^FkS5`t=KYGrnj=x#ktq9lzP+d$Fb%V!Sm z(oG@O(djGY>KcJD5gBg&ADdb(MSE)#>K`Irkn$-kuw^;ztdG23UlhJ1Z`G5<%5|(5 z5mS*3uN*jz;m`iO#=uU5ylpcQ<4kJI=lB>p7{sx*MWew0D*#q|{+0+0*P zbCket&P}#O929=c(CcR|QS(*vqh7K1jh@(^iJ33v`*k8uW)N?_ zc_7Ivujjt6*&UuBBT7R2@mk3tCd0xehCVMR9Gr=ChBhRccJhO#Bld^ay1>v$hs*?D zSr$&|rS1t56C)!!UBbzv*FbO2#)}|XHtGYSv@o%sJdnG^@8;{ml9(Tl8Aa*U=bV;b zkeh_%HPIg$Vlt{$+vI}!OIeY(GmhOPPrJC^poin?6Rt_jDPQSZh>d6AID^RgcnKpPS0B@(XJvXLr&z%nE)RFMT?XTk$$M( z9Mq?IjbuGlJXlCP3)2GSsmE}FyfDwt@qR9QcdRkQ$e{+!E2&O>v(<( zrH%lllDrT`bx$gAqKNa&BMNm$nwlpLbV%~85bFlP0D!nF@BL?(^s4~wf2V2unUwhA zjbBAZe*uX9DsN=@YpThAkIeFS-uUgS@$0K)>91t-KjN(Xb(ZEYxe(0%*!cC|2p|8f z@~`lb<&T~6wWEJ5oqvOmf6o7phmU_&{ofBC|C5%=pUdw5$sU>58NbT@zTO6m^{ru{ z#KQ;FzDLru!g}ZE&iLkX?cb=O1_fkiU+`}rf!!eV@mekMFJb@c+Pf5MBGpJ(?{sx! zgG^~vQgRvj>Ez^e_woFYvHG&^;7~G!ztJ=Y^x33zm*TzJM>#b1W8LND?dIWdlWy!q z9SuL$Ex;5#=GFr-HjnpVq1)@@!Lg0?Sn;92^EMN9#JCPdsPWM(~n|MZ>sbdk5X6L%4pXyXWBcq9)&wl z`fFo-8)zrI=2sosYuV0A1nPTc6TLvq3KF#DQsCC^wjnp9qP8#-Hriemc@Q3iYwW)v zpaSChqfK@3ZNR_ZuHm^{kzP-ns##JE0pTCTgcZ9o0}nTDV$Lqf1>>}%RRu^+Rl6Tc zxy^HJ@@;-Rt6ErW%DL;x>24T|%j=hY+#7z5l|+8EXRUE}l)ipCy?|plHL8E7Rc&y# zKi!et^!0DNxh(1KOs2J4Q-tE|zm0wK3fcKfeO9E$SACY-EAZQ^?F$T~QBLy#Q>8Xi zN8qQ@MOV8uC{n}%y$3J(6R*vL>-Czq@8=nVUG(BsiX(tq%GO$Kf2${XQ-aKIRtHQ%gs0j;d{^keq##X0L0TK(yM>vBlDE)bYd35=*RQwm!S~p-CMMe>Ka~j85I9C1Hj&r!YBGvMNYOuN#VX zd^}SmeWegWI1@S4&t$u%Sf^{X5aF;IEjWwoFVnN>G_sVfiP3o*SpXL1{>t_l6HI*AmMbCSzS>Gl#d@|GfJFxG?eY3PaOR5vZ@@HBO zV`Zi;CTNDQwYJUy&kgE;#D(UnC;aE#Y7GR)hP+Cmuu^EBx>yAbao>wN01;a1?=UrX zDJ=ng=aAq+i*dE8{*t1@-q}Wf(0-Ru# zFhYsCZm1Q^VLT6lgTD4)M`C-MaFLCOgCN-FPG;ubky;4vC=~MtnPx*^^}cvZUr@z| zXGtp+Y7U*Dc}R;tV!@>H%S?f`#TsqH0C%%+}$uwbnK z8-4}G2+F`0mnU_8%$1NwR`aCj*ZP6Q45{quq#L_$@a{NBYaWH^{C8Yt(4@I;PYD9e zhQ@*O=1j9$6EKLs4|z|PRpJecZT4HBx>wH$I%;Q;pKMj6I)vTY1*XXD+R30Tl7&UW z+k6Mm+BD_!NkqNmG{g?|Pib*G7%BMFFwn!1`hF93!tlfOp2^(=%N~Xeidn7a7qYCI z)%fy#42k_qQi(0vcBv&NSbLY;6UNoLhL)y#acN2eSo|KJiRe;cZU0lJmHoZyv4~ua>ybI$bRVWr z_2ni*0gg_80NaKnsnRk$-)4*01CI4dfPg2lB7ec9ypd7l^#+yEbE_h}FYP)Ssn?0r zUgxs3lP{w>I>j8-SQe#WnN(Uy?SS-bt?CLbKq!h5UyKzpT?$RItOe*06lK)p5O`xz zfzn3v7-2OJ>nyg$Bed)M{YmPB zdOO;VVCyIvI;@{b6A49D*3LN(f5ImV%rZlX*y^uYabWn{s|3l>k!dCAd2>)OXBED?yS04v{STja4BG(LFIG{z-XKH!6-a9Aw!)?4WfPCF! zp+;kIx#t2Ku^D71ab9;DVNn$DXfmhY@m7m~#QQI*SjAb--upoS|9C7h0Ote~fJWvq z*QwZzQO+zb?-E#r1>HH&k>U>JdB57;H{&qdGPP82t$929$;_K#{);Wr`Kk;^QaGo= zYNlX1#MfI%@x|rv;*($^cJdQhGMosSNi$pgdyme93DbhpQ_P^4z^AYD(Zd$!DhwM@ zbLaw0VBH?gEQWnVY`hvnqBk2Ow8{k?@ZbzJVj`3CdUDUbZWFBV*vDi>C;D=lX|nhV zxbb4t1fXD!$%}$;7se!;sx-Rb*F|R$m&94}D-dA_hIAQ)qyd|rigCXc)~#_(6+q|v z{q~}3>y98&h%9%W(xC8;wA7S_Yf->cB%#Tyj2OW|i4mHwKf{+TcIBh+MyoTazlGTG z&8PF@;h&nsai1S6Mnb)BT~isEZ2Ce3l_Zs~@qRfKca6~05+x{MW?%`tVB@ibDN1< zwR*hFdrGs%j?Y@i84a6S%>CybV+N5*dT=IQyNg?qG@&rXHxR*ICMtoDtk_HCZ=^24 z1(U}}H|1rhr6(96BjnGbX}ZDG3$KO1^-$QSLx;mPj5Mw5wLuODjZNbq=qW2|4LOIN zAvz=y-?lLP=<*cggUOX?zyQkoc5ntXa^$RZ`vSvJa%91~8KZYdq`2|ke8d^_*Qx`n z;}kj@e3}5P0S=_UH`g{0Xq(v!EFHytOK+&BhbP2ui<|alIV@vd-4Q%SS^zUImp}&U z*#CvZWEb}`!j9h5jsUhMtl|P`)+jj>5Y|j+Zeerw{lPT;S{;QE4^X?15?rjHt)^ow zO}mcYIitv`8HLNNk``8AoE{cgPTPLxj1ypCUPYs>yRE)|7)KnnBJJ2%f6dZ8%sC}Bwhj&F+vA~2V0Itkk`uvjl=dwr9Y+};{B1{LM;ZC0K z35jg@KVbOpTr6$-MD<&8Mi?c>OdPKeXcotLwKH&MVNGG;*L*USBMboQ3=4jZth8d^ z0ixcr59RvRU%new1(!U`=6Z}R;f7gH&D)oVlyKWfD76`xbu#~&aSzaLgxa5pRWZZ5 zK)hS*v~1S`qrEr}_b$z-Xmo#o<$4SEZ zMM}73OnJ4R7=Th?X`EgNsyf>F&&TN>REY$umFDbND)y!tiRD2{OC%UF-SM+2jnu$e z;>E@jVm&rahQ4WTg(HH9Q3OdACia2a6=DK3JXzO)v@)0y*AKQBTfIqdx4UmfH%C?Tn2y5}LLp&X|Ka z4@}KmAcO2=hMRNbkI4!LsBW9w>HVtbK~ZXQ4+}} zbU?JcbaG5#9_s^kyTOJ}6rpbkY)%#tEz;TaP(mvms|E|~PDqcaqMtEcRlyqP$0()r zc&%`)ev3&Lv%=JI0~1AE(QvkdmAraPe}JKE+)8dqCZf#(sGZe4O6W5$Ir#$@5Mx_f-7F@=eJxQXsi!R49ePkpJ3`x5El=SigobQj zZGi{%Eka{He6=}@OJ6+`Rksua?);-@Nw!V8D0dyB({FWI@>o^_k7De-2QJ6r8;V5G zb!!GO6eRhkZt}b=P2^%qJ6@{UZQ!RP$m#n;SW~jVlHzWzd8-ndAiPCMvA$86r+UAe z_{QpD%vfEDL$NEOBRw%BC*9$NMlY@9qH%+6q_DZ|-VqOW%{*Xvw-Jt%jsxeXMMO@( z(hf+*v}A_igPqHnsf~VbR37&@vo1aI{cyiiQYAR01g$5HcZ%YQOajYAk9({`nb~*p zl)%z*(e&>7YWU=!Pk zxzyAv2JAaNj7oCpi6I!ufo2Y0@i+1LzIrVxF3SP_Ded`t#>kiO^uKA(pE;g?o%Z~H zVVC{?iT3r&OFH6eLtZrl<&^oc!EJgqN5ZHLdoga_NK3zq;3+UjWcpg zZ*{#+TOMsc>ub&_AG}4n{)|oAhu~{}xmjc*_1-33<&V#79vP1AkRs_7@&+xi_>b+A zXU!Pe>(Q)OCN-F!8ym}@Rwoo(CBrhV0OJ;3Jx+NVHCE-?ZJ!Rk=mHs5N z_)%m{e9HF~Q^O`ahzi5G@QE$^J1#w})v{DWf(8eb^po^s@46JE+h>ob_c%|gB1#gT zPMl2WCH=0q`?7cAHv@y`zDQU6(HH!Z&1r%pli=A(D@(vCy_aXu!<(tY*w7A;Sf$oH z;26Gz>!=A_={|Z3j6)WDIKHi^<~vMeb?=xxrb!yS?(I)r=1oEi5<<1NgzGc=?#Mk) zHk&TK@7Q4c4PSvRwOhTrt*qOvWr$jn#e}LOoqeBcqYG;8hPws_vKXyOo}ED;HjErz zw)-%ymC4>oP&|u2gc7a>d|JC&^y&% z{GD5+DPfEO<0?Y}-UJ9iu9~TzR473+5Jz7E6jhl&($0=dd9GU|shhCqC@bKkmpGg@ zEupWJm{VR<64?^}cc^p!ZX*+>j=38Tl^WrnJMLrSCM&ZxzHX{^K)jT;H0|1TDrhaH zLm;w}ek(7h72OT5(f0fGw!+~X$LTV4a8!paHqcJN_S1tU`ll@53Z-HOuFy} z#dMfkw?75NdTsfg4=qE@*EjX~2)aVh6f}H9w)z|C=IET6!p5H+cb}< zjFeeHXYy77Eb?6O2ibeB(Y`@+X;HaZPgKiJ3<(UaSie{AZ^y8xGy@Glj31eEkKF5$ z=V&+1jJDzFbCU6owbnbTah+Jm4>*=LHbhE6X`F~l)%4msn@#eifIU|V2gJV~NZ*o~ z8Pi43!R-oaCP>yXUi%>uEfq5F(Kk}al=Fktl#$0q^jLmnQuIcY z&O%-Si5F&?v0Kivh7Y#Z4H2K$5*uD45fQu$JAqpfS*s@~oto#0g&cK2BUaemDJ@#N74 z4_z*gpl(Lh*bj6Si%kCpa}zV^1(P_QlVIq}*Q z`i8Ll$>W__+oIdEg_o6A{Z^)c}?E^oaGmR($pkYHQ+Tu14fkrwl5|0=x=EO=bt% zijhLHWY1+%pAjYlgj}aH*%3Ac3dI&){)-JBZSGn)ya&hvbBf6Ut znrUL{vgvx5{HR51 znfErob1)J+zI|7O>BitC61AI8HHfV~|JkuVUjv0Rx(FwJ;bJ-sKfH?7OZJl}!iY7Q z8ah~WTG`pxZ?0=FLu^#{cX`|Uf&J`HQrUiMjF{9OVzo1Zfg_H9>Lupw9R%w1k_Z`9 zyKy<=io+_O;}!8F9B|qsk?6hPpHLTR4StX|C5KehLiTc`gSj{Ajs?dveA^qy#mEH)Mk*I>w$$ri z9}A5mBo0Xh&M0GR$XjmeAi*s!+`iLlMinG3@T=axZIpdJnHPPASrD|gGZfU6UcFkf zeDWe}T3+vYO`h@sU5(qg2)wts(*M1hk@(mdTLXo9Z&?GiJ-1lOVQQX{y*QNiBU7z0 z`7`L!-vvIb?i&U$8-!1SqnpyV%|!39w+Y64TAB;ni`sS1zM1<(HjVQ>q)nZPY)K1O zHLwT8NL&9DxD*^G^3&@sGu1XJS$Nk>3S4@0&(AQ-q@Igw18>Pn(3gyJ?oyT4>xkRGXz9NS7EP+!I8QxF{-1Cm3gj&H(Yhu{;0tH zi0aoeZ{xFGq6n;O%^}c35*W+;r~K8uU>G*#p~llM-K#`VOLTT@d-ip)QAC^;KL7>Y zzh~8iZpMQN&CZSW8j_Qbdw-lNy+fl)eke5pPJFVzhj{u!mCVZs=J`L~?nDP1AEGD4 zyS${bnXE3UQ0`nM!k9+yL6xbW4ZDwo_QOBjI*HsqAKngE3j!agU~;7$IRyV!6Vu5Y^p=*Kf zEV7&~^#UMulb36xI8;*785<$w*i(A-lvRqs-=$)&3-x_U(oPw6mYU;+7N)8lb1Za6 zUqw4X<`H{wdoSIa(Z^NUK>%kx~ff`4rV1z=W>WNka4)8b&S;WwnAfx30N>Y zg=Wv5{lW0KzjDx*Ohiy4e!xlHsEM4eYggSoiFkTz5W8W=MtPZrzkc3e(@2ad8LO)e zG88#f2*KkfK&AFpxVfK&R)=I~Nb@cJUOMZ8IbzBHKO=I2gQi3@zGy7G;MWh*YMOd% z)EsF7uq-Dbl%Mt$R<0q7XQ?H!i`hn|`?5)(Db#|Isk z$t6;8mCp5mw{Hy`$kUL9SG5P@w*mm=8ZnBrv(L+;Eo3sF+G!p*@+DnSNIAZq&C_IU zMh!FXUJ&tADi(CAQB-jt>Sm4m_XJB1_XM4~?=$)nYG5P`U+{f1|pO~2O;HA2cbq^tJ$qt_Y> zBe@4XPOAZ;A|v36S-YIStj#7J7u|ggVsQ2qcl)yz;pY5wnCbZ`>FBNA=d#X+`C-fS z{er>Qi*)alX$ub>(GT)FK#3EsKNi03jNxf7#n*85j+3I zD>NVD6?v^Fpq#pS(2P&hk7qZp4>z}=sc7O^G(^O63Zi4-q=o{V<*mG{%4G%N9|-Yt zToK~^pCH{ieiR^ZW@6unB2`z_Vxoj@hjDwu3Q>BcoGFQD->tki!=k`rXtXzch=`^Y zlp~YQ#pPiR$6f~ts?cXK+OXmT1d@N(-)AWm0$A0cV|r$we1|m4Ksg3r{b^D+$0{+? z=+`9i^&QO10})TP`Fr;lu~`UHS^Q_jhV`HLIjsLNKZo_NQM~`Xe$IbD$@zy7KxP7_ zfAe$x8qs74bHzkMlkCH>c!1~8t`ZqP_&;9@7Va=b_|8153^I*-NJ^fFu zPK=F?k(HH$ot1@>{?8000~-e&I|B;=<5yxd3mX#wBLfp12g}!Ob|$)S98CYp4CSBe z|8J}L_t6paH;(@!&gKs~YDM(E{6a?*Rf4Ym5=Yh5j87BAz3`FC;2D_TxWFzWeK!#! zAc`2ND~c`KhIDk*J){y71npxTbvCkn*xPc>K;~9UN*FcXYNTf}G$m6Fm`R1wHoCaq zpRg_0mo@PZ*|)L!yzETWLz=W*KJOmd$+>WQTG%_^gD(%4Z;}yAayL^C2|jsAHX_U? zi>*V@lUC7!KdX%5vKi8PQf~>2rU)^*{nREnJ~ zL(W#t*Ec`C+mAdy85Gj=h4Sa(-S~T)iyi;88?X+o3d9kU37H*achNq)i2#l(pQho zrBd-u>p?a9(4{ta?{IV-JZqZe3+A9L ziz?NJ`k1!5VlHp@7N)XGE`H;ddRH`&`} zy2w0>zw-y2XD_d=xt7V9+Sh%xT$Ps`=>k|&F+#tT z?92NG<)04;VF)ew%pif2nspx-K8PKLGzbQ19Te%of&c9}Lzc+$%EFJ(nUBLnJ zvsoza{U}#2mKr_~jqB=hW^T#BsV^9jCo5ZO;sT;zmy16z*izP0y&&R$LT; zm~X9=Ro#q`2mmEkOsAglYJ3dTw2DPNl>2}YC#GAb83Hwz9Y9NmUWtBb$5{k{#MIWq zGdN7d6I@Q0EaEsLCDYe~#9@En zx1vduSEra%!3ogej(I$nk_~o}nBz6At$rN99~P{y714oATNP?Zl2bpgq|?&Cs+h5jw({U;)4C9(H7g6(m2YTs{h^q8cw z156La+=$Y+8Ad+xX(?;gQ1Q>eK$O4$LN^yc1limr2y`}% zK`s^iqafHxbItR4&1&WAjaZ5$JAdVj`FmQ1h+3<2iemj7uxl4ROZqJ$BRLI4=3BF| z8=s~O3s?q!%PYD$AU~#ZNi06q6D|G2)q_vn2&q@sg?5ycP5nu|-Rm3Oce!%E2C90E zpB=v|J2Y@jO_x2T9VnzV5G1z?$pCqCJNh1{b3d9N=z6 zWz1C_BHZ22Di$deUHm;JAQ)%Jf(@b{t1SRP-mk1Yi=7h(N_IA;zA$Y%+lq`wAt`Kz zig}@xQNfJlYe2LMih6Aca%nINc0I8p8;k1dQ`%DRUCHiN$xdDg0xuIpqLIx9zOuXN zL?WBKa=mm#Z65C&)`Y$mp9mT5$6f8{r%5NvVCn!t#Pg(P98EID+JXogzco@w1B{hk zR9D!tdJT0y^(KiyhWgd&_fidRW3+B3*{_$UZ+$E&1aBMxp&}Vl$b2Tud=}s-u&a~h zk>yf`o-{<7cKvk5`TGRhod3zl!L+f?@#_O;jUEr`VEzBq9K8O1446KVMa*L9;U^zO0NfQcv?iTH!Nu{O>b{=GEyD((tgDyUhEVSK{U%ao; zA<21ZjG&Y?<*2GSc$H6_RfH$5q33U(*Y&yU#qS2viRANA%^dBuxuWsSd>%>(x)SVJ zTDxr>cAN^s`LHzZazc*E8L+V01#2{oS)#kzAGtroSJ5E(;BceT=VQ<>H~93hP^87B zOOqTf=T&hGerK&nHuLIS&XdgRTd3@rvMKKRQdMo4Xj-%V8W6|Ez|3^$egKIGQ>gRQ z%eXG`0d|U8Gr#IhD3J*H3s0!` zAptcu+x5DXlpD(eyztPQ9}1e=k%{60pjW6rfk6gg%K=u(nm?m29o9lAjo6L}KES59 zor?jeK%D}k-;5+YU2B)=lwCgT8qHZ_@wb5Inxp^mFXv(4PoOo)DSzJrvHCyo7>yZO4C1v+h!RWnYgKOLWv7pg>0?D z33<^xh%ru)FFwsbdYJhvbS{eATapikJ7Fu%wz}CT;t7&Y!U<@Y_o&jT-(0Y8KoeCWm znOu|y>6(&liV^c3BKwX7m~%$Mtcbga!NFvvBFGj(`JJGNy#eh2JR9+?s*IA ztQ3$ySAPz$iD5#`vTsBAb>j8fHziE?_x>jk^tsu&=2DZtIN6E6<6v1Uj zCY8bk$_*vYKQ|oIR%WS>v}<=GONAx!?MdjBTd6Ff)>Nun7~4A*+i_SF-xqp2Z`iHt z#^TWRB$q?Ze4f`vS%W!h7=sxpvOIz0!!m`miG@m9>ru(t;|NrasC^D@kx>%kvdyMS za)YW(G9YOusL?Vtq-8%zu@JxADQsstjpQ=jl4l_ ztIa|A5;ct|_U&0QPHK^Bve=sIOLW>3%%*)e_QZidVX(&_^tjd;7b=8=vq(+pI@p(5 z+Y9K*_!#kOJZp&_=y~-TeCvTQ=zcXI*fqL<23dkv*ZcB`YCnA?>Lgs6X;y#fSudsb zH_(Ld6sXhqM8#g|`6vzGz73%e;1?EPd&PBIq_|jKNbN>|Wtn7TU9X^JR@rXqeh39S zI@j>&BCV7XNqOBD=nC}T>~q37d`|uLy{40h&(??Zsh9GLV8z)v<_dt=;Gu zQ25dmKL-VSQQwWhIT*011U)|4jqVkjHrwWQIZ9_nh0IxwM3hS~Xp&2d3mj6cq<2Kn zYORVhisEjeJ1(g%rh4}_Qt9TFWD)&Z$!^|@dy?kZ5zM=v7yU7@YwLQP08sg)csp3> zwl+0O=J94E3nZW&nBchYuT#nX32lySkp;W!0(w z)#?yvmKZ4YP;yJ=rG4wBNh6|~z(MI5HugReO~8~?PSc?7?aiW9gw*Y#Hk-h)2dg88wn&0P1K6=o%l3QPQjrL`KT+LR(MkxtnJxWFbql zRsPrTN3g@GHg`P6cGh>9cgZ~-sz^wDrm{6X2VyO>f>Kvkrpc7p?fC0zp2oS+%<)2e zY9uI16t@p_g(atxe*f9g6uJ+Jt~kajFMS)5%EC7v-2jSmhp4y(n)I#D8c;?RomOF! zD;)M$bS1XtQH&gXB$7Ced6%j*rNX|9G-U#)SFI)*hmGa27;WO>EX~b)bjiJMilS}B za*0es-Pb#uH=#9jlr>O4DsaTNY@xsau-{CrWA|GPxK3qfFA0Z*%>Hu2GRCA7NsSs} z9|2RqSTMlo1>6ovsL>!0_;r-`c`MH|J0!UItBz5g{17ly=^ecUT)%}lSiKy@4fZ85 z*qxMxcLKP;SK6La#0f)+DR*Uj9xqLl&%A-9*x_^m#=Sf`u zo*atH??+lq7|5TzJW30K;o?Y(ou*|gETMQxjaxvPRYSvS6WlkbjtJ$C;oK3Rcr(j7 zo^fKlw5^Ihc(J7>{BPX&0-bbad+YaEJbm9n#rD4*rJv^(PKdt~AL?;^?7M%I0ssua zBH(KEPf6V0MW26gr&#|OKmW2j#rD^f=>G~5_t%-be~f+p`=s1|n#BDz-T9veI)9y1 zv~|*VGA8(H8h0?a{&yR_l>gGbB(HC3{11goe>Uka{N)S2{V}7ypxYmb>>r@pKe$K# zb5ooDtolENwPYB+(Q(jmFfy^T(n;$(etGz&#$R8{*f`O#{f%?6d}V`u`-&}NWv2U2 z8d85QuKzdYVqj$d$9cG~UMm~YVc4%;s~7M#rAk*oQQ+By&d4($h+hT>Mu`1@Kym=D zpXh{nSp)C||U#-bae`!r#HSlZc z@bZ4T2~>Q^PqJ^c-rK0Kz2KmW6@c>U&v*cabrFh{)|_}xXn`R4KD zIsVpj=|<=^$mLdK58unPkq&^5E_;t0GUw`X^lXs+_ZtxJCCgWgRp=w>4#TX&GA`pr z09KT{k=9vVlV{8@1}~^@O>U2Em+#3{H6qR0hS>fguuMBm&oEA|R0fMnUMP~GGu?tM zmZB_i)U&`2_)|7MOtnSeUlggz9kWUQKC_TQ#g#W59d`pEeaA`ZkQgGe91JKf-oGeP zgd%wezalF}&`UalT5m)Ib-kjweT#T7>+kuOvQlLYz+!hd0q2%v2XTN4L@JpVSms_0 z=s6B<$NK5vm6a%I36J+r104aW#}B)GYeQ&O^^$ZHgnac{m4tS0cV8Yek=4H7ESq}$bngiL ze4X|REma88eyGr{!RvO7_r9#oxRj%Z1=iE(*98!FyK<|RakH`L6J2Jmq*A87*>%y^ zU5FkLnU%$8{IaBiL2MXMx|;W49@W#{)iGYwrQk&R>R%AMgKE_kifDj1D9)%=A{!T+ zex{dQjwqF+C7MMEZo!0|rhiw9Y#NK;v#@8d0wZejGiv@dI|X`sgYOl~$MmK#T0Fa> zu#prx$feAfm@6#pqd~{+I@D(HJo}1u)VB)5UzHMlk$%fGv?KJ(m(n|b7D}-+rlvGo zg(2i6Y2RyMr>T zLS>r=QcLCCG6tuMH&j=m<7)QLcPlD(`&ylb`oNNL6YQ9d4#p@%p=dT=AX6{;Pz0sP zP6_P&D3#wJLH$og&}U@sCwyL;>ZJJ9^43XPIMf{Jj;uuXO}WksH;y^f<4q}TeXj=t z0nmaUYh|qHu&5{=x+`@Zf*63<`RX?`0N0cioZPJuVli`a1o}Y1$e_jYNCk$F<-P%` zaTj;NmlAVPuENl3IN+jHwB$bK{JGD}IV9E2PDITo=W{;ixLE@~9Egh7oPJ;#QW;%Q zq!9;3wcao^Z>2Y%0rsP}QS2em|0q)V@f4~UHE4;ZL_J!Vrjusi)D)v7?IZ4HlIb#e zs7C+fD2f>)k}xrNrh!d|prGgA&o8%u-%x|Ei4pPVN?iw_s*qX2Koj@PgfE zpL5G7+^9r-s*kyEzQ>(-qyhG>V+DR=+GQmi6<-0{tdIkxL>;8Wa@8Yx|83EC;WPes z{W3Abg(1FV55r;=_=AX^-8z8dcMviXHFsJEFTl1MubjX_s;U_ngG1~JdyrbGG)ANP z9tA(Kg?ghQyYGI2g1f@CpoPw_J`0XLMb<3e&1R#P{T{Gd{GZ&h@Pp$!5O>Bu5Pln| zl1a9)D~AU9zXk}?OIAznA=?Ty#=_y|s>-0Pj|O)YFaz1!H4Wz;_TIwM-N|%DSfK5w zy8VEzDU}r)q$}l&cN&^_R=%5o5^`YdH;I=8c422r*Bsfxnlsl(BrroVpIHPPP-%Hm z{p~*=yn7VUDmj#N9UzvGa&(96#2sIL2A&Eir)!@&Q1Io_K0cI?>!^3f2s>I18~ zF7oSEklHV5=LIhcO)$qTl_I{k6>lVRN7e4$uV2_!Bb=VDtjZ$aSQAt?ty;reP&7e} zA8RR_2Yb=Cku^N9x1ejKT~T+Rhn`=UJ|kaG(0D48ROBkcX3KMT$7;N(1-obj%S;Qf zr(J4wsL1yPHy%5*1X*o3mnc0K4!E5pX+3zbHqPczGpXS`?ojDuEvKr4GpcoV^=RX` zT)8#QU>IeVydyN!XIh~%iEJb}?N~9PK-xXEKB|-Tv_XmmGMnP{*0`hcfUiYiGgGA;wpjz(fF6qXB*D@jWKox3@Xs)$8a(7rrk z)olXeaW0Vqs}5e?;c<($p_J2+JvT&5LebwWd#^uNFrYezQzz0i(@Xd{xxgK@8ohKg zGtM?}$*Q(vs9(s-n3LGgxk=#Z=7$UTMkTt2T8m-%xHe)#T3cy2luF1ayNyV#6euI2KFSok9OtDq3n=ACGd z_gW&n9={D-wk%8_E$5P|0p<(QM6qQXz$iq>*81)kmwJ& zc#5RXnDEH8?!5cK%8*c{7UT{-Mtb(3z*jSy=HH#lWqKFXwQ`H%-654@cB*K~M}9xh z-Oi9c)q|Y%&NCP9WZa*k#>NRE9Lmt)N)zv0z0v6DGiScpaoqLUu&^yF64=s;H# zV7o_;flc9(J&dwt!Lb9m_q{Aro%e3}&Xf}g&i zZ+c*GypU;~e;vsqyd`)<;CORA(S-!?|HnJ7Lwi zEE<~$D#GsjkH`ynG4Dr8E^xS47t3mc$y}pgM00ku#Zgb zx>>BJvA09$al5;V>l%fp2Pc^68!kT1TUWG$4Tu$d;NendZi?4i;xbYmJxfqHK8SHR zT`yocg11OPqqJ4b&QCLZN0CV+>kMuf=u@6^A@V>}S^)cz`3=Qc1tZR(TO;>DQow1A z&|#w49akAP{|6r=4xSIs@Z73|(>aGHXb;#(Q)bn!zx*}B?F1642i$ZfJJx;uR&0ep zoH&G1=kqs~W?p93Xf&X6Fhb^}+k1Zu1;dvBfrD-buwg(#B)|#+I>(kQJsf!0I zLI?h?LvLU6&^I?7qF3AAN$NRq8sHTKY~j{|Ryau7G|_fJrjubMPWcT#wG7ImgJIOm0LH3_U#^~4I^35lT$#v(wafE znDY9W%c*s!>55_3YF@xfX6QNDxnay6pAzn|;+;7PFbYYOx4jaU zw{D5q-(#OGmhFi{{at;7eS5NglpYTDj8}T=>iOY3A`rBaK%ael>PsP&sjUvdECOQj zs;^gpk5Hjef|n}~63GI3vWnnC1)9Yt(QCJjTKQW(;W(g^RysN(B?(XnzQ6fYuyk9H zLl!&>y?^!Z*+|_DSkq<%dvTol$Sniy)UbP>GDv(oYD$g^N*@%RP3QT!TSu;n)REmR z5nZ8NlX&`=(ifj!0*0*YMhjQ6MioYct$FWp`0PpOeXRiN>Wa*NsJZtx}JxMqM+zZQ=zw8ySCO z~=+HAzcz7Kf1?>6^^nUw#b=rF_VbJQ27jM&`NRVH=sn} zU-KH{TF#@LCHhs=w7-mc7*%2fOtEb7O;r4+C(7aoaDKgD^i+a7TH_43(-a4KX7pHF z)PL)!IjVNDRFYk%U>#}56k~C1(>^jTU*4qDr!41D?#HAjrl_v7PvQ{E>hp4+^UVw7 z?DFL+WohPqs&5!T7@w&*()70WP&J*@Im+cIc}7#^Hit}*NJwDCKalcN%WdYJ+G=e9 zUEr^!OL0GzK`vamG2^YlQ@qSOt`_cBg}^WsRbU@fE9P4%!Fx_jsG6V|l^!4!E)Jp@MrHA%v zuGgo>8@8niAiW{_OidfaBKCRArVp;_)t#7TSSKxG6j2DDvD>V<#5mW(F7y8Dw%nV} zW}WxT`)h$=xD*Xx<{poFeqb$#ThiD_#*_vBIEoFf#jmwQ9<>U8zU+&(YAf{s*JTDw z-|^>@dWTa%5M9#}m806|HSXfID)TmFMF=_5QONjgEL7Ok%6xDI2FpmOSUybD01-OT z_UMS}@4r*!^ghD}?&;vPdD@T3kIBCt3S2E8_Fj~zyG+Iw~QvfrN{03Z7{tNcETuiAK^#=ob$ zMApPGE}|UKgFEdAs`U(tw>}KRQD%J|9h!4DapD&py=;J{IdNWp-Hcj@u+({igX332 zs79=*eU)+O>~KD!$}#WH?_sK%)&e4?Z>m~mlw9}vb2P*#c^xxgac6daZKINC%|h+) z(O9KilT5ggz5*f;HEV9NayHfEe$uTdFjVD}OZgM>8^AM_6VgB9U~GS}&;G_4{ysy< z_D8DvmpK^we_j3Ae+dWs2MO)VNc&sM*;ir|%ij~D{_COGpB?+lE^0A+SxtYc%-H@s z7Xk*hKfd1A@y}r9*YR&-g=~L=XKa7q836C0*RU*x@GkY)Rp?wyrprR_@Fc4nn*+qP}1(zb2ewry70 zd~@%8nz!Tj>3%ypPQ?8RxE2la2Np`&A_X89TzX8P)$ zKX@=bEz?);GT|{X(ouiqDEvJ={O5b&|CcswbpO6>{?g)Kw4wW>ZT_w&_2=8^f2Nv$ z*5aSEp`-f>()h1w^T#{#zg5lu2EzaIckyr4{a>7_^lWVZ7U8d%5stunE-BB$Ie04GXwHa8sSNr7fqJDNt8~oHNZB>(-G8tb*@fRu?9UpGaWWBz-xk`(B zpOIkfc)P2X0R2oPYi6Sf&*D!``Z;!YbFdSEMbqU? zJLhYJS(wx{`tHDJH}FbueHjy_^~wEn1@dd>bL~kMUK1tCbI*xCY^Xt-nVb{7(|)Jz zP^t2LxCe3XYI$&3A9Jj+MoJo(E~U~`^azHF*&S5wk+Fa^4bA= zCW`Ca$1k<)USyy_obE>?n+lj|cG1739u=x>^baE`Fq>KpKtUJF z{YG`ehuro>feYAxo8aq>>COl%ug+k+&1x3&i^+bvU2Tu`GDZ zg{xLPk4wXPcXwTuFaRz)X#F%kABW$%@|MtaR-@!4Md~Vp~ z@PfzRA=}l=y0*IL9-MLTBiEpKL5xcEgkfPOr+1pg;IOQ>A4MV_IuzF40*|Ju!!-}$ zZu|u#@{^Bel15TRL4hi~AJXh$p;XY)G2KFMm2Cq?$qu1+893CZ)Jo8iL6rylO24o= ze`UrGCFZ>y{7$^5{mqkwzb}~Q0oN%HB1S zLKgHJg#o`r8XLxrqeBJw22l)1?8$PnU3>+P2xys<1zff=47Z5P*9XLZHOGuXaD~U? zFUDS?t{Cjw!K*m+*5$;V4DFxUnKhQwkn6~1p9$m7D)Tv&uquC)t5=7N{^0puRcO3G zBuK}YX7-bm#@*JXn1&044QvRj^k6-*Ks{{G0fVt9kw9vJ_34^#c6K-EJzZ&2^2o}8 zs;B4Jej?at4ldcaBXoU9OIf_C>Q+4V%NAudA712MRMHb%L0{aMiCwbtX1SuRI8BBRn5Qng~akYlgE+fCA z8!*Yh%zt&5vZfd|s=BFbc1_(G(N*IRr@0t1HWen~i;y*yvQn3{5b>N2n?3||_&v!~ zsi^r(Zf-q@31Jp%$}c&)4dk+LIGmVQsgR9h+#s3LafLxG-?8}a2)ysa5?PHjjrAR_ zn7JO`NLjl~-|bUsx&mobi-5D8~);NNqH-THR4H+s~|7C&f~j$b7p5MVXnAX^mv!h7uRCLK1^n*)R&A@tkYEH z&isugBMann>{#d$aG-JF)zBEsn8K!E-z(7His1Dmyl1(^i!8=wLF5-djwk7PeJ|QJ zfYy;Eb>*f7MOA#E$Pw78dNopb-@zkGnyd(L1B3e^+xQYe(h5E)r2*Ug@ir@p=bC>> z_zaur_EqDy?jZwRP1`!;ljJnAxncnql=gE9k_fw(a+4bvN8n2f;aZdE>xr&p1fo+1_{TG&1s-UDUO@^YNZ_P z?DoR32v#Ye*@9~plDrDrke$FUCNrpjE-w+CbOSJ^a&ADUUV^IQZZdTYQ0FR)*hDhG zPIknuuTa%Qk;ZA>7jmXZlk!Lz7r37!8;5ZuQW)D8u0v)1B+G1iRGDyG zhz3=fYWvb#assMEitU|=G-fdE?XcpQMwzXclXgw zQQMzBqRspeaW&eVnMQn#=Ht>Q2R2b2@jKZY+Ze zG{m!pu7idw5w)MKb6jL~U4Im;!{V*Lvzm36aE##zdK51E2(1EgzV2eEfV`}?nr8c* zCqB?pnZR&D&L$+*!f#YFfBA&rO4rM$HG7;*n9C?$yA5ILyP?@ZB1nm4&DWCXh5RCs z9CpWG215KVI4gPGehjcLf|j`Wpp^)&(d)?=m?Yp?>B>{-Se|xZv{6i=6b(K*u}NE6 zgVg#A_~<37>>RcLV1LSR!ss}^QazBd>P2=@?h!9u(Zo7&9^BP2nEGS5c*4{;cd{EK zZ+RmxS=ua3Y303Md@YrCSod_JU51g41LJVm0@v3C?!{FanbB`jzgH%is?hA(%V+-8 zt@)fLQEmUDjh?SXi4XUO)3(TAd|iF;sF&W6sgt&AJUsEhema{ zp;=^A&xaXK)GkpV+-NqMT#eJs|iUi zAL76zjU-fMSVREIcu8-jl~8iqcUu|3|1u*tld8k<)QujTSR;>PoBA$>Y=&kCoZdB-ypw24^OL{>7o&u=ubaa2Rvx^ zHF<_kk6v~5f8_1B-L;)qZgu=lYkD92W^OIMymjLY^@^!*+rj;)2+wfzh{SkxD>)S> z`RMwwz)H}vNb{Z1Gj6Su&7Jn$v$zBES*1dW>!E@>Anm^YLTRdRqsow#>9GlK4wC`I z9rJ75ykVFl`3t0(D@}A%A8_MXl=I2d#fbd`TG9>X5ZnEFYn(iG$Y#5TnZ%o>bw>Vr z)fQy)J??EogYLl@=01BMNAgqE*%khBO5#E4Z4&*r9+*|6sn$|p<&_J23NSq$y6#GNmc#I&qMdMiF;NiZ=@ zes@ld9NO2quHAkL*1y`9vcuDreCSoEZCHse7|fp(H!d1CwwtbyK){a8s15z&SM&F{PY~C1l{z_rT!f zIVW=SR%hK@cQsS?2yH`XGqH7xTm9jNnNnp{u;WI%GH*SgFkh74VJ~(o2;qElT*6jr z;wQ`(s^tfps`jNtOR0<4XW#?a#dj^1Kzi@`n}n9}`G%=>w(0%8n@MHNZJ`?*y?54M zW7RB9m_Zk}$REDuD)GfbMm|RLo2aDIf=M8)txde2-twoCq7MdxqBL09;BXO^vOffG zuF}g{P*oT%SCwn9UU#-pnxd_13S0C}P{2V7&Yg5YFiuHO-HBMS6y?%%bT8Va`SD8w zIw9U}qLm{O%Qek5pz(1Em-CWGnAA(Hvka}xQekV)C7v8A zUs>TQpErhUpiUUBEz8Vp>bTtL8Y(PDXFd)gu)sWRbnF=mb7ll5NA~W1Ln7 z!#nFHcOSl{2kO7eWX?Y}0nW93$nS7ik^n*#2xYd8c)ljtITfd3x1$hx;KC@KbgaB& z-+-BUN8;WI1Kk=E7GfSanFJcw3$+COyyoMLiN~9jX$qDocXmOVJWh4JOk#1!ck9mH zln!eaX$n`doN*rX0QFM?^L4akZr9t9PUDQ2Kowe26PnL@~#!y?o zER3-+NfN#}M3T~wZ3A6TD84q^ejRXiOHLiC57Nm-yTE+MEZ@>$DGkGMsJ?=EsrTqs z0=V!<2lPqr`0{2m;?Iz``)+Zcn>3{q5yOYQj+5Z}vY?DW`BRVnLgb)&AiJ9E+Yw9X zqyiJ5RP%)$vqEmxCV1v~XlmT57|xIa@XtYlMsVe2si_V|GIbDsq)_n=YCZYf5DqjN z#r#qlvMN(cBnc_%vpf?`!(HCvO)mq&raR8nfdZthtGws}W}kGe8W88Ti5n0;0Ah1t znLG5*;mWwkIZp4a)_Ryup|Q4^s6aB50L0pwEnAOy(@bI1cO1VMq#a8Ni$Gl%qLMn; zs8-qD=S4o^eaj-WjVi`gMrd_3DwiQ|JuqI;+q1(sd8)QyLNen6wKGdrG%!$AcGt=} zkF~&`&zl=x$%eE_8fjMOl>N|5I&a=pq#u1|y{*G+`w`i}#(LjsE8AaQ;dI>azCWVV zoap)r0oS4bm<~`lFt0expWBZgu}L(=?0c;gai6APQruyEVv3rT?M^8HZB~+Xoyt#W`R+FVm!P}#h0)(p>uC3l2!0)>ibgQGIGRwT#&n2P88XOUkoWsSaDWO zJ9eW+v|8oZ*HXb{gzxI3eTn2MsptXeHcH`SgXkNO_*4pxVB82rdNdH+ay-_66AO$P zd>>EGaXrh@rHsLZ@U(W5iKeG%q^f~E}0SMMm^Y?hmkY znmiRX7k7=LeShvtllb6SAY{6GMAcxl)D1c!vXcJ%bbC(t;9empFfJ?LiWTO?@%?pl z4c>r?ek`>Q4_DH7$oZYj&1 ztM31T>}VP2|1Gi;kKP-G>3po3#;umSB>(2?V-@K+OvMYtM(KxDyA04H<+J~Z%a1bR zChFAY&Y6-jkvpWXsyMKFyEl}yRK#O(Xl*t-cSa*^DtDDcY<)yT#Nx_RR6KY0@_bUn zLvBbdrrPxak=dXwZDfU`=~Z6l?fE_zmn!AP?B({JGC8C;1An9xxy4-><(&mc^;|v3 zM$?KKlZK1nsq;AUTx_b!I6?tcKJ99#JTs=;!MO27?3l|pyc(OE6mZhSUzA(X^s_`! zOfA+^f7c{h`Ci5;v5)6y-d`=&(!8yzArC@XdLj)KS!8P)8E*uHTjC)L?dkTZ=cOdJPkEkanG!`4FYpxTm zd3brgJR&a6*GkyZ%^i_k5-6w)cX?HGStMJITD#m{2yT8RooM699O*5zsHiTMN6yY| z@$_MQydU98H-l8lt4zaZdQ(l2kY1Rey5zMQuiuW%W$j#=pF)45WG9(LS2_f^-2Q|s zyI@BLz|Xq>btLa|%N=_$VZZQx(D1^GSl*Mg7<0wm-C^bHo&;%uCMjO**U*dGW%q!* zj;8SQe4fiGRp+qTgh!Q;hqa}TCu!U(5!#cQ2n-J%{GGOoRW~O$pYjL!@Cu0{RD+^z zZ3x+`%L3-8C|nS+46ur$Kxx@X^9e96(=(EB8DhQ9z(i=@E{ zIv!9FKrtfa&o;Gm>nJEB#c}rZgOSXgEt0FZpaH~Xp)}Or14{7^+~5-JWS4I`u1Q0q zK(^5dMYxnxoGO`~)tt`iH~_yE<$ZdoGYY_`UnC6phkS^BZBdyKoiBYLHhxRf<_4Ms z^AgqMw-&qd)fyetD0B-q$GhrQ$`j#`Q5llfI46`wk4cx=SGnGGt`I#1o-QxAwHUqC7rFsD#Cpp*aAA3Bo5*?eaIi-`CVw3jhzyOayUHCzSgz zB<&FHfUz;G523~co8I0HcU-SxoX7dBIzq~wB3^zMYMo;+{g)1)`YM-9BxJ1XjPqOZK%wIX}L&`cOchyCn;#gq|l z2>igSd)omg@ zNFm0oq+->_l4VJvIRWG83c>6cq1*xmP|%0Y#bMvYporugY}fEJ*B+LdTyJ z^wy>1U?B}J)H)*G`6OghAy_H)wLI-{1igS1~}$=rFrT>$d|t9w#PcORHlMp48Yhx zSd(z5%g4k+WbxUzmO47lk(16tW>GhSn{@1oGIpH4Wrd|TKnd`Wf%V4^7MP-VIjGeJ zbC-tJns^%$7P$PfPB`UL7@AH?o=zMa&$#7X45}oGJ-sbt0BZvC2xjpNsJQhy)^6)E zFpS{pK7?;hAjc}~LtDZ-VRr=Wqqe;dgO&M1DjXRH zQblBvkY~TobE25<*Z|i()1ch<_(qZQhWg(llRY|hZ&0i6tqoZ;zZ(Uo=>~1~I}HJ~ zEu1SpC9f=R){aOz25;`WnADaLx0MmY+D|k2?`>{l4t^v;b8XSyiPt{A);CV<38Y8W z%@~E*N1RxO-7{$+&{-fiv%X=$3uLh&Q=S7VS{k0ZRDUA?b~s430hH{r+fhn6W^qKU zUCZqjUitl4ySMKfOF1{f0?GtZm_z1TN~r5}B^mJjmv?Pl$&eTu0gT>Z1Bi}TfBvY# zY9lIXtwM{ok>9Lrv-xp-MbMU?hNW1RXn{dVV6AHnjVz^uZUmlKo!+y8mNZQuBJ*8! zCE{LwQdl^%-K{J$40Y!UjV9eB^6fo1ikhYy)#>D$)f`x27DQ0MLZbmw^8y)rb)cV7 zU}<9n2G+6rWy3y-UP_KxO2+jc{R+cuKR65=USA){AC+{#8;;P(`!#Z)t-jsbP*u`kZwXq3Q7!4?S8ycg(bVh&o&Zp*afz$P`V>v$@hlNxDC zR@$A13#fV2ovRrt5NFApDZWTePqau7Or7g+qPeUU=}Ydaw-;!JKVD7GCo|hyXaK6? zVMoBlYV@c(91{mYHXnI4CgmIvra+65+I)jpTy8e-_I>OVwc3K$Dhyz3ag|_{@~V}d zxPwg=8jQQ+5dcx0;nl>4)TCk!a!1dnJSjssziUIyq#j5k-ZOKjh5i7eNhZt4VriLj; z{!aT9!VJ?W7j!xp%c98ma8F;m=BPtlS%qCJ-#@Pp$-SE372nCZ%`~2c%x1Xi zyNL`6+&76qmXTAJX=ZL1rZY9o$JAu2SJd*xRPWF^Z?W#poCC%oMrx5Hj{_BqkQ)*} z;AEic!iLr?WJL>CU4T@e5L=leNwncnvQoI&O1#m;d7z~RS3Z|4&h$#Mo$y}PN2++~%byG)uBD@*L9czF&&{Y5cNOIbIvbPj#>zZDD z_A7IclMSbXHoGI65s*txv{+LKS8+m?gqV^2eBj<#U3?w8f9F28Rb=pW>enbpzPnrm*@q@N`1NkkKN5UQoo*@pv;KIh(yOm(QA<;;lI z5~fII@<#2up)UYgT6POv+e)S&Bne2Y^(fcCQ2cae(;llqaUy7tZiWXG&xb;@w!69D zUm5+moFA^T?t($vt5}X^whcPD&*(e&i)R2oiHldS0;~Dcl`*G5LQX4HiQ*Fv*)AdS zWWjeAjBEAaB-!?tF(7}Wqs<0ir{g!Xw@CK)q-XM_NP?i8cdxJMcJfvsVSSwr)yHqp z`4aHmR(e1qUt@Gcr#LHG1`(8v)q@Pbo`#2n>C>>fk$$)2?(20;^YMEx95}~ASeQ-- zVJIL-37Y)+fPMSS_r8ZHakH%;^U8^+9O#kIbNgSRD6*BN? zi-s!|ZkiqF+`BZd%NKmATqLD7rMtTYSqSW4Bc@3@2V5tJW@v75N;EueA6 zEmT%zIRgLvr3x$;7VFB%>&W-6{vZ&x&@BK2QSPO&&K9N6*^jZl-Beneqm)0PHFjK8 zt9jMAD}!CG7aB$nT{@06V{qH@7UUa89Gg?RxxwiZb1h;EljNsrWt4xf5iDn+^%sx3 zQhr(+t)IaLb@Vr=x~Ec?Lr-c9(#M#o0#>U#{PAc`A6_r zd4OAqDfGYxL}#`&e%enxk5V~Zd)629!vGhVWb7+FH6*A(T*rx095E;zsq1V^gSb_#K>kLago}K|mX97WoIVVxT&`q4yX( zuIwKETkix|(Oo=K$QhBkT2zDVcy$h;<7a_rj*U5F6Si^l4>2Oi8MaUgFuDgw&D&9; z) zW-ZRi4L9R9M}^h(eQ+RYrmj?!a8NdxSUP;ya^wdbHtyOQll0c-oEl=ZRs_^)TH#{? zYjxkCa*X{`j(rVlCaDM$ud4g0Lxo1vKDF21BY_-4>d{y-|9iH8beDfc2s()nx`zKDt z_Sd-g-^;1k{?S~q|6+vw=jhb`c_ZwfBK^gw*#2f@W&3wV)<5t6!*i-XtNw@ORDU+t zf6A%8=F|V(62vj9{Z{ymSBg6zxhF3GUx2m^H3QUcUKCV5l$Pdipw57VpIu+UR$)Jg z9NW08rXpzke23~qP|jRW=f9@Iv!o7j&8GXttYJ-Mu4rI3rPzpATzCqK=dNz|BOw1-6w7xzjb#WH!&c|K%ja51DOommmvY5a9-aCJ~q?=BJwND={ zbO&rPj6DXEik=jfT+0<*8L1X{Ovn(V={hYq=~%j})xDUk8|WygwZJ*yIWddyBvj3> zv9+%mQgZ|GidH+J4Rl)SQy%NQvNu;;<~CU>VC6;z^2|(k1hT4)RBVy&sB;^|w9y}o zl)$v1b^sJ+I>B;p@~!(hg8&4w=)gBn9jF?`8Jo@OYR%#oMJbdqj9j^8Q-tvsrUDaI zM=4Sr$1;uOOOm0YX_Da8t>i8?p2}qi_dPmx4xp{9;*Vf*g{qrXTCgY!R}Y&wN(%nu z)R8>k?6(N2g54A@u9o?D@q8Z(8Y#R9guUdBzxBrafB|$QQ_>7eFqB*+<$AlNIN#~| z{5bm--E9vE#oqh!*ou?WoHubL=xEM)yVj?@vnBjRsmlMLR8EBeTVIq)dBc$&fWPyY z=!~x0GfU*rsN?kGuJ%t#MN@&l+rm+xkeVgM*eZx)GtME(W~0T z%jPD?yFBHc6#b9!aNZwqFS=f~z5Ifrsy`KC>ZD6i%*+Qy$O|*~G_6TV_|Q^$;dK^Z zN-9D3g5bU8x8>8zrKSOE(-8Q0kjKYRS%sG3%=I}I#N8Cv7A~y4B!e^k_f<6uJEbxp{B-)o0}3zkanQoCwrXd473F#SFe2_r=j)Rv8Z8Z!s1 zeUp?Lk6#=+z$Ee=9YcyXryCP5R7XvuS$jJyDX%6DfzfIa6|O=_t3K|#5G=mlL5kQy zw`2TA5jf6Ox{mlg59@#|{^pXMC3LPV=-B97}4f9;t!0G@QTeX8Z{X=Xjo{V!N z5Lo#~$s$>NI~TbYS#;Y=KN_L*q~=}dq|eIiuFJbn6&f5i6=}_FSdN`##94NM$_IJS z#scnlsiBy6L2s21q~!3zo14f@8+PewhK)%~R^w!|g{`cx2`%UjEYdXij1@y;c+2K+)T9jz-B{ zCqvN(Q@S&6v5prRI~AnPe(qq5IwjJ*dCO-c2K@BwV@E zvm-DGfR{({;RmwliwlPoHpQACp+k+!A0dADC`T^kMlw;u4Q=Cc>+2L7+Wq8aAZff- z*hK%~VAwZUf1WFoe!W^v+~yOvZB z)#jET>=uG)J91)p19cqL-%t=qa{LI2$fykWV@d0~i&b5xcHB};Fa= z%W;(@a6rAiamUuu*-K-Q0*=9Yn3HDw)lU-t>SKH?bbXQ^j&G=FlquQl)BW$#kq zRJ}jmn2TT2xvtlI(zv#pie3ms%b$^PcQHMoMtY|$X@VIv#Xsq3J+=px+9f5dpEFq19wK- zV|NChvjkHh*2E;}y8ZHjQjMpDlM( z*=w)>Kj|_+gVR8cSv1~{j(6cw@oSMKm>UPyYNW_A2y!*$Nx}{X7W<2WVw+1G3~||) z5*U{>Fdi&(_!*5W7>zKepel3QTO|r8#f_S82V|2ZqrA9$V}-!>H1AGsH zmx=?7>uHV0oI$8&`ert4My!)4!J${qM%L*6`yHXo1t^*CxbGbsp`vKtv0tXYC?Kwl z%y7)~Gnjet8y)zm*fkX8>hyzYGe+!a8^F0Wuw0^Mo^rx6{j8hC2 za{v_)LpbAo5kmf<&amiFremc#161?BGc@f;qC8Hd~HOyO6+nA~b%j z%Z&sj2fpHs+G7!G@U+bv)yjoBcpR7d9L2!&+RH8S@Uxh2H~uY|8CK1yS;ksp?WWqX zAr?EOgSyK#EU5#7uY4Lm3oBW|zDCDYVWPE$-l~fN>vVu;1*33UgFbVP#dkfJXDeSx zg-C3>oLSs( zr#t}NIV8Q#y2mj})gw5$AEBa5y$xYO)ANEbEaIlbP068Q#( z1OG6CH`Yg<1F&m2It&A`(!kI|?ZN6#mV-{>U0Xbo!fkOOww#Zi za3Pya9#p!j+!`be2QDlE+JZ$fW`(v`NoL0Y)fUtEE$~I`Wk^t=Pcr>|-KcpcR*e-d zAQlbKO*1HrO5QMZpCb9iL;@oCJO3p81z3oq8C<@J>eY;UL|HJJ%L1TwZPhR#hSNeA zRqO{d#cl(t@r!3Cc=Hw-96gY+-xO9%1#%V(p2Z*u?C@k5E>cWXj;B5^QVO8|??;GX zI>UFuQQX^QYlTh4;`e~e&&^SFbW5&?#?^3;ejM#i2P4nBAvLGRUl4J?@wa9RG}vUs z!aPj`(UC2%1URtw=W=A2wFk^kLoCj)DR~Mj85)c5Cypr*8dVhDg{_^%9#C$8xqN?ySQ}Hk+z*MW@tPjy^gzSb4cVEDFA3!c9keAP-?;{C6Q{+ww<$hoW z!Hg0n)r~Dop5qx*+sI|r0mQzWLMAITQ@Emu-Xltt_E*WlS;ORY4t%cKaIMHSa_jpo5iMOSZ? zctT_*kQzaJ3WbR*Xe5HojbrLcjQ zB2R(M15?76r;`MkRS-9(;`_zU)lq~xFdZ(0&qp!Y5ZKsw^4-mHpF+2OdAnNCrBC?x zLP}?|0+$p4(k7`Am}-u7%ZWw%({UlMk}H^Mxppis!RDk?iBg%`Cfm<)v<+lYvQx3 zZcXa5t6@!|Ya$9?D_THoY%x224FQSe4!@$A-z&Y1)kL4wzCMkaB%mLQ?GV&i59wIs=+*2N*@?Rj2V(Lx1p+niW!q&m5$q3>0JSw313?{ZF?xXTwcm6 zhyB?2w9EC(_?4M+BG69($Ib(&mb+c8;?(>F`41qv-A`gKi41A#G&7Q_m7vkxcVfG~ zWx?8aSjv_u>eK`_<=)H2^Uusi&EotLV#`fx0eH?L&f9ffKEtI#%r znj!O8EYBqA-S!6FX4EEpuZWF*#4bSiS3X3|M&ptzda!P zzhPARv$_6r0K~|^@NbPu6ISc22%V4e#~}SXJc2s-*&9^YhmE{I_TVV?$$}{0zrGo5 zebOpY{>mjRZOf>)khTDhGL0?IstVIqO?zzEY{x8ZLVAGxHF+#<+M88>C^uCzB^5c)--j@`=s-XO>?Fd^=YkHYp6?I zTz&8%Dp2wr*sd=(50`brY!BFyM7%dD7`t^!G3%T@PSfvhqgH&y%Zb~K^O2DXis)AQ z2S;8MVK`-CE9?iUpxU-GMRWCdVjE~@X-MFSc-6UFPHG~=6E&*|=bRWzGrH$ESRn_Z zoDg6H4hyGcHya1yvK?`IS|6wmk$?a+s|y1ZdC-8fnUoWd=dnMU>a z?PlJtVIHowK0lqi39_U1{GF;lQkSSWt%^83cIucipFN&f-qz-fz;zm12-0+eRi8jV z)mc`k*&zrG{4`BB)S9i?Ydch=8+kN|p@C;G-Vgv0v{a_cxQ_1;YjhcrYaqGzpY&gn zJyTdvh<`YTu{n!Tq}=v0LT)s%pU@7auMEZK488695K`9Q9x6+yxX7NUvNF;dX}dLC zH4)o_8n-d-of(xLM0?s&y7TyY-76#&OCQs54v6Ft&s*5Vl!Wln+O3yP7t9-^A zWq||8NjE7+9w!fRx=RMarAHnA)nT}D@C3dvEiV3g>|02L1rD}DUlST(4XI5It5v$g zS<_6%JTvU|5`?en;a5~lny3?_yI^x zs7}!N)U&++mD!kY#K$6o*7zrthr^LYAS4W*5_-;e;U6y}PrT97;AR|P@R+Pp0IVCr zvUopweKFO-m&=5ZAu)$`0$RWZv)tg_+vOpfSTk}rOtuZQk%lK?b~E-O#25)q$e#<2 zYKU%D4e0lTN45KhkIaO;B!;dPmm%+4BHupo!aK;4i-uIu;BVhKfg864wI`t4^I3xW zeQ!BM1rG*j3zU{RJP2qD<7{P3ua+L%0O7$>T2;RC))$T5gzZzh>SVayU56?Z z%Y12+@JxLK7kHhL{s4SN-&+Us5U*2`@Xna!fiY(Cw&1#+_+>3N-DIb=Ad5Q4MR?#C zbmV`e9d*QZb~0^E{P-^Ry9h(wQsnH(I4flLYCblwl# zSsnxJw*ru!amJDIv}pVi-CBweebv1fl~7k?ge6xfqQ+q0HgDb!IhSnN9g7RcM8o;Q zzjaBo`R*n2=&;@I5&ix%K$dvlirNDj}lB( z6z1Y47wVmKY?XAr+GI#>q_);3AKDjxq~QKz^34r2qOA>qGceN*osDg zy6@f0V;2-m05!i;K%*{?y)Q4@QK)!loO2k;f|}1!Djx>JxpUAYtsIOMm0DLbQ`Q@~ zNo7C|S^5~QV=psdEWaz5&3Ae;Yk-VW(++p6=lXtJJcA6PK8{}-)&)xe3|9A3ZiI;g zx?m@nx~$;fRt@d1?zOCPDDTyn6jNNQNH9J+EKa%8S(9P$Fi&%G5b}&04k)M=)u``% zSHK1jGg@pNk|r)#ro{}Yc-FU1EiM5U=rhH@z_H@Ge^fm;uV0o+MF+sjRJ-foU|h%a^$e=3E-Z&xKB|giz%4D zsJUKGAIlS^-^?ow&xxo)1B{hrX>Wl`C`3%I_6!+nc% zkcYYdeDXW)(}g&aqsI;!+KoSwMOim-AV8j`u6khOgd~_Y=O*w4^P?6CC^k=F<2ExD z;oD(xrD>0(Rx7=A%4vTddD`ShSMcZq|L@*czboK@2Y%S=$ zK_SiHm04e<5}2}}+r;l3h6{-;`Y;srVGg`OYOhhZAR_#wy`R!gHql%+xHqx|C>|C( zdTmTHE?5cxs@2}bT6X;}=q+7I?b{kwQC!zeQu!H>;?xm8|%o z+j%3Kc_X`dBO7TW3uIqyD5gIu)16o89W8O~Fa1tF(%eg_uAByh(K2~GUmjAL%Glv3 z(&Bc0>`k}D$_FCl^g5UZq<*~{G1`$hzqduEM38RA%fK6!sFm-+5zNxKlo`|u%WN_xjoZJ?l={0n>K4bJ=1Tb?t@!WihWdB9cXEyMO6Fu^=*ipgrqFV{nNmHJz90) zhyxqG)l>oQ@Ma)ob8{pm)-NG0oXw~5n&dVSSe`ICm~a_oiYBK3wwF*=ZRxH1_*Q$! z%OJXWaXT{+7O34uW!pw{W6howC7Z?u2Peh+-{x=wuRotrOnvZ?i}SF2+@c)Zq!LJ& zj|?O4FJL$9FuxPwj;%DUI|_64G21Oi%7KJlcRfnT-833Tgwx$MV$P zQL;x%eQUa%dl`hENLxWyePiX^bC3D>|H#_N_Q_DF^JVuew;B$^`ncvsc4S|)h z_H8ZnM=w@x1?RG@A~tbvtId__o$_~CfdmfG%y2UYFqXobg3R480D(6~4Ny5kJ4NU~ zrkQsKU!!#G+05v$M%p865nHI5`llwxon>1luNKvn6&W+i0sK8rc{Q-7Sr3JL(IExh zu(HceHQq+oc2hq~-3+Kal7dp`tAWgqS4nt!W0*L@b1gj4d~N?3yDzNOLU%enq09^| z2v!3sZxp<@O=!ZD1JP7UU(JCyEg`RowU=_F$) zW|(6T3?m&@n1?4v!Y9L~R|oeYp}rkE+`ww?SR(~)JR(cUydMCOip=)SQZx-q&_EF| zRE%~#3I+T1=3)H8p=lyXe82{=RD+)-W&QL3T``?kD00y$-#OyY{vl6kT3xVXUb<0);0emE2@DCam+ z{tobG&V&$RA#U;mi5$;;ZA!*0aYySujJe6D#fFLqScS+D}$(pp3I{+0y0kY z8k!cC->D9$nshZ3ZZO`#+=%XzQfT>2!&)dXfLnoOJ31H6b<(=ev=+%tD|zNp;1pTP zha2fG1N{TpjJqhf(4smqWiN9+s&%cQkq1G^$5l^8e&VW)CYwgZ9TiFS1ysckfEaoDfblaya~NXCDZH$baRi^st9S0w4z&NNy)#=o}v z(|ldfeJxP?tK_c>`mc+B{usl*PX7E~TDta^e1D<_dfLCdCiJxb)@$Nt%$N=-FeKdMZ~CpYz8KqbIwU6N|3n5eXV?Y1i4OF?pNP{qrI?1u0@_ znyMo2&+>*2ldr!=#O0CNMrA<-)?<#3JT#x2C#KA!K_>@axpY*iTGw85_Z;~Hx0h_Q zN+IHMmUBG(R_X>X+jr*z^z~FBRwK$+|hf+|8~yC3{b)C1Qo=xAX#lhTfHmW)Hq6`7DI zR+Nr;)1Z?Bk=?2k^0SMUHi@LGdfB=Y#KlY&wy^}!grp^K>r&H9;bl_$kX;*BM>v1F zO!~l#mr#xb$J0$^v&GL7o0=@jdp)|QDSZV|QT)%2Iq)OSDne@TtryB2G?`dVcWEXL zkCNuHiqzx_BubV~?veQU0|y??USGxp5A57~gJEG;pS6p4@TTkam?~6M6X})GPMVa@ z7rJn_r0&5h=NhsR7q?#BgltKauYOuD-j6TM(LB9!I0^x@O{O>uMuoWqebQC5rdfRB z&p9DFW7~a?5&g>9JpFj8ed&fP-x)(AY!7W-LS0u^(kYv92q13|dO}2*?2N+XK&Eg? zrE*#|Q6wjps7%G}Sck`PGvW~m;=cQ1c@ckCs&;yHF)3NN0`mTO+}HA=3f7f@JNtG_ z$^*gxMMP4-@>ZgSdxLkzy}Y8BI6LAz5Tq*lKMf|{RYE2%S5@BWjO+JZB_mFRYf>~k z#Cv3boz96WVRml!-?xnzbEr}$0EeVjHu}_-5bEpu=WX>4>lz?Uc@)5<);04OZQHhO z+qUjLr%%LObj;j4(bLf}|1!VKjQBDmBeCA+U2B;XMZCVtUaY4O3#Gt%z^#CumV!)2 zZ{q>TiPk`I%XOylCVuEWY<`FrxxF=Z@U@sVFr=N~t#fsXmU2F$cu~Z{6se#9QqJwO zHubrYuG}-7j%Xqh>ulB)s;s-*IbcGsZpfQuy-$=M#-y3+$qZsf}QPw5=Y1~LH(fwE=dja9{oH4q` zjaTQ|vV9H_jMR0(6W=!rnXckp3J;wgE7eJ*&*!fw=-r0wkG0=zYJa|gre8b^M5mB9Hvk^2))*O+X(?o4Vfe?cV z|LQ7Bg*WD{+rf0o(7qJFoRl9k+eIEVu&QUIiYMIV(vk#$KLl~WrpHE6jk_XfEO5ETW8IB}M@C5LIy-%>3Y zqRQ>Howjc-VO`VR#(-1OSRh@M(l~CuRdwGjt`w`TbhUduD+>W(;EE>_UUOi@3I1X+ zN1P0AmWL^S4%TEWYLfL4z1Eh6y~1cyRopBmU34A4#)!Rcsxi)JV0h!(Q>pyOzji7# zfU!V%l7!DM3|^=U*r?^A@B7yMcrkT{!-?=uIQi&!%XHdHAnyw{BhYItlx1cFnE%@= zLZzpr=oK^b!}a(%EjYcF((cs&^C^-T6%VUk<;LXEAZwfBX9%Ny78dR{kuv2ID(ZK* zz`a^f*g3G8_{2_H`YgkWIs|%f;Z6FSaQ~7)ofh@ox=&5+xT$%vV43wcwnb&iFNBWZ z&15d@Vfb#4as_9;CaQpA6I~M?!m2f!X)IR=+f?(Npv@5$oGP7qTG6{%BK=jFu_!q7 z63MpKM;b&k&dzZs^q#^jEP?sSXg92=e&r<{*eTQN{q-g~?LwF+*NxpT(!tdp`2pDk z^Ox)4EF7tDi;~j35|NX?9uBFxyfXDqa3xH{`Df-NslP|K6$xhn*wO*7VTxD7)nPDz z^jpeaoOti24xd#I@r3M~*StY!L8;h9T?Q&h6|iPQ|8_aLU-2lyFYVV(fxfOg+T?VY z_NJvLK9Ve@!kN;F8(LebyfsI5b!v=2W#WhbV>GhYJlOxu|jf#JH>_euu) zzSY0WT0DUd=&EK{+w5(nN9MC;gp)i`4WrIt*6CaRoUTPFo z3t@+j5Wq;!cjFx}<=Jpd{X&}AK>Rlwtf?^IG8==udETD2V@Ml2Wy{9Lbam7z@*79? z4XPo_l*sF`&#W^JRSl`sW)}B?+k9sR0~;)$6Qdd;@Kn^vG<50H6;-)I`x+*Dru{3Y zHn`|!nhEl=Ovv7dg03Fx!o6=L_ioppy$hhQI1;; zd&O#nNLthywl0i{(;;J#ToE6h`?Bt(s;h&DTWdV9Fi&b+wRo`$W^mPz!i`a|5xbg@|HxAjAZ9ZAJMluR~>USesI% zG@jn$G^`Oc%odBn8W+umkcKi>=c<83zMYkIdm4F@FCc5c^V8bW(tpBDpaaxL1H@m% zo_sD6X#{OE5UhwCL`9&{sKqm92nY{cwHxyTTq> zOK-#<94bi6rEq*bL-E`%w7e4;g_H_p)_u}RGvIeBrVrX{$9VC6{plG&53TX#?}i0M zDi|kA8?#jQoRp*j@yUhZDM>Dr_4sR3K7+>6(2*XE6C^mc-MGb}XD2bm^l$Gnz{r!p z;ii`L!@R@s-lCvE`}akUxb_YeVb`bdr`rz4w`R3i@H~-ws2ct=)Zcz_V`~L!mj-N% zJ1Mz-D=PdDyMH0dY82GmTN&TiQaelLq?DY;VDXB2I{YreUV-3k&S^a*DmN{c1-h!t zRndu=byR^MZ3^&?+2+iIa8o8X{tgFu&f<;GVhXqVr{!FS(L~nAybk3-~f?qCu*hAKyjd=S5 zcuwm3C5P=~mw{9(+KW0!@{!G}N^XDNbeag6@GqCjyq;X(f9iCiI*fuYxOESa((bO0I?ayy*Kn`XQ=?KmmQO}|fc*PQ80a49Ew z@{o+eL!_$JdOE(x6b6t&%HN;kV+b0C;o7E-rH7;Dt$xsLUF0mut_UfVrXaeLyie3W znDY&qn&JBfzQbP$z6X`V@dPgoY=$;@2ns4&H-&t#U$+$Fz-KFY*l?cWS!dzxPR~$+ z3)tFc!nUI6kVesC41$}A&aGqXW0AQCOid8oIL+&woDqg@5xh$`f0V}{saluH3!57| z{mBRHEb?6k7ph0`{U`@i!Dcg)&pX;-E6B{iDd|b_!*XqYxoYt%s3Cuv~(IN73wGVGB{KMiX9_*<0#7a@sPs z5P&9wl^g7iT!9?9tLu(LuqD?H7&6m*M5=NMD82nDYKfeWMwZWn$EtUqE*HZM4-`%m zBsU_=07Ff9ew*`d?6848?HRv6yFT2|W!3NyHhu#$B)bTaU|oBj zz5oNIc%}}fpRf(y~|#N1j%!)PYfU`Q|r-pkW;1Tvy5 z7K_V~!-S$Pzdx{m6>~L+%2c|c(2S7@3|kpr+P6j(AcAY?5nPJ<(70{RvHg2q!Sj5& z50A0w>C}5Tan{jAtv-3uSsa;rOyycEuYwFR71GvXfsUZa10Ro+Fq{NOu41MBcCs&;Z|{VKn^9CUb;9mAh++<2VYqu=Z4F>u|hQZZD=b5pPAFXHAqjHpn*5tE5>oPk zH61XKMfZw?Z8g=~-}7bClE@lw5Z%@hmqj0m9aO0*D>ph{wspAykVF z_uYx^5i7v7eX>Jw#85WFh?v}^0d5{d4jueL_ruDMiB2U_9>a)$#6Z128N}BHx1N5m zpezedkFIV5P50V=^;h|YYU#q!zBzwhO^0u5*YHCK~ZvFx_A}lB1O+GrxS^z zvX1elV@-+P<@^8v+H*(w{vBnHiQ#`mh5lMw|HGr_e>XwlubKKkcmI#@H2VLCviFa( z_=n{5n~CAyAR;D){}CekxAA|eQvYQ$M*nTr|C;50L1piM%k7Ajg`MrchlmFMDtpA@ zNdGh_FU@>`rUBiuFxTJdg!t4$<6oWhOJj7?d472j5fd>{k=)|GQfYwU9UqrA8a~Pl zb?e6S;b_yHG%C3$5l(~oPNIBoOGYZjIElATtJa0*|Cz#YWgH$MmBrYo#o6k_3TDHp z_-X$>&?_#;($1{zxqIux;6v&+IAcF19erWEQ~V{Jye}BT6$r`vm3NW+|nw2!GqOc zIf8b-ewpfu{?V{Bm|mt&{bYAQBqHB%hannXS`?gFRkO6y&)b~%*39Jh!?G1aGGF6c zuRunW(wMdR9PJDZ#SG(nR<0-3LdGhJeg}f=)(R2|mXInNo;P+Tn##jyKzK`GwLA9_ z5+wAOP=^SjwkS;X!43?2^zS{xiRJv#$1iiAuVg!OuAG_>e)zyL%@VDx$0r0suztml zccyNybZ>L!`ZtsOs~_`sU?6MY3Tr)7jRxmo*&d%EtgnyfH-X=FS%d z2E$gvjVB9x;-4R*>;wK^Q$ahCNu2s{KP5cdHW9#(L(g=$_{h^E{kFPMBO5t217OMK zu>8wJyVm_(dHdU3p;4V@X(|w-_vq+h-P70|ruqJ0}5hBuzLZS^nH^$OI}fn=*h_sn(@ zRzxx94}xv5l*&k24rpfNImb$J! zfA;$y&X{ILYE+r5c-DC->liF^OG?#~E65#TU#thxlEVpkj!(@trx!KvlnkvuMcmEX z^JHV60sZzeUdXdsn%!~)c9_aqX z_HJx)kc#1Si{?)hd~(=nYZRz{b_t2FK7KehP!RA*vDt>z)6dTfg(Nh~6{0Jrz|gu! z!^Ue|im4$J4#FnhBGAsG>S!4oW&K=J4ku3}M^2`T>6>$}ZJNQu!M%C!iwT%OI>u=75Py$UQFqW zl^cg21Rt=rGA~#ScHdC?v9JPW$2@wsKl4vdUGObx#hV^`2!sVJsZXxzShJ>U4JyZu zrzp43N1X*KNouUFX~U0pRe1I6irdU@oCdx7`A5&%iG1NSWO;M1RGYaD%srQjdLln1 z4k)OVFn)*q8X46r`D89}#w)~)?nE8Se7XcC;*}v$O1W(Gshmxby!aB9E6)TZ!`Qdv zj5c%gwKQ>ouBAisrovruVrvNvQTKFcqFFI+$HNwchj4@X6j|K1d;>roqx*;vR+3O% z5>eC@=)?nBTT-SJrNP%U)#Pmulm@)dS=8$wqqfE@Uj}NE#J?2f=blk3Qswhn zATyvpcM6V> z6o}BzfImT}{HZ0or|DkiL7}ko}6{=3S zsRMn=c5#gOc~*G)y8k%PeNgFUG(j`|X%bxyh1a^I9jh74A~3qWtBx4p>Qs_xD@*{j1O$Jm61f;JOXL%@LTpvVqIA79l;t zK(q6fclX|gCMHkCy~ISNfavjRies*^ zkSHDU<*pyBLWaSH>~9lbqRrx46pr0Orb9;gH0^G5cw=3HZSg>|&yCCODhO77>w4du zOO-vb=Pa$l9_p||&$~ldv%<;12c%yy3=w6&>qfTTNeez(p|7J@HDkpCMJo{dAsp@i zgOsS%)Ku=zbl?aKZ-@{@3d)%&FC#~!b}08eQE$cSs$mWvG=K|-7fz2IF@#y4y8_73vsF==A8ET5sO^SaB%a$lE^$JMy8om7+>d(&o;U7IYZb+M4- zaZ&0UgmDR7RIf|oIVL@my_$4PfO}}(VDYb-sVi5WPfIa%Fqak?xEPjNBd}(fIQ=>q z2FDKNCo2;@JehW#^sru`ODMbG02y>|>F;fFWVh0Az|Ai_tBNHN8=U)pRHRb1w@w?U zb2aCHq+D&hMr93d2rg|jDGtc{7m5NY4_QV)AP5rwzBs1Sn||k}K_@7;+dVRFXOzpv zH<&WV&I_D-FhQxTuT_`)_TRi0P-ls_NAv7AqwP6@xw71#Kc~hg4RgweNH-UO(V?m} zNI#r@VkkpDXpC}oKd42H;a(jQddY}qNy0zHL_M;|Nu|pEA%|d-Q*FPnV7qEA> zb{dm|U%(*?;p6NqZ;vwm8_P4~^yxkBnstWZG|n!yQ6C=5CFL)>ij;u@jHQ5r^(?E$ zvq(j!AhY*%p$duUgHkd2nx%S&_WMsni>m%=yn_{l*+{bceDdBh%tltsus8#QaCr^z z-~dPxAkl!UCUNe573C|oCB$A`HoS`?1Vt}0)Mtzm^m?L~IBjg>%6h87N1cbU@^47c zv5_+sfHNZ-LReRO3aX?Wyvv4ApvfX--4hFb@Hv^_n?>4ori}RO*Iyp_*})*8T3TK@ z?B1FHODUEw?I;=HF{!{pwA0)a`|l=-Pw@=6l@@5Ckh=T>bI2>zwpzQ(2;! zA6 z9PauEre3OP-lm^D9K*Sty$)e)k;D^>+e;<6a;O{Y&2VgiPq= zov;KwTy7}Hsv{C@6$2WfId3hz37u+4{PclM+`ElIj*YJPcD^2>qTXJiB1C6n$Q(>_ zBR`akVahnn7e&{M_pDb;rVgIyaOst0XM1p3?4fKAU~B}ag` zw(YHr{)Tptn*`JciqIiuDTym#RYo@y^AY;erN&zU#*H9B@Mxvd`}k&}`6$M- z^PF14<`7kc6^uZ9AF1^_<2wHpP);aI9xb*4YrcNEf_lsD!gDJfwyM34 zcFL;uc8Ls{)vcq3*iSS-F}aD@GP8J`VkmLh$TBnW>G~$O?(&6V^oQb{&9n_PHzptO z@lY9AWYU57D9}ih%9S7iSl=~b-s@A3n^ZMJhJr)O5E5-}%UdI1Tvyk|md|W9X8OZn zpBE)LB%YKbFEm?~!x)_iP+>3$Bp4Z>ICrXnMX3~#DC(&q#G_8PygO;kw@qu9+$5zI zaZBPA^)wp?k|v~rrZHsViBMRj7RqTc0^_*B+{`S$!X)(IwVY0{Ik-%78p{ios)6qo z|4zEZ2*h9+Ig>3wz!LBTdSvi$F0dq3X8ms`#U5baiGvhwO0@TYADTR*TIIf)<-S`U zK2js3d3P73{{8fn$d2RpaOH1hvZh1PN=Jwx-lW2XpXC#7OBA4k1{@*&0AiW6 ztIWTU1ePw|&9^_-tpcr_Yp)QN`}<>)4^l;LOuqKvj<5Fja03%N*%PTyECoDhb(7|| zTRU<3X98Yf1MOGzM2(j_JUd!l+3DbSy3L!z3-q^_&*0tT$qqj^h@0LBWfz61AHWGF zAVmhvk}SgtC@4m$`f?y8$YXVyG-g~(L`Ydct}3dwRAh~Air16|k@;le8VRj1c(l5c z3aDf$LemLYv3hClZoX8dYgPDtVrlY-3Zp?&y>gLOPaRb{ik|=@n~H+__4}pM8p}(% zV+ms1&xRic!`rX}#wil?`Fi3&YBbAs$J{W-;nU=yudmK>&G6;n zP&}Uu&+J#XJ7`cR!D`GskV(^wd(uS9F=}5L(l57Fda{QF?w3ib*Wq0sy_rhY+Y*b6 z3~14iR(m8$e*c$+`4;!H5hrz${uTe?%~TvKPN;Vov^tQXiijg(am`C0HX)UYu}rct zQ-QRn4V^DTX=%K#PRZtKBNh;vf;E4ZH&tFbW$c`tMB9@hI|oSvyXX(HWVY{UjOu$v zFR}jm(ZwiuY1R{ZX}3{Mcht_+!DAWvl}dsdBIzFJa+0VCoh07{5|FzTngaB^Zq5YYX~CaIu$Iu?-mJy0;d|H1#;`FpYdJ=QTCN^ ztn39Ks4V({l1yw3V^0&ia*zb5B#0y=FZ%n-;mA3TUbKotEuAf;Tbp?vip#|8bXTmW zyC2t2THlbk;s;Zcy>zHc%GEg!3$tkzvfZ+%*zR~6Vq}tz8}X=2j5sYam}^b2tnqX! z40H9=jaf(~XkZFdR^zVP;Af+);TYV0J?6XCxYxMPHc?h09{WPB2Mb`t4_@F9D;R5c zf=nCHPVZie4|WG_7jAixZNa6(WL96qc*zjKl`Au8H4mmYtwBj40W*5jpC1d7pLF;6 zLqJgLM@nV4Ie2(K=jeA`qV@ftku711v!6m?5DtqUZe)jJx&x&3Ejh#XEc zMmqe1>q!U~ljP8K_z&UoxQ)SG9Yeo0OrP1Nu4gl6WmuIE(K{&f*?wn$yA9|K>6_y? zrfmANIvEDBsy;$kwvyUj%xurjfH)HpN;9|;Iz$I*X+WV}%u8+uB!;ik;UJnMW`sLD z&{FsY-CKT)!1i3tLc$>9Ue~KzQI+_o&#mK1e5o)Oxx}E*mc_Pf9Lo1VRSPo1Ix3Yk zD-8$q1E8UzfMvs#8?-6s$Qx3JWT4%+Sgs7M*^q#ZK{ez&31V6!+vb;Uym41aI+OXm zL#ObYnZppiUnijOH%pD+=5erQq}#nO_+q{wA^&>#?)%o%<<`6!>|r%j~8|2uBqtOb*^i1 zVKJ~-8s;E%5b_rc#M&P?Y%A6zW}G8^`b+_nGnDi`Sa5S}yaHLAIdz28GJJ4gX9m&| z76|5eH+)k6Ny*_~fGRchTMFF3i!1kW62fCJGct?dbHM`N82%GN+GXN3)UoOA-uVIE z@==;x!Yv+6l=^sP`9<-VVaa8Ds$Xwv_ zA`8_dYS@-9!i(qo>mLu!PDhd9YK2ofVMPC$0Ol5DVjff=9Y|%GegMTb5{#2Ogk2~|71fKU_`i+RlvsWA+F%Bl3h)bRrA1RL5Xh%^X*zd}QA|FF-@5^=S z`yUO-@7a8Y|8AAx-{FTD|Iheg#{Ud{`2Q_3%<`|$>3`-l|0}flKir;<>7Q5Z-wF$i z|3Zct|0iVlpX2}K3k(08^?%vI!awh{f6eayCPsmS<$owF{P)Q4XyY|H1LV@$Qn2@f z;$LL=E+7SyVF7Gr;=5ZIDhNff+`}U`B)>yBO_8FST$X2xo*(zzwQlt2`wR&UFsD|% zYY1V|;0mo1X5#DVJ|`zgo?ghTV*_+~{<30yb^g%U^JC|uht5FbsQA+12S59zqnCL2 zlF9pbPsY8c^Xqu~mhD@SJ5ZZD;w`*O&9FyUjby?nSwA)p{iCeMT5-zYM|V+*i)TxZ z`sQybX&1>K301>KRO7ti@yj*5synn-ouXJ9$Iq&ulJ?jB>rEddW!UO$^Xz)4O^k(zQ2Kj5Iy=;i$Zl6`jtG<~Mg%<>^hFf5mW5<;ltXsQCjv7Q z`*}!h{de@^(mFaxK}wQ|;lC{ybqQ(IbVR57xb9JHq7q=V27#mdx zhGE-M8f6t0=@$k%^;0b$M>o>tWfw{LM=!T=T1 zfX>0}83CrZJ8#7uFi--cCS7J-TpYH*=hMqFFD0H2{vzrD^pu2qMFE~Nfu4rI%_uTa zrSVX0pKD!4x?O zClXmQNITb0(KfLKwV#5ZRBS{uUCV` zn;!UfM|j1&*>`9kIOzc8l@wt#fk^->w%Hgeykz-TrtN-c9I9Q39boRFJa0cDEDofj z1f1wyVr#W8k0?l42-&oKZ?H7av&?2GwPEob3C(NR9&$v)Y zb8B3;ASLDCL~Acz7g=m7=~?l2Iqy|JLp1SJfr>3qPmT-HGyz1D@cymT!@>UKqw+{k zb;Q@HK)#&h(0QfE>K5*mG8K=1DGMSfOaQ@v>Nq`|UJsTYu`H`J&ILT(`R7ZrJ+Vd8 zScTVnqBVPQuI1$3bz;XI`#dumOUqr_nO@XJxVw8~sfjPrfSoon6hW~R>79(XA-yus zlM=CfBR$qgB5smZ`h8R<=zWwQ7@r49Y+}GQyi^|bX~y##F0K4*FSt@rl?o|1;jsSq zy6OA|xzcTBkz-aZNk6OOXa_4p^(gTiUYIpkY6ne20;NDl`9?YU+tK7blGS? zeT^i{Vp@H>x;<9Ku-(>ze>?%<@`s*zgpO&6WL)8_`EOoO?t|TJ(`y4f+1NhU)rPL8 z?Q{jX<}=sZN$U3oJzgcWt~^0#Guxo57_Wyk9a(1uuNg%N7fQLc)_M*K8}9W356|?6 za=H+3HU_j}?q}6W3UsPsb5Ui!PD-`GN|d6j zxgwa#gsoIxNyZ<96hO4pc>Nw_Sd}LH9Wi+i+u&ED3zkLscBI8Qlms<`L`tF?soIgCdow#Db zpdF<;!CRI!^pvEM6a+ptrn!1<90Cl2Fom_l%KnBni`dh~&MPGa8`-k*^UumaMQ6s# zB%3{R=~?ciPD!3&iXT=Hykw_WFqfOBP65j-?~)rV44V`acv*EBPvL%29XEQ4U11!Z zBq!BA@-$FGdnXyfJrUHa#nYY3sLL3?+;9j%gzH*-6WlGr~~GDp(|y+0$V$A(m<3dj{Ob(V5@ z4R!jgL8g#NtT{Vk%3Yfo0t5G(Y_M-cD+{|Y9vUVf$*Ip15h_@|il&}GEUsE>z+ftp zu$~?qw%`@xg@B7SSRk7~EfV3Dbo-y%fipOmQYP2YKfMKxWPAncB(xAroO5ssKm?iC z5oRD>GSJ#7;gj%eO@wYGzOS2K#bdGb{i)#$W z;MxgRe@QijzjD)*QF274oELZ9L+ME?7}JUtsMvFXuT^-(Pun71nKg=UfosdjGS*=2 zLHwTXVb`0z~g7;z2&{#MBo zYFaE*D&7TuWOii$qgC;7@|k5wFE}0v!eo~2nt3-nD%B$?yK1#8$Nwe2Y&ti~Ez7HK z;{;pfmNx0DMZaQgx;BT5t5CzMrZ!V5r8(^lvb++Px5df;?8&7HyrP~-ZMe$P9h;ww zN06|q_V}bJI$lk<){nAIu%3wm4v`l;@Uh2yO6 zwRqzaH}SO~8cyoHJ8@1MtcO;jul^(-P}3HhxyGxJCsmxtGYF#ZGx0E3RqIw0Ph_0v z9v8HZo=wL-codr%HxJsAz<#UJ1y@~GuhcSbF#no}53t7Y`RqYcgsO0#&NX`O&@fD= zKC6u9sow5mPY?olpvGn09F0wS(hL~9U&PRfk$P`PUN7jVK;ch*pZ*LClrC%An~YMv z@paGcml0#(-o;@j8;DS(t9wpg7xt*s2-^K){RTZP6=@4+3u9vs-J;RS&uacOVTp2xP?XAfo(B_Q z-2zEKN^~4{jHzIL8s0g=!2?>SXAkX&4Kn$!1>=)zyC`&T6~e$8t;J0M+_FMfb-EQk z?wz5uNC?C;#M><4ucHte_%3f_8=wJHy#cJhuT zdMoju;~21yg$ybD+E<;4d)A}}PE{>tI2v8gN;$xYa{r`bBNq)nABUbQAUVVIPph8> zZL{7Ki9uc+f==*g9lzQ<3KSZYN;m@l3vUo$DooH z*rb=;+TE0#j=KgVQZc7O{ zK2Dc(!EnTPQE1#yHgb3gg3}|5R7o!q%{(h9DH5sM3CW*lNc~4<#>#>YB#4#rUkDNp zeSZ~QDEGZ8e=UI3m!18c3;5aZMEu3yCSTVq?+A=aq%0sKO zpD@^qlLghN{+2gi!Z9U8tHJh3GrDSgdUXh?wt;#&z(^JV1cX(?;FVK;B(1 zC6N^7{2RcTmE|?6aNVdPIUICH=k5E#n#x*eYoom|rYa-v>NEZ@1hQn?y?-wGZ37w- z=?ppwzWl54GQP7|tD=TVkE}n0N26!UJxUxqR53Pk@3hXVOu{h0I`Z43ui3>NzZn(& z+vd!vS?+T*qE(ZIa(y;p5C$rbvh<$Gu_XP2shXUvr0d}ZOr9M*rW;2 z+KAUWpxHdMf?6R-e(g7#iE!sZ#0i+b)x5 z_)zHaK<)Q>wYw4Vk#>jSEV6lr@On%XR5zRg?g|u2epCEJ4p`Eyy8f7V)jL8?^5;Qu zBk=YsLXH|8Sp&)Qhey$}^?F1VsI(gxzixA93T=%9GHfk`lD~uIDp~=cKnKNoujcrP5fS>_u8tNE*0k8?H!stQ3OXN5r~{uOxU zR^eCX6>N*-)#k9*C~_X`;~MxM3d3Afg?wzd(D^(p2F67Hyi#fM@q}IJi%h{dgn*{j zQFp@g7kH;k_d6@9V;MxXeiQ9h>S>-2sl-P~+Axk+23kVH1ci~a`uK{r~ z*%Lq()2`xq)u*E(oLWRjun+(%c6P4w9Y~O<&=uLj2$?sV(CXQ8_+IOs-CA=X{(O(p zZUUGjZM&j#CRL^f8SU{ecBAA+g*$Lh{t51t0O4N7*Kf}}HQNN-)7kYqXGhKwpJa|^ zsAdlI;STXsH2yHiS24sAg6<;eMsNV$T^9^>3Xl0ByHAoDG%F=FF@>}I*4!kV*Zg3w z{c{06ej;lRUv*xT!RYhtMylvOJHkNl->B_oDvJUb(fC9&4LtHT=jT@e7(&=9;R05} zTcUKl@cX~MQ3OxehTj)RDJ@6lH99+TYXRp>-4Rc=3|2R;;wvbU&%rEHZf4P^=aM&D zyN^VX^hY>798-Zxt1_lSvnE455T{c2j|aL!>GKRZae}V(REZp6f7RSTcaK~G0Q&Fq zotlVw$hg{wG?tkJ(xoA4C&w7u4$lrua!5f1I9WXuJG>1f%8%aCyX3^vdP-LmgD+5> zL(Fq6f9Q^!XuD6|9Be4QI_^W;>?LkCy&}E5jvvV#NE`FYcPjVL{*B5Y6>IK=fS<5O zAwD8)#%iAEFyIlmm{c#(!!^A(7!}p-@g7J4CEj2T2;!0qXh>3H*l*SClAF~+&j*gT zx-$eT05o8h0?|O&)G!yD@E2_cR&HBruFJ>v2q-86Y@I!Q@6{6##`YU(O|LB#wgz_z zawzrA=Qt{GwP9q1x&T>O#Vv+lV#XDj1|rtlR4CZI0b&}3#HvQN88XtGJv&%~qMd8y zbPTUo2WITvOM;Xqewk^q<^#HEIS5i)_+#dtb!?&eOv>YJ7IrSDXY)%oa@q!+-hkFM znAETvqi+}qyOxn=nvZ0=Vc0UOhkpASf-}LZvn&4bb8Z`heSMU+ zh=e{WqQ(b_KI;`xM5@D!Nu7#&g4mt*h(_i^N%DXzH8*@Y2bhZO7I#qll*#?LN4XV$YO?RPf$(#Ije`Iw>~GqBwZvbCL%;D2X3qBRhB~FfN&NI4S-Dr8(RIaW6OJ8hH;o?hU%UQ^nRec zCds1QqOiSP;Eev}_;V{o2}I=>TH{C{*I7FPTjNN?6NFr}!ii`M48r^_KpKX74LYmWsun&{ zT6(@%W+p7Ny*Cd)X6PS)_wF|NCz+1nLHHL*woE;W_-kZh(3x8JiPS|>{5d1oJp$$Y8+hp(`_QpXKq;@FJd zgPBp$B@R^4nwtSW;+fpiYHTLBtYc}M8n^pk=1Qe$SCU;e9c+W|S;cH&qKN4Dnv_Ah z5b&H@A<;~{_uT=L6_jG znKD&xBXa})DW2@^a0WyQU!wQwCVAzno^rjL?B3bo1h4*UQ|SN)aD$YI&BozsXh7X^ zhFy^;bj@#nb4t9;K!%T5Rm0a2?-yNj zO;H;C_&>>OX`l{a+S zZ199f6Pm4&dWXj<XIaT~<54V)JW-#bI3IUOXFWNQSgr#hVoTBu*jmBhEIKaNUQQY)L?UwQPp& zRpUOS1{DCK3loCjTJ}7^#Okp(5bIu?J3u` zDteDf3fPK}WLtg>E>RVj+6_vdVW%6(N{s zi%t4gAxvDpXQH&h{FUHf#ubggfcK|y`phUe)FN2Dlx9x?20~HQzsHINh1Qip9+zGz zM9w=twQPq50SO0Xg~|I4!zeg7$Tar`11W!o6zS(KHZ~)rOVRT;^AJ=9g0M}T9USiA zRy7`~m1W(-9}1zGD(dY~XjVVa5$aTG?g2fyUGO~;1v5buv~UdT!14+h|Jbi)k2CT` zo+I+)&@=2L;P|n?QUx}jOdF70#&}0TNf7&?Q^N$D(QNXk;t8VB}d_Y?~yYx9}5$9W{UZ|UrLWsqe{=oU7h9`!PCD?Ib}}S z7btI|-f&|04AAsJDY4j!p}Jn)njSJyjc|b2iRaOCqsV|a2R-g~SK5673yzmtWr&k} zQ^&?_)btd1(P2aZp>E5eBb~6=)bFAM=0^3)5XF!Va%Cb*QL=P`WqISu6110vy5IUt z&Ia>vP+Tra1GtFb+ z6?^Lz8T}rp^lUBqu+@mD_HPur79|t*{)T{!z86bcoC;p!X(a7leCl`VD%ctdJT%eUk-9yKyTlUWO-$SqP)-+|CkOhN1CY%8$4doLP#? ztoxfq?T%4vCpJdQ=;aQy2DXVap>W$)H@hMo;D>BJ?B`jkS{nfbqzg^eU%Fq2xw&La zH@dv3`@46!L1}JK^sUK||4~_$FtBPa!2xs_PX2RL2!cSQARyJuNO_Y+cCdFUS8s~{ zsoD9nFj?=531hqkQ2Ff((7 zu|hYT3Nte^Gcz+YGczX@W=<;1%*;$x;mNChJzvl0wZ3j?X4F6CO1`>2w(Nb5edV*Z z*V;=L)GdbnWMQl&+(l0S0}%*C`?4303EIr3#X(u^7x+3Dxc~J@Q3$^5k8&5W9cka4 zsnUAniz*fBp?teLXnHsi%K|S z%??#08uC7~J#}Gdzb!Xpe1l65%~9xzCn>2RVRIy)pi2 z1YrI%hc4N!c8aETc~Ow&k&eK%d2&g18R1SHgDkaoE+nFa|IjaKVI0D4YcyGoGUiO0 zDcZz_vkG_K0)4M}ront>?bND&+V`Eo;*0Kd0{_%ptRNO_;jY0H;I|4r@g3p_h5C+X znhQ73kUfVwT#spO-Jr7a2<3|YQ9EKy#i4R>Gg~LHq3Pvq!8hmew;L($wBmOyoP_v% z$iyo~XfDgftPy=alNCOtDE!AiMb!v|Ywm8ElZUbrY^rz6Hb0ZAP9?OxmE@1bNa9`pt@5&${#&-3Mq;}o#3eAod z@o{t67o`tGppL-Rcs|Noj3>Jq_Hqq2bzc!Pa|$?$VR@)|p5zwaWl1(_!n^FN&BOc* ziTde7DK_LsoD{dzTZ}NkBn{dca%& zL9h^m$TWTbtbT5kjDKShJZ2n(f}cU_C_H|&ddY%puiukbxeP+lqlz8u`Io=1V;I<; zZDRhN>#s=3nBk)QO4&;bgxAlo+7!GNeXRCtZSpW<$!0cS^3e%s6E?P!cSJdFvD^3p zO-S0AA)vs)7|MieHVP(7J5h+&a;Evh4_d}J2#{zzg7;_Mb~NGggXKay0{79vf`+mW z;~u-kCe?2Z+S*K9N_)-Gl7^{M_can>$yyI;+Ct9JCmuOeUl>Y0s@(v zkwu$$(RIundY6+Nk(h2HdmvLBcUIR$S*SCDo6!G3R5pK@yg;d`d=yUoz#}%4eu*8_ zHLwjOrz(cL@R7MwG7J6t+es)(tl|u_o?6rBDcI}rM)k#nvYRgwOmMy+wC^j`a_ zX5+2+W7-R?8lN8T_d)LwT@zBfqgFcd^PG~3c}~*md@piY{F?zJ-Nlrwg69%%YMYle zQ>rbCZ;1q~15R(%&5dz=^_@%36tw7ZmqoJUv|8bI0a9V&C=@$`Eu{*|Z23kuo4=|O z*!+BRROxZpvZ2Tn@hC3bv}R?vRUGlx5P*qG3+H|cmbF7Z-_fLP)Mdn< zWD_iNb%nj9Zts0OE^lLiRbPB?@j30o&7o$I`k1IEq$;2iD2iey zF>{jfjvO>DDKtq_v(q1*UQpr2a(L9vayYbHh??a?XHwj}w7CZks%&Uc|Ay?dA70je z+{Io3(#CDx#?9J=ZCxh@+Q3!Rv6J~))64FpyYh=%75W_6y2!SILV@xib*|_*$ZwV5 zgQlBvde}hjo*@a5)}Jfwu#%}0FJ97TYU`zWKCna!*{mUQ16O(@F&`cPnS_i!eAG*^ ze1Ad$YOK3jt!iopM;PGP=>(N=@*K;-J&48@i?RqhGNnTt{A2Q5^wilxQj7(2%=T%g zeM8duaiKt}#D#KUNbdY%&fIV|6c^PNuZB4h`C=s48@}>pgplH{XwAv;c-D!D=J005 z`}1!NL|}K?3?HKXJTWGimGkqg&7};Mw3G~=h1egMHuJO(dUmIN}8! zX^mJFG=?*hTr#D_N+4xhnP4A(EywD9QAYDCuQmQwZqkl*2 zAK5HT0N|gLQNaI=GWxgue|*;PZ`A*c@_$|}iobW-|2}ER#KQg`Yf&_+O2;0w!gnoG z@%H^X(2IREUhVDYQ*~DM1L7d1A`B%|`_7sUj`1VXsE+^cJW-68Dxtojk|I)XX5L1# z;Okj@d+WvP-MUhH+z0nNw$wRozQ-BLW^HK^kx0st`P}Z)>1AH^-pcm=qMsn-!Rn1? zO`PlHZbbLf>vPhaK!jN37lo&GvF-mn*QJNoheW8su@({n= z0h$Z?l6}OYk_bmmxXBAy&Bmo%Q=DqH=h|>lHK$5IRYv)SbL$KrmAA~aCN|8O zUDt+W?3wiKo?jwMnffKGFEA@+QeoPJP+;qHYv{kP1W^VEPaqgPB3qy~r%_ayBEhOL)9_)n)J1~OTF{k2F6o_%zae=}>f?QGUP*G}zj}8-spEtHv zhZxlCJ4B6cGhq6@?6Lwun#NDY26Wz*_BS^x&Bb;~?~ej{6zRCiq`x~p-* zt{EUlGih5aoFmEkrW?EbT|R8aQrgYbYbaZYhf3%<%?NoQDe+szy+BO+ub^0f%m+VG zaYOB*{;s^wxH0+WF`mPf6U#tR6ux9&@wFeqHszg437-wuBOpDJ&Ij>r6g?{ ziXkFssh(6(KrAB{j9NYU(0kFV@7Z94$Hl$t)Q+VRDH3v5y9?~%7oU^rR4y?$CRtry z@7cZR&_3ur+mfUuoC@!R@@EVv!hk5w$~}?!qOwQQR`!l_s%nQG~jK?6;cZ6@7;N^z!G4F_u+7-%zmmrB@N8_dV+^-i-+oZkE zh^@l<^IrDMBAX2S3FF4zq~&7uj=aWC>@L6E5zTyd6Qg8Qh#H9E0W4XygGS>E)eTly z%MqS2II}rqI0H^4y>#AND-Z?DMH*LfeCz8)ehHUJivI{w7-OL@U+u@+nn`^4;P6^^ zgJz4wu^ADfLvJj_eb>@F-{0y(Uq3Jsa@f#xEXbuoo}qAM2W9$RkowZuu6Ze`akmx{X#`_rOnhiRURJz_jLPIZ$%!loj{%KzM*SM%dxbyVRxAamJ=75Oabv}I$)tNQn=tn%xyk|@ z5-T-V9XCXwl*Hv7sY-op#7$7o+lBWeNn`gBR=zs|*lmnjEw8?=Cx<9>_xUAcswQ<) zPXyHz0Txl9u=97P?p&U?UEA$9cCW|9&{SLBmtSnW8=o(=xgokAue<~FK26uh%j0&$ zQLSW~v^F@TtJ)pgJ-ph!nJEFnTRWvYm&nSjci;^qw)r!~njxKEnCP&cW(KL@0pt)F zg#R(Xrn}XqQKwd=h)YBbi;fStO(SRH!G44#Uooqx$}^)RMaMdOFOS+mk#kX|B&^v> zK5k?gL|ECv()hQIi^aN~DcujaT8>vk@!N{zW5;JhQ!1hnK)JsoJ355Kv6J4Fbws5`$%C*^fD-n1|8K+`^iqmF9Bl39<&P+G6JU@^49D+b#77O^N7F# z{$BOZwn1umaL{r^gm<$>WXU=V!$}m+{_sl0Eb7%W0aVAc$Z3O`-w}8{V=%Ts&1C5N zqo|BQTv72HQ&|QGm&?XaR!6t~I_Lc&;1=x1%tK-AXUG}N&t7^JmSp^4F@kVn$(2up zjhylMH|HMu*+`)L0x1;*c-k}(*kr?GkQf9;A_EG@g%N(*rdSt(Vy>rxa0C%hQu?8F zF%v@D{Fl@`#e(h~P$Z@-VJmbO##LPf*`|WosKO-39Ib=Q)2&NWmc&G z@{E927VOa8mMkTMAcJj3?V22d`P3{e$3%gip&X3GaaQ5yi0fxsDFC92T#_y|Q)HY& z=09?K=}%%nE;r_Vur1+ z*F?1k&ZHHd_^o4_6D>8p6g0$IQWX(*M~2b(`YJ>kbTbHXyN7lDktHwmjpY_<^*HI= zjw({39gW2Zxbp#<;})bnni0sIW2(s7{3XIoS8XoQKjR_Fj3y%Zr2&i!&*o@hndgV) zB8|g+SVnKM%=gjN0+VbRQVqeUrdN#Cu2{(Ed*C?;fGCa|h65j}j39;sc1e;69&1(G zjM$v8;Xt6%F~Rg|9U+b0w<@>1HM2CW6K;7YD@Dvf{(Rjve_`_~4>HA_*4R40Bps zU%YjB4MKe@FjET^O~B6_RL>ranLE4prdd9>3Rh;x0uSx%iHg^qt;fGg9hgCRMP0l# zkuc&r2^0gVD^i)TT3C9p2PV%9q(Zu^(1w%wRNW_9A$JvtCspoBxu!kBJ%RE6OtIee z%A&KW%f$Ifq|Ul$1SB9)=5aexwV7=iK%i22dX=`9Y+GF5#eUN>TBI&PtQAg7CuAtj z8yN`Z`n%IISbQ~U2&miY0c_=>IW4rSQk52e#IcDV2Ge{YeSLBf6vIG&_xSV=d(@~*m&=w(W5eqe8| z$~SDGx=E1~kSJxllZ1Z5$Bp$FlXsHrOmIpnoL4kJo63PdFCr#R#tmz)2qyd{r+o)4 zIWBcAoS~NzQM5kLzgN9lnlpBaDzKN-a)#c&j3C}sLKH*li6s_+P-#w3v^79749sN4 z6MS>m)G?G_s9^Y!hHn9hE?IFU58HlnB_7kv=gu7#CHef*a%3dmL9_83ry)1LSb9Xm!&$eg*icq-oyi-Kb(IQ+)7(73Bdh?% z&Xs^~jIl57NM?LkC2slqk*HdafFM#8KA-Zo$#dlVP8Th;zF%@H<@soH#J)&Nf%L%mXfBH8ho^u@}5YAqncZn0BFvzeDgNwna;tFe0HQ1&M0oQ03-!~E%aTRTc&45r3wUK(W2!E|WzM)m(m}&QgEYu4YDL4tBTRctq zT3i{#zfmK5T*OtB$y9FnnGgxlKJ=m>`{i_CH#sVW;H-*)Q^wv@TM%MUuZV;p794B#1m-pPzlh zxca-~0*+k)D%dEOzt?wl|MfaMyGl_@26JEB5O})CEUz*7SW()euOZEn-DKVeAVP~0 zm(P(#c%Y2eu&MF3h10w~pa*~>MMDCY{(+XKT|p3w$mQO#iTWN#QA*DR>}UHV#CpVUKu!0uPs)<5M(d3QP zCGj@iA9@h3i0>l_XrMpA$G+#?EfE$Kse5C>Vm}$h28qP(xp373n^*u1uf>QWG{!XO z3ld;Msyk}-Ls9<#Yaxpg@~e7=f6RckhZ(koBH0HwqP2o1p&_!=m&Ec<*4qibbR%Uk zqJ6n^DDLx^W3qbX%32auA>?=;4imQ`~Rf50sgW5Kh63%a?;bgy1LRi z8e2OV8=BD>+FH{a89SPr+R!>W+u7MVIMLfV*cv$-I?)L^ghA$^Xd6`oO9CTr~bG<}2h^GFJ{RFo6A0v~I_`d%z z$zpDm&N#>hb-DRClbNHb(}CFbEJDqAf0V6Y>QtV*L)Omoq${wu*K91M`+M zY0+$)*a6(#z87gHQQ7>&|4Y*0Ee9U%*Ist+oXO9h1ng({PfB$=!=3g!5LxRP2QTeK1R9c_nq$6!`9<{Pxg&ECC`I6xlQAI<|i-qn*WJB#0Cdrs(KvDAGCh z_kgsUE!?|}TSzCCAt$jTP71^Pk8fOq815>O3NG@`U8A7X9mQa|4BY9%>A zrmM^INUD5m&aoqYw1ak6S!7SWIX%O^z51TP0atNemoZ&@$Mp*Qw2gOW>i|cBQK#3K z#@#^taYnaP_4-Qj+Dll~M{h{DI|*_90=mVGwW7d`Um!t!aECgT;Em3PgO2Kg6P@kS zAju__rUmf5e-Lr2On(=_eo>J`;4}DNvUg(gf6Cq&WmFg+wN~LV98rSEP#Hjkog*u1 z6CdZ{c+b(ETG<-}VcFm6NgfRQygqQJc3DD~W4@o2Z4%c2$zQE_)@Fp68N{$r7Nn^h zh3wco(X8x`!B{ZTZzER|3W*CXsK&Cao=XtfdNBFu4b{Th9w`Nzjntq1Up)eHR5ad@QH1>`gav zS1Tt==$cbMwTn=?QehEzNQ!I$b2>0a&{`x5F$h_!3DD}ymEl!)7)-UWTcH^62Vf55 z5b~}~xi9{fHju-b@ZdDp)>!Y-`L)uDgFD9Spl&M)GeEM;k{ddUO(uEP&`R3}MZLZx zC84WzWwUn9;yy#6S)1=5$k}buh$LgA?^;gY)JoS&NGg_VIHuFF=d)9ukxK&J22p{f z?og~1ql*XTTAW9_MzTk8-teWnip~)UQHKxJlzp)0UO!<=q`scD0 zFzlHm3R7F=zU{w;Z+PJBP(wC`xSK_)3xUcpU>7hBnK%bfd z2i*4T$YMAcs(Q6eYgKYy=lZ7B4-4+0+Hx?t?IW@{#ZHe}Ed>U0Rx9uuAu$Uq{GBZqfl4A+G-}8CjZc4{t8X|IivH{0W zBW=6ky5t0AVIH=u^4Hu*kF1Q-AjP9N2Z}@@8CMtY=2y2h=}_{jx1d#eFe}Cf#*0@oD#^8gMsCf#t+ER2mI2b zq$a$TpfK*{Kr`Sb8_bG=8` zYOt7^3l57kYh?^DB`K^SS{Avif#64lh$yw|&ls?j{2gdl2~kams_=}wD=?Te9PY{V zXw@glmwYXLMsgqni(YWBN!+}p7V`WY_=QHhe2Qs=@0|JcMrGC_UC6?U(K;)orAXMt z1d0*DCy@Klb2f2 z!sScKu}FiBtWdgyAdiA-Lw-!(5cUD2FBVED z!89oRioVcc%Gn?fkE7gs8KD&~VB`eZckrYp-}y!QlgA#jnw`Nzbev1y60ei)P;_$# zoq4+nrKlRotS$WHEdp)er40lJ(Os&_-fIKtJ@a>bvK}_$Dhf7CX2#1^W`CZf&gz^ExNW~Q4gDS9^*p0R8 zdU46wPu15+!C6yB;}!SUrPgCvk|#K#PhC(?YOz`M{fLFVJOLgC_OuHWE{y&CkbuHKV5?Vu@^w%-Cc){`Jw}sfBE0veY=Ut8 z?`NnP^$^J5^`-S*89H(v>K=bWzHnW+dnf$VCp*o*Q$A6}>I2@yd9P6Ew+}$_dDy%c zp37BmXLi6)n9Bp3Yg}HzL%yl4>}c-oR{=xbL5tEL4dq89b%y&cmKLYC5OgqqdSQ?h zkR?w{BzZSd2@-xH9Dm+VP9$fXkZbo5#0CXZW^^3T9;a3FQ>LH&PqVzwN=GH$sc?Ml zkUTf>XnMGg+f*Dnuw^!X!*Cc8o8D>^1%3sKa&5g*+KPL%1*Mjf+8wA7z5CQHYC?LI z8TI^b?69sL;mOP*(qFZ&@0&f zqeFxY^hyrq*8k}M|JDD<>6;q=2l>z6EAx-5r3qmAOO*H;{lCZkU(o4q`@dh%uahg% zD=UcqW1%#2a=fRG@cl1A_`_F4^F@8ZWmj8%5HdHljwOWvUwsreN zfAr>abfQvXs8tuCiJ+3+f_Os(5GG$1mD7i1_WSmHt356t$)U|_tQ1hAO~pg#X`9x{ zcA<~C6YUGj-n!yK{c}G-Nzl1GfpvxN?&8t&`XMVYqDhpriW|am`cC+4R6g&(^_A9j zpjhG4!g2A)zV7H6QEl8(d}Fn#9l!27A~F0OQ!3~U-fg~n54jM?8gpkaoFD@tbLXA^ zgr16RFYjXun_B;h?H%t%XRa+nkZvoFDuNHFjZ!=FP7p1BgXxAuF>1@s!MUg?fNjW? zFiu6RuvbUFPV^(Ut7eW26&@C`GjkP@#)f7IaYMQTKDmh6C1eL@Q^ieE_9w@M5@TYr zV9wlBO{kn0u{T@@n9UeOR#XYj`F3L1;A|gpCZGfbnT+wv8{Kgv66kxgc#e%YZu;pH z;ia@bJ`65bY7hPddp8WO;*!dP#A04K6^3`FQ8&k>&-zdrjDH*9>T5P50VhM!K!CcgETL`lx-v~cPQc`q*~70Y8z}khPhRu7ph;=zodDb?y#L3 zquUw;H@2v+QdEkERVbisUK8DJy5qR8VT`VGT&@x{ZLmgZ5qp~2+YF&ctD$H4p%@Ng zD6FPEC%QxW$nq@TwZPOmYei@cEGH79r3-0FMND$R8Yl&t`TXgaocfAPKzRbNuUm;@ zQIME_b37jValE%%R!c(h&N1sw^vQ?Fe;U`YUHn`WhJ~W*Q@jyA^XFk|IJ}X z;iKFNhK@P_j!eD;s#0FVk((G31B*?0R)ew5g`1t#CQ-e$E1u;=pBVnv~SiN-=zPd-#8e(r8pZ;rx28$j$B zji}_V3?P0sEEf`DBU6pkidd)~VIC9Dx2Zt|HP&JkKo_D|i_h&sM?*+g^rF=vkEaz7 zC~**(!mv;xP`5gc{#+mhwifZ(a^3MzfUBJo*A~UM?UA&KoC!|V`d{cZg2l~- z-@j`Ch;zZhwF3bm^OUC13V`#ou~Y|>SgI^~P&1F;CbDhhLq>$h@@s(M{&R2PM%~`v z5KFDntncLA1n+NYyf=?oTMN|@Wabuna-`~pRSPc)>M{fZ()k%ak`9c@XN=b6pvu9M zbmQnuWd?!iyTmXP*3w;V7V$k}vNaxTjTjm}-#MBNOQ2ENF=qky(a)VGL5n(#+Ya%) zYGuj3d57&8Optpx2M2b)-0xYxwQ}ivUZs0I&x>>iF5b-3V0Hb6Ay!{r{ z@7-L^L4vDsRkOp7wm|E)pTL)_8SoRDb0|mH1}OJ&%V@PjZv?Mq*6=T820eTC3Bm|Y z;AivJZu6%*8)lQ2@`H>IuJ2 z4&*(UkC<$!UN)FKVidsF-)@1vp~ndrd7OcGgdKe6g9&GnJKN43{W4A?6o|9%2qPfo zfJM%4N(E?2b4v@2fzwGZ|NKVE0U3WsxiuUbB*rT*B;pVYwkG(1G^!p3CqX!7E?|^H zNgQsw`gj2;J|$3u7LmRH9q(Nb7PMpZn)CjT-GYR-(H$N-N`u9SFR`7zs*o^PEH^gSK7x+s-F6He`|?X%<-&Dn_D6-4XU-v+v>`hLkhq!tI$@6guK@6|64g`}YVM z1_K@?hBynA(xc?!knt%Ykbn&H%0ANHr1h3fup(&V<)w55p<-Ws=2}Co$5vo_qsKm-ROppvQ`YQI!B5RY7 z1wI<276(&gU{k*lmSI(8o1?-9H&!~sc_;@GR@api1AtA;%YY5BjSE< zsDY%xM{6XA!z#HdMAiupB|yk}R_53d&=lY=P~wk}iLgOMb^2wn=8;8^;@DT(cCcda z{&yfm*uTh5zx1gI1f(==U|HZrHWi&hVmkhg+g&YU9&w$&@TrdDjM$WQ2uXt}qsI}7 zq!YFVg*aJ%MUoPBl;5EcQH>{`wErd-M@%5Endt8*_B04XDqL!(`+Hf=TT#;pkK!&4 z1zxO0!*L4rXR?^N5P+$R4P396Z$&Kd7)Jwdtm5!a(st*55`DC|d$-_nQPhq@L)-Kg zZBX?H6;WH>K#z1BD(o_Ug4p|1y8;H^(rL2_ zHw&!rx&zx6Vd}9w=ZBfd-FZfc<$uUiX_*0N+WsV&y9}ON((yUKY^bZu5k?sBgO}n2 zN2{D)Bo~ha>yvT4@L@-iNPSAHD#Fx)cwlD4Yj=N?*XM>IMrT0Ovq-=}EK?TBBrPmx zMY7OVr^b?z35=Z8k`obmw|YvOA%KiWOhf57bq*|ugk->@?ZCb-0$OBj*x$({J(en9 zf-JSk{@51CP>M%c$$%665al6#_;bNT<8JSVh{`uOp|T9he_ytAjbWZ`v?GCA(#gLY z_d9*JJBjl)f(?3R-->4E3gSHv0^v>zl+}XsIl~hsR(h@^mMDa)h=1Z2`c)RszH%%N z@aBSCw>*XXE*=$#!#y4ndj5^Ht*X+x6l3Sacu6#?OW*l_yqRp zx2$=EwGo`d{sJUE8u{7UzCHSHJ+I;K&$v%sHy5jHp-z|{yo!6DpyCu{sc0S`PPk=R ztQ~5XiaVkhHC(q4;Hhj$;B1u@t9gH>SqhP4jii^NSdOG7SLB&KO?uXnX*J4tlZxdV zJ{ALM=EIo^2``tZ7;kr!aFxaXaqA_Ms;L`8iwYu`?j+ia!kk4N_;Q-yB?%cP!PYDT zvV3H790hC6EUtMZGVbV~?s0om6zbKm;53eP%sJC8*^;8zG@f|U_C$WUs<3>gQO~a{ zK>aA4ahp|%71h{xi*s8K^^=RqS@76}y7)0pz~_XrA*#bE-6bhf#vsriO(|euEm?=h z)1@$*B5-Frzl-O7qutszK6Px#a)A}8g)GvTcWoNpg(w&aPk%AF3&Z4*@sg94o? z8HLY7PTikp%GDnCzZQ@F2O zW!f^f4LqN_{D342u?$R0Pm4C$#G2xWM`^>lTMx$sKXo=G+HeyuSgr$IJ=#l(g==OQ z`wBE#chaE>OH^j$KzjpMFoN+o6nQv;lawe#`~*pC2bo(sDvtq{Gyn(nt`X@RzK|K0k{?DN5~nsoYJi)dLClD?UXh=ijjAY>Q5*kF zWit@3#ld|smbT3~XHapb^(PFsD)>gM~kieu^8=30dBS9hHkyHhqJrk&|xy33Z`f!D6sKsgkNBU$GN zP*>jsnTdkwSPf@4ug6J{$A9XyWd3xnln;7Oe=nYqZhE+|B4=(H&zGtOKOLt~ts^5j z)<&(j@dP;VRlHS?r4;KHl(9|9?KwRrFPc1&woqvl$t0aRAAK-{UWzSeXct9Bb!5|I z|B%&J6)gyD@7uvFgc?gt^ntSYrC-ot#va~@i3A%V9Z8Wi5YVo$-bs(3yZ)ovITM@s zd!3LP_oA@+NUCVk;YVVQ`wePOb+Yz&cjM$jhQi*}$pV+a5&_F{OVRgDVh0W;32i>t zVr?MkNoQJ`o*iGF(Zqxc|vb%@3;#nr~(6{>2n_G8x zRNkH1eZx(b3)O~0am%hm``R>|C7L`I?T5nI|liNqhKa31kGlBYRdt2Ol~=N{dg zm!Ud%aPioC>A=p?)hKE_adnUJZ#$PAx8SQZU5CyuQBAR|?G_kZ$#B5H5)feIa93Q_ z2h#i-UTM$$x!72#N07K<7z!cvmdRq1LXakn07Y12rK{0h&6kloLzCofjQkG+I1Y&Y zGeq#C(D||B-;2zakNYpAUM=c0XCD|dn1OUZ zSy~8na_`$5Afn`7zxIYJ{yuLKBW#ep&St$<9cdt_Ma9I^qKDPWt!}xwIDAIHcp~b$ zx)+DS*v|HH^v_63qqaG3Ch@pKLx+2G-~FS|fhirr zl0o2k+>XzRVQ~hWvZLQ=eGi%uB?_6jzVUhssk78NkPd2@6cagWIlOB|S^N+z&%s1i zRr-*I-f@rOiY$hKwe8%9GaI-%QV-q))iDAtS|k5TF{?jc zML7`rOQVzZUSqfkrh=L|Axut_kr}&qAZ(D|@RHq?WlE4(w^TR$RRXdU!jrm(UNjmh zMwbKm%$*pS>>$XS3!mswgf95Zd23tn`{B+=<(4`|} z2aPbTM+4iZ`UD27jh{=H?FIChvY3xp!C~AJuF8puq$XKaOJms_W@9!6Jr=p9H~qyq zfz8!mM5rzgEYCoh54SR4xCJg*332j~KPdk6#S>|D=($Kp8h%?6?i8fo*hFtYq?#3Y zV2zLk!xC>=^pf5XH@Hyjgi#WuiRXJ7NZTm z6vqc21Y$Tav^B=m!4hr_xT*@s#p8}#ay8im7|$wuTqx~xcoAabX3Q^WL`N_PRaX-= z)9MJ^g+r{o{8H^q5^8dIGUIT5yFO8WW;x279(?xoYKwx2z_ClwSeQ?OOE#vLwpeT~@c@!xvu{OlYeg z9vmuvh#Ls*IRNql4@==TlEXDjg99b8Y@w7le6o-#&QGc+oezwBI)|cnej(W-b>zi~ z86Oa`{8DEfo6YW8FSI7;92;n)sq$KtVC^OM++}=T%l>!HPH<_A@QXeiQm7%B~Z>ViHSjs!J1p-r7@?1RZV`BuH2@SO^ zc<#}fWu4o!2DVzKNL)PSh+qsKH zUO8?7a8oPVHktRhX%Bn8CDD9c+ZiAhJPAgM%`D|{;uii9ky*|lNta%Ut-vzM2r8`n zQpe`@kw3F(VZsL0!xC%8(i5mQqk5gfhQo8EQZqG)XUTu2fUI?R&7cJPxXHBT@g}bH z3l?s93#u$Rnk69o_)yDSbuwQcrhwn}7rPbTt2S_q%~ZdO$47;K7k80vJm#l2_zBZE zEdqBnqkxONVF16LNKl{xEi)y=ld)J9h(r%FQn72Pnfcs>DoG)HcAT#Xa&;l&LU|@{G@pDqg%6 z-Y6xR*wW;#ToN4Y&DUHDSyF}bJ$Qkz8XK~RX=itwyoFLwQk+wzBXKKUbFe|?wv&`~ zq{Foy$w4_-Sc!}lx1Ispiw?at&{B1|h*is{kan*jM8~lh1tsO$xSo`@?-wc6xVr4> z#6THNXa)~VMToY}fu(_$pb-*T_Je8yTDC!ae7yQ&*}-Ajm@$u>Cu!I2&TQ2Y`dsvn z2#(SbsmF7J<4O}jSXUN-_A+Anl2bs5gLkq!&JSV|Z{$Q!dCk67?2V1UhEJ`Z`OE~UI;!sUj>e0$XJi`yuO@T|#%qLar zkv#~pbwQ$2oFrf{>48>HJ|}o{ibsS?K72_AmfV$dsFbF_%Vsd;=igqc(}WJtUCKiWaWFSHGLhjrBz9mqadJe4@V!P;3ylg=S1$WGj7xl~8SKUZ6z+GoC$ zQyMEE3^ax`WO6K>L8EB)!sNZ}b9GyG*}fNXS$oVLYzT;^y_yA5eVAlmy#o_wV|U+C z0f4s0;VB}`06&>QHCdKOro*&Z-4Az#}u)^adfHP?&+BH_RmPlzB7SQ?qH zE9oqNl;NBY0_Y-XfDGGa)<3yvlz^obxG&+1OZ1x7#jiYolKf}x&j&jyO#8sg6I)3b zz|*(gM}OhBF?p7ad=uEx`k3RL1G(ATE%Z8$;kPUf1GwJ${7?N9QP=?G5eVWfLU?+1 zpnEX%yve6tvCH6&k7qi0S*^P9<98gqTziH2G&n?@PMYZ-y*~AUdiVxVat~7t!faFs zK~Iz!6BAqkTl9|x-!`66wf3I-@}{)=LRvzYu<(19xdYKUdh`-Ebe79{$Lw+RVqA%k zwzZ~>LqjqPfdJb)$dDvcY|4-dO*}iqkzspyaXfFUHFW@yd91el;GY-R~}Ue z^=K*VZ?p@|u7BG}k}IioC8^FR*_5hUtQw*yvOy44>|R4QfTFgA{#-?A1CA6y_JAu3 zwuXN+8wtjn4;-Pjl0?roY1LAgrR+m$UdDsjdC9myfp#-nu=Om#__L;9!*ej@)vW^3cK;t1t+KTyTC~J6&0d>%F7V!fqX?Us>YhEYQ@xtphzIxJFCI||KRK$ z!z6jucHg$mY1^o_?Vg^tZQJIwZQI7QZQI?`wr!vOul4S|*FM+#o)70kWo02V;)#r_ zA}a4Ee)ovA%42T$P_UzN2OUp0bfo2?y~5E?N^TWVYgMh<#Wfh*0a!6QfIr@6`%sFz z-qm!l*Br-q4W14MdUeQD!u2K<+SA`9Uwh*coxpx|EPpOPK_dj!2FeHE@bYfN_-fee z_EW{Gr2c#UG)cwsf;?c0sj_9@(j?L-G$FPhWk3Nsh5Yy0nv@`G+AqpfT8%13Eu?i& zdJ@$R+tdDWq`DJm#OZg1nDV-Smr!Yvv~-cyoCff6=I%mr`jl9pBy|~1paWjrI;4!) z+IT;OL=yeycTDZPx&iS{pvm41z@)%CX7yNV(CR*jKd4>P zN3>IvmX#7u2z1f^g=A-%UmiMGo@A5s^avNJ=%Byg_86jfu05 zi;0?f&47_@G+e-umYG5v87j_{7ul+}kk3*OOG2u>RL+d> zhf669H!OnsC|D58eEuHs-GCNir(J;?e{R}?4z{IS(_t)%j-S^rLKfhF@GBp_I{h+z z(ShV%TM_&fXUL9huD z!oK$-I|?#k$f4d&NA(=0{4e8dn##C8^)RSVH`BBir?ZX>rky<{KO&*=AAn`L!Z80TyL$W(Je+7%9aEd9hNh4H+!U#PO+P;hoIxcbD`Osk^;x-Mn?>2a2> z{=ip?BgetCK(;ZHi@5|B&fLv=*W@DzZkem3U6#dKXrbVgA+V<=z*{q79&I!( zbxm1hbKgR;r4ikwZvCn0SXkyFHu)whkD(2Y0(ZQ~NUT@VhYcSgCsVLwV!YmNJDqK5 zkb^9Qg!*7BVe>lOw$+#gUgJ)& zd7dka@pSrm^tBL#%ommewBgc#h^ZgcrcHQ{9kaw#<&P(l5ApRpW|KV^8nqO!N|&Z?|shLaf=NFizO3)E3Gz&0DAguTw)^4X;*GZDhZZ|0xe? zvvSnQxL?@#V!c4?0p`yGCi4}Hhx>Ys!Y+K4w}?7W#A%l-4Hm;_VJq~v1A5Qd7FYd{ zlc(&`p^s=G`N7N|@=^QW|15r0fl?@+O^f$C_MO^ZpJqZ@0`fhk^|KpNFemb*zQR1Z zK0%9~409{R+g%}l%4*3iH+JR+mlMSNSw*Qs)WATE`1)6n(B^>}MN{U1D(Tu2PO%$?DRc@QzLQ$2AK35x_*uE@OOKutH6`A*Wc~AyO0%RP&d4 z_4qn~Di=y>u!-48ZCWKdo5qHM%%iT9H)?&@^pD)>XNY2pUTO1hJ zgata3;c7Ns2b&QW_53<;Z$@mJ0peNR5$JL5$!_XHEbE|=61k+&Ci)8{LD24UC?@1a zBi@37zkR;qI|MK0LO6r%(Kk!p?@h%pfcujZ48<@votJ+nEl4H3ZEowM* zfFx-`|?S@VI$MkO>`s+Bel4< zl=o9y4GuEB`nu9NDaxdE)5uH)=L;&a_5*xeivk6SjpwGc0z=G+nDGe77s;!ZHY%RIINgZ5p8o?Rl?`Eq>vXA7K5b1 z+3v#p0%#SY=*15i0n80Tg$xfW^mo9G;-@$WIlnGtcuRH*2>qDjvV*|HeZ{x@oK-%p zqjZ8tKg8#4e+3oWV#`oi6#4bCgl!u+pirs?xkl8Vhsfb@EZ+Jol@IOHvnO=~-SsL? zdb1udU!@Lc*4IXsNcUG=FlpS@O(zuK%P}P~^%_?~h^C0ai}vfnQ_&=~1R#w{kp;q( zsalowugGPHM8svzq2d-jZ1jo#oGr;SOU$v!Pq5?-4ls@~tx9|ZDtQz%KR2Z+@^Tqw zUUEe5Z9L~)2QnMh^eVx20X1twzZn9Wg-sK|6=o`UrWK$5GZAM!t00c3*Uy{HT8!#* zzz{W|TW4f60tvd_42%cKNpaqK{y5T>;-Qey=iJv#N*>P%iS3`N4p)%IaeYaa~SpBr3&OA)UK=>TF|laBR0-BL?8xRJ_AI-}Of z@SI>KzcBLI)rr{ZEJ5mM?O-`L%k@#8A=x`_BwHT7n~h_%7V<95qae$pR_TsmdjFWV z8p9s8OkvDQ_|Z~XVXUBn0qQ)L$sDqqVnJEf0=VUka2=N6qZ-7Llpa|>J#)ZHk%|jk z_06-l)R74gLs=yDlk*Xd)|)$wDv{mbnaNa2fL+Z!+o?VI}8n{mqc zmsKjyf&0qA6#htix0X@mo&8qKm1isPI!(gJIVu9sbPRnuQf}Df50BRmtpT0rEB9h7 zaaU+E65BiZ%oYg?#?@bDQ;7we4xDANPf6SKTVSp7FmwpMP)4x<|VflE#6GN zC4f?qP4Iz$-I-kk^ZHx_YK25jqDmW&PyteUODkhT%!99hcv9LZNJ5MNBDL$Y0WDv4=<<>UCm*3qs4-+ zvnG>@z80tu+bw**^f&YjCd~-1U$rz$JZkPkKdq73Qe4Oqt+N9ah=tCm zE0sD9IT#s`PDwN*ZPIa&l3HEfy`EdIyeV^i$;5>d%`No?-*EF~`Z^TasE!N9$wI95aC0fW%6({UI!ckd_&DcsaQud>!r{qA@W}?OxzV`-eysdG;a^< z6nmc_9?OSSd*Ys#+MXBd_ApD?Gj6w)w)6BS3Zm2w*Xv1W3j>34*NiO{VBFz(kADOJ z2jGXIbG8uftXW&VMRjm(M;&i3i}eWdew?!5rDtt6NLV^&U=ooZuA1+_+CxP&A!}-9 zL!j(5T6u>(>_3MG_ielSlPSqwcPh@2p|*rwCc+opLQ9}xfwx?3ylwwxgwalF^0Emm zx3fzRRuPz$;Bm4pRw>L)fABtim=N)(&|~>7uNwfo5Oo7fa+T6H6@{7}uypF$v?K1H z&_2g{nQqiye%nl^V0)&VNlmV48>ZlHMcSaBkiF_RV*?{SI{m%x=~^E{1Dj?J)C)32 zO-oBVNt2FGVa{biuAH(mkcrEiEw^|jEwNiH9ZSLz=ro=3={nZZd1d*0IDfk6`by{bpgq|UhBQI&`p7W8N#?#Ve#n^2pqRxl?w zW!`f+@l{eK7}_%}7uc39AyN+0_g6(+q7(m{QOGlOrA#||D5_x$z;sBCU9A!~^+VNE zKXz^hNXowamq<|2U;PAh`aY8oXdwUek4OOD-HnL-gu9syN+BlTT0m{UK+c>W{lQs% zdabU~Amiv*>D))XS(e^m{=@Ae!r{UWm&VTj%@g00!Y*K+2Y$gbGvtAu@mnM?_!Wis z3T$}?1zr`d)wplg#PXY=rPZ*?_c)YIn@4(~fWqb6rmG?ymzdF8ywSu5E33Ma__5-q zl^ZcLo%6@qPb4P&?yf{ckA%;T{NK*<+~uT33;QYBFClc|EiS#40*ZeS&qz1(aHOOC`WnT>n-Hh^o@=OKC{eHR-~>RRXeqRDvb)6399!+OX(+OZt;Y z;jxao6Kn(}Pr@ioVk{fP+i#UXMjj@w^%|kRe52LeDy+)lGHE4pY&R84p`d7$TF$s8 zC!Us+;6Ao*e}=0`n`L*=+rKP<%ieJyI*pcem@ zra|4_J06xl>;&yhuljtt=IY5*9$v zF*(n`DA!)d?LjlkaVxC?Qm5v3F$}pi(U8LGktnp2n8_5|u>4PuqEPwM=-Hb^@}&*v zz#Cj&a>-ymt}`73zkV6OB1U;U!K@HHS~~+L(-r zyZmCgm;Gaz&x`1R98n|iW;L*l;B7$?pjoN!IVZe8-8-5#B3_T}jI=8Xtj_9XZK?R} za#}8P<4*o_PSu1e!Au%b?19cH=rokxdGIxGs86+lb-J3ccX>NT7 zryAeLdrrE{IP4uEGptbILPVpuXmsuG`~Q~+kQ47z=|a&-(j010jZm$vo$%N0C8{ZO8rx$s>8FHRSITI70~kRw-e5FiVJE->dy_ej`zP3!T z+wLq#;`hR@^Cn4VYf9MmVr8rvy8drI;=SDYUr+PsNnGf9$0fRju4Z%J#b8<+zXo2k z_-a8cff<&_c1V%sSQmw>8H9y8FvYvD&S*V#?iCv;xg_7IHV-zkEX_04dC$t5F7zpX z(99Xgut}X*2P+!|$LPU+fM=J)PgU-0gDi_w=f9J))6T$FB`oO%=YjF;qu;lc7wG+= zu2yEzB@C|E9}VAWEPsCAZlAXKe-%S9hb*Hoaiecv8XS37X$)(T{`enMfGv-$ z*M!@K6VU`mSUmm90{G(R40D5fNIxf&=dqFzATI!WCT;#GP>ZE3c(r)~OS#f6WJ4(_ ziHg%bftgrN=Ab#Jk(L7rL8awu2Gw5bRELzlu0G%zP7k2Ica2O9;ogq&#cZgc#uL8P zJ}AcQ0Lh*uS6TIQO)lf|JfRp7&2KJ;3NB^8P=mms;Bg);%W@x-rbW%PMz_2zpPah_ z$e6<85pGIlV>)3@w*#?#8Fm0_T<$S^kak9Bm)2qKx+&;x9S+J9IgMH}6DipmI6r2* zGI%#|L<9iHn5v5&0nR^#2NrP(RIa1L5(uTpZQ~c%yD6pLjxpw*8`XPwS#5Cl8jicz zs;fREW}1XG#i3ugl1`TtTt^vcpf{N58CZr#k_%OXKpfd*YN!>3#QpW0S!M_A>028* z9^7a@{xff^W{8akZHQC<=P8Bjz48u(+D}mF{2WU?@7e_{J}kfcFJ)x;J$6^ z8F=w#6NI&Ps`3RH8%0jkxq+oK7}nIhItsa9!VY82TH3AZ2}+uRIX))V7lc4 z2-|S#cpAwN5G~Ftfr)zqp<0j?;V%5#bpR-XtQ_!PE5yhJotCriE!hQB%=26KZir#i z%mo(m{@)N+V|NI(Ip>6GAwNLSI`6DzrNcqV|tu+h~xWk4rx=)ra~&RWlB>>SYq4%aAl^#)#wPX)@CAhnE; z?Ms8);W^)jL0cP}Ne)|NFW=G7LHVIwI?MmR$zk)s&cl*vhQ)$ZVNkY3$mx-oVn17G8U~YzlM%! z_0R+Yc@rQ%E~OXp<+C@=Q~P8p%x9i?u#5o6gjp6ol61R53-`1>>u&j@2ooVe6owX`m@ZsgU)7mJrp!59hmTGD{KR-*DdMW2B5>wJ9U?xPzGtrPVo(X5H6$p2~ zH0u@{z@02M{RFJRd9JcncEr6~f1==WA=*Ww&h zC?pKHe03WD^&lZZMX`zyWQ%A_>+k@004t6od561ofddeTjZr2Yu+|O$vmsgWxRqE2 zgKkLzOKzkc5|{~r8Jj^rzqYB1fk09S>-<(0il@c?)P>8Z{nkXjp6d;gB$ehLOzxk0 zKq(+RC$ZpzJ&NS@g|62L7e+}H9SdBZcnmm7KkCc&UL@;fCU>hqgkqZ>1eK$$s&1%g zp2jex;}4~`3Dz^X3`oc%z`@%^J8Imu;-2vj9(Yj+Emo%W6b&!X%i0&NP(2l`@_dCA zWdFk{2vrRkEKSyi!94rNp}x5Z8v8`)kkwT58pQ=?O}U_4KQtu+zRvTfGE;#AP*1_D z%AE3ggThsqBPfhGTX+JDSt{mF8#D^pzjx(kb^*$Q3XQ2V%dzER`8Xm_R zs3o&%(XoQ*pIEO;Qg%1<`QN~bE{54bM!&TshTzGgI%OC-Yq-+7OsIyz4MQ>;n#ZeT zJbtl<%E@CMqD0JH2SK5RK_I7cR7oij_ZH5^rzep%K;R|K zw%k0r)~8lk{9Q2}=ToLi(Zlaos|6Jjh003azp0Aog_;rSfh4kL=zoiXsUsnAlAd zMU}!*d-e^a0pbhN0U=$XOOVQHi;h%nTJhp)}9mSau+6%YkBbBvdJ3j+{5;aFt{#a1h3ZzrD%Gla9L`<#iFFFE$4#)bM-UY!wawPq8%q$L~zD|~xp)&8X zOO%M6;(IR$-S2+B?V5A&wIv%R@Yp^;k_AF*eLvy360TAqpS@uA4-(+ zfvbMC!2OmNvdng-Ewg{m{8p4`sOGS^uK5xK48MKE+l;jeegU2;Lah%3$bNfxZDte= zwQgo&`hl9xd6>>&O>Lt~^)lmc^$FHyvP33bKyf1spv^ES#V%}zlz20)&SUGqv?TYk9GUGDbP#xuEE3Ta)(G}+#$&-y^X&e-tm+bogp1wwtu zeN?AL;;oC*BX+j?*>gF0SJBbh!RK!eT8Cmn+f3UP>%887@qr^}vl51vi(YStiMWlE zE#6@Y7h0~JIkI|G4=oUR@1i_40ChI`a{`J~nzM2giT|InY^xRc#j z8XR}y)(#wOB8I%G+%L9RLrUkjpX_Aq$J2C-+t;0(fR~C zGIPF_ze+yP$bv|d5IBx>rch#B?ENgV61(Q7q;v~-K5(0sZbZ+otwtq=^}?LBrd;Z^ z&by{PfzHa&Vj3*Y;}1+k^)Ds5uR7OkIxIFQ+LlNA`MOO5HX_BTk$v1i1Klq7j}6_L z8kaAV{++YufdoeG6+e~`SPqn6KP^p$pqMQ~4tKk8Y15DMKr!yTR1+@((B}v9CTs4A zhq7Pf!0`pY$8q5c*}L4{Zm6ZrVhDU*UP{)!Y(B5&H@>>QD6^oe@2Q0B3k zC#do7s<(cZox_Uty205XfDL{xQr+%Sf3&b5qApoPnBev;%({S$VXDnE%?-w_$ArK4 z47~L>%}0P^S;EzJel6a=DfW8?BaO(kB{0;xJ%i`zI;92W#}QP*bE6v%;r{y#?{kE> zpcQRZg?c!ceAKn|^=Wo*BI@9Mn{%SPRs305;fmQU-0EXI1LdFY#iRl5Cb6XeVs;jYJTQJIl3d+*3N*tb>?x%GCPv^E z^wu2B&n%xk-c3l((eTf&Nz))uQfsU0{?d|F@Q$RgsiH8Y?4iEB*u%LjSgZy?HK3D&mbf#HFtp@k zCGNR`R?6{t9xZa}skoC&qOe%$UCVihhEzYtzk9U5Sj)+)CD?7@8_;(SO_|!l**y;} z%`2GI!Laq;@*~wbXd39mQrtK$5x~{!veM{Hl~W`8uQj^@LoQqrl$_ zSEfVFwkysvkM>)Ob}wziR1+ufiMO|5qk9sRp6dCuD|IZ3ZoY+^Ub*j{U(ca8jt2Sx z5>fRos@(Hn<`*_-C6-;EOA5=!fidN-rFxT6nuViXf`GJb%=0p|3>Vkv_~2KK@2YPes-ty zG;uj)K`KrJK@bpY;<2E-@$k^4+~$?mm`SC;tJDJP-M}}gZuydEd#_P69X~{pR*Xeh z*i{$=lb*4gV-;a9+eDU1u@*nUUu$d?VKktRGGG5`DJA%}mG+g#EcuRGuLQKj!Cm6&lu{%X05xEiOgw`X%TDjmxa=kk zfEI;w@I+Q?nC~<>vh&WJwVAF|*c_DHq9`Q>4wPF~1-RO-n>;fdDU9cJ{XIMiTyQtA z9sf9uPMtt{^pkjeu^TUH7fpvrIIy{=O=mM~%jk4vNBl8&&=NX)iyR&}cUHUKD+v@k zI`BZ`KS)2|_@a<1p62svFt=l%K~HXoM4~C*@BU^nboTRX2kVI87jgsN;V(nw?c~R& z!=uGFfb~ip(Nj8Zi95*EMtrb1JFdB1oop9RzP@{Z9E1Sj+H+{|0X!u997;qD{=#)n zu(lNT$-&5Z`kp+zs}v`GDGyt}yc8K?AQ?oLZ8ddoty4d!a@_>DNu(G!^bqd#W{#7R$Ws zAA-c~ebVxlGP(k8UER(VKZ7%EPZrmuZMy=vQUx@oP&od&m-j}N^Xaqfoz>BhDP(dr zVnw6JPoMlW;l_sPnYqrd=}cRoXM<9uKVd#%=O#=jp6#!NKC_~HthVpe(6OD$4?EPu zvoOLa^veyIPshsb=odz6Hk1knmx;w9*C_)vQ#;9Lh(R!gQ>_f%Q&;62kBc=Iyzn|-^u55i)cisr=%e5O9EP3@6PR# zor-7tTh@Pg`0-JI6;j{`?YMHls+rR_2}|9HM+Cc)oriHX8{mjkycPmS6VUFiFf%Hb zauzRayS-3WT?rP`xV~&GClV3(7jpPjnB@H{;h5E+lLct7Xnv!IyP|BdPcM`dMFpnd zQ`xn|{MevRZ*RVAsxtGZEva1AmpV z)6~0aSC2#x4dPKK3f(EoAWzrZ%5Onw01u{vTw=wQd~^$cH6lFC+i+Y^WAF+>FX+0& z>ZMep!up}{iZiz*@hTgw0ak4o1@W8BsXYK_}{{o z{+CPoH<#ys=E^YtU*6podUkq1Amk9Z0>F`aKqXAX{vd+r1$t<3mOw~)K&x)RWdChR z{{c|_pG#t7VQ2W?rsKkrBL85N{=p702>&P6#z|Q^a!&;>$EtahTSO6^wV@HxQ+ce2 zrnEFLFkE0)n1&ig2n5{p&SPqf*ehzlGFG};zq|$Y!i!yc6Q)M@;)aTnr*C+jjR_7l@>LmQP8!7&mlNcMV zubqkz$eKmOaB*GT@iX4;^2%<7s;Z>Z6u`%%?o+kTwfyU`c+x3z#h775=dh}*Qg@T8 zd)w>z)S^OJ2Mzz-M@F}&^;c2GTeA#;%v5&wO@wYX-i7YlV`Ii!xA%&A<*;j)YMVm* z&nWRzZba20j-fMEE>-#(9p8J%1KT9euXO&>CBEwFCjup!6oA%eeUp#rL(aG>HT%+5 zRc~ApR#}{dJvW$q`>2dj|9x#E|h!i0JIdn0Q6s> z;zlt@IU|Y4V~c)4v?$yrjNr5~lGIxnYZ}ES8Y^6JaML2i0n*D3R^nRTm@UQrtgFes%&NC}Ilx_AxZ=6%@;zN!pIZA9ZmL4!jo#ZT1va^*p#@ zl9}Z8DCooE0dTdMha@L`+8WG*=_h+Ko1+<{_F(yY{d&5aJep0`VAhMlBXdRLt~6Zr z#HBryLUA1)bsd?CF4Z!>sR+dP7-H&4rRMZA2h~}2QF|z?xDqgq*R!cJHWtTORh!`} zw%FmNe)r6-9V>Npc{~sta3B4MUWnsv*z>Upg1GV*DDe=VtGzrOBVe~(#;vmb;a#v* z-%jtRfzxa>d;7d~umwQMELBWh6J}=J(<$|rGrUN43U2!b4vL)}eUYX^drqKJqACS?TXV1(!@A@W~E*GZur+D=!s7 z>I=jGChQ}KLwn7`q-^)qD?_&g<+S|7vqGYA}9sTzSie}W9B>biz#s;mmU6r zus9)o?$3H0B9;qU{Oz*E^{jrdjl?N_8s|7?`=HTj^G(vcgJU~YATblQCOa+ESQ?MC z|5^v0a*9sA-H2e&E_a<9eD*@{QABPnP|I_(l)`rhjSCXk^iVL0Lcx&OQomS8r>L4r z1CFf6;7PJ&FZiX{-pZ@}6AR~7WXGIE0@6G&2o)mWhEsdwD#>-Z+v8g60#%z~Fy|EH zOEtw>r)g|^+G$lJX{dwIypYfuH>CNXpJbRrKE`uW=BQ!*c%uoy{~_437SK7h8EQkd zQ`L~oemigOxeEGvTkWXpb7zyNTZ;LTwR#Kk-a*VG1QQuxY@gag1M(x#hcqNlHh|bq z1c)r?Ik%-?E`p}4*wrIlA3I3ddY&yW42Vg3Iu^&2XI6>##CWNN-~Z?mHtC*bl#2U2Vy-B#Hd-H>8--AiZrgXsf-Ac+V;^7rKVp5HG=bsf=KEG^Cvd}4;a464{gc}RIz7fC zJ7iRHppdn&J;Z&%{fIDBn4X|+DmtJ9kn2-G(qQL{7aPcn6~^SgeQ;(TmlFyN69mNa zZv?2e2K5xS6HM2b@XMXAlP*td=Q?ckS>r)&Ogc|M2VRSQ(fJ>6jUr2pO4J7zh~|n7#?w{|qMILys}@_bLC4&HLva8GUPGfP|!! zw1^muu)dYKfrB|f(&>B0P|(KI>K`bcqLZ<;#5dXeUpp#%Uk?{UG_@7!hnAT*9`%jv+zN7I!2mQZG`zKC!$L~TUZA@&x2Vz+VBVz}18&gV2qwl6L zcXFrt#;Kc{J32YIQwkc{8W>Z350Q3uR>s!iSy;5$m^4|K7_^yKIW)hihT2RVOq#4rtlG@%EdP4u_N?WBmgiHY$y1>VruQInYo;Ap3BXiUh$2KeRW+84z>VW8)LwC z;vc}t%)!|B8(|J`G$jSLlW@cny z2AF1KOkpW=yy=4J702~2M-(7_f;12N6{&&-e+8EjzeP4zD2===t z{qKJIKhI&=0FhrTN-QBVrM=KO$K%8bb9Q!`j)kvg_mjm*`KfaoFY^@bD((&?sn9ht#!UhN zhNuAFb_^mZl*9xnFY>wtH|>>EXN*m!$5XcFWh-y7R=YSB4o>?)UX9no8KTcgHh|Cl zb#?D3yYtEaWL_3?i(qXQ5qI+fVT;@4LYH4{?d|QXIcB}q`+n$tWrM%H?D=E+3r}tc zXzal=iNrH;?TOkoiYiAXar#`9uY^)pE)f##Kv2|OLLOfwu_lUYOD<9Ti87`a_+XPb z_-LqjXz2;J)nuCW>XMi1Gy0Y8f#8jx`|=R98P98fI|os_>+>F7w`&KI-DcyyAj8*r z{B_`@pGJI>_qDLt!gt}$kiVnR<1AxHm(Ss@hr-FaTy zx9$4VKMWD^9M|`&r)%TQ<%^;2uI~}D9dPdXy(R)xaM@Z@x4)R5 z46cPD`kOLYkN_&8M1{WCF*)9AmKuCJD?%9#Rt|86Bxs$ZZ*pp&38ev6gb0ffwbJZ} zd$sO4q{_I}(yoJ~W11AtZ;usmVA^woBsaFkd%g7=N+EKgg@eZHyM#hF)^);EFp-DQ zGI=f8dy^qi19Gik9<=V%^KhA!=r#Mz5k0cWmI5*B)eFnt3Cp^rybp5|pKa>vMmrBtPi<4EB+Vit2- zn4XH<27c~C*EkGimQQ5sW@@BXXF0`GY}&tfUYTtrZ%Jn}#h2Wgdd_#AB)vq1Ypizc zcUO1&l+CWPG;>+fjAxAFT&QB#ltqp2WbiNf2yNa2aQc7uqoP)dU`nJV+Jmc)z~D|l z@Ur{$84ee-dY2KSjyIr`96Y@1)CRLF{}I*o7H%KVjXw)s4^>S#D9mZpEK6r=pB0#9 z7E@p*qx$(i5R)|$U0F(<0z}4Ga?9!S80iw7WqLQ1mqp>j3k(io0TgeiEY}z2b0p)d z#ncnXGCz|EOo0PZX`j!WMuFwf_U6k;h4Q_nCE_+tj8^zu!k3Za~_ z1rSS0okRrt5IUTX{CQE5(EbGfbVYIvVSs^yh8WFAu%PlJVC{^!ppqH*Ba{$jfn4DF z_0e-r!GV4v-wZdLoH*NW`xG8Yt5Y7z<|xgTkoi^KWX7s+#ENpr_id^orAbF)m7}d~ z)3dAbua>nI6r=?Q(r^E!VAT%)9qn3=gDHiQ#2l%`p>rj-pE&^e4n6v9Q6;g1T#OYU z#HcpVxz6}MFgQYg(FtmaET^|)fy@xkfecCtR{GV%;F-hzP{!P~pt};Yp5BY-+8&`D znFQkiD^139%*YqfSc%uCidsOF<%O%>j87>z=QXu(`1;szu)VJoBFgxKM%oF0qR8dA zQ{SB-WbT4r^o5v^6_|kUh%;IquYFOEpw6MyLL>1L#HDzN=z#3=HeCdOa^Y1NH&VYC z7*f%R9uxHcX$QH5HHsIP-HxHW9SW6%nM6k>RN>ItaQ*b;rA`Et z1)ddwO?I(%apjt=p6@i}c($f+#T5-TRjM>%H^#z0J=;Z`sh0*TY0=G#diw~9NSSa} z3P5V)!RM955kTcbf|-O!f|#gQZysX`F~Z~o6x!!GOBx*Sz_H5XINM=9h5dX5ZOiYz zRYxhG{COqR?3&kF%(lE6h1tjl9N~AGwc=ylr`rg*Xj9a@k_l?)_mk&)jb-H0 zFQ{aW0dfSF*obh+0-Z$5*>L147Kllk&9jDNW>!s|sO;r-IKfjWG2cT3T$t5ooNEqt#;!2YNp*OhY{^mWAQvKV9fgy0f8lm~BSQ5)?EW>?N zUNkso(5jTtXp*J=Rq2Z{IfIonQCol1K*dLA0_V<_8FjS#Ou8SO~kLP)+z4_7@o9M?c0)8B(Jg6VGqCMuzl^+yf=6ZhAE-JV2zNy+@?r#j6% zOf~S^*4B&X4v+l~&llsgz*c895?+xBW40P&NkHH6d)tv2Y(BAeKRik1@!I%a|EhLM z6twuUnZZnpU``=)*Z`F<_~L4I^pD^}ERa;Q|)Q(1A+w2} zFDBN*(8~BTW0vD{X)j%lFJHbCfEpOOra`e^O7LZeKh%$pzDPg1jmnZxtwu@;bIZ%V2d z&`hDTt*AYyVsE#%ZCr$ZRhwxR*L2ggmSRlvA$k$aE~}P{a9T9vS*mF5py(5SL?ck- zrGvWeS_?}zLaXA$5veFE6C@Jof)dgfW;bE#-6bpQQyL}wpyRQY=Mm4;v@5X4LxJuj z!e_Ke%9jTz?~UHpWb;hfqz{~p z#g^LjP0m1-(v{R1E7=1)5?N5eb(W@L;`dTTr|Ozu zk=w;}yBUUM7~XnQ*Uad($6cTYy%iYE&+_@f%;;5|ZA%M8QaW!Ycea{ZCO{}uwAze) zGKlww$L)Qm7GOyUiHaEa~TJS`;VD))8Jp#!jU`}J1a>+^wNo9tb zpX%Dg7|FB*>r*u%P8pZA-uj^F>a9-j-B#A^IEC%Va2gvb%^Rn|YBOhR!Ui*nMT>pq zxDj~^XrQx6jH95W6cb`5a}tLB2Tx+Fuswyy3Z@~%vzTs)W$8=;9eVZjXAEqH^K8b- z(i4?RkuW!>XT;8JcIoNb^iy;l>RiHEJ;%!fhT=B`mp8|xSXt+pco@q^fMlNmy@{l= zU2c)?Wrp^k>cu7lphWWApW7`>VKewQ7!6ta#%)*I!P<}$h-bj@DcpOALpA#+mPJx` z1ur|Rn`g)vS?^t*ff6|$R_tDUPZ2qslQ==m1vDFzvxlX=oLymq>>X=Yd#(dHGs`3Q zazBrPUj8aL7imCbY%}`Yba(~3`Mghbu0KdIwd?$F(6(sAP07GqCk{>Ibj9Z{nt*ZK zzsDTjdfA{qJY|Ubi+@;LwBNcfVKY7Pr-BIEK`wto50lLK$QLw*RF)KzDG~&N6?^pu zHsTMKRItl{#^~$$RItz2I`e0}r#Se2uje>V(=-`=zR$5|f`swh5K~hH*85{u0-t-^ zVkQ%E9Bc!AoIN@0!7a&eu?1Kx8IR)6s;qh?Y>0? zqs#FLt-m*3iTvlUqJ23(T{ne91T2cBHWLFT1(M8z7Yb*C zs+=7`*(TFE#S0B+kwl}7OrT6;qtX2FR4}9S)+t^cER(7oRkaKi>}l6HoHesVKsUP| z2)X6#!m~bR*|ZxW|0?9*P3}%k41!CD_GDI36l zAxaqBFmkfL^pO+&75jvw5|>`_H=IuM50#(}Y43Izg(F-^g_r-bpDCGqz+z+yCj%VU z$N7}F>Alu#+RbocGRj!5KJ*e;FLDpX3WUiYKbrwX&bH!fq$!$T=vyy~HtLZd7UOAEXDDru0kYxkNt;*nb@?NJytq$h+#A0OBbAC{q(tVsK(%oYwW{2 zk#(qtkJ!EI){Bs~_fVJz;NNgzc9}2x_oJH+ukW0i#<&})+D3udXRZu%_;<+^y;LJV zSoj`17bSGrt6$os0BqR({5mt*I1icVLWw{E=pDd&55=1df1Hx(_7?Cytek2C*@_NW zDeN>B+>KVWJ~}cJxnIUE_fxR|G~5ET-P5nM?L(W~kT)#h^jck2RM}4!*2!oWmP;|l`=}o9RgDoFx&57aI zXSl!Qi|=##S*h#M<>hwQOy97{6Z`#W!E%amx4i~t0VjyP)DaV7lXfa;nt_7hFwpJY z_+9i!4VCs<jHW z5Q3>tLnr$vIn}sWsf@l}&AOE`i^L?trB+J~fyTZ{^XvxH9W{Y{;ezHlxz*e@_K{L8 z#zVfjvu$Z0-IC-8rH(gVeVedaTC~VyRrk*kgXboXFGh~?(Z0LG>q-lo%bh{Y*-V9# zR@6^HPRe%U3M*wC7PQQ~yrB8%12Y+9{k|t^huVTiqx0NXeqzJXn;hAV4hzV&AZuT= z8t0|9ir;pqu={||F-85~!zZD~rfEXiVKW@ct)W@cuUm@Q^zW@cu|VrHIwvzvWoE^$z1GeTH_AP+2>@JUo(RqVtLANs@{k3u8AFP}z)2tjG@^+; z!=b`wQ7=p~i(K%`Z04rpGJVYz?C`oLF-7W6J|Y+ml?m+|SS-4QHi-(t2qb8zxG+8{ z1O;dvKY};{7A~X%K0#gGu+dY&rb(Ew`)A8v?)%#w=yq=Jn?;Xh7 z3Q@-^;nk^JZbgqrZN-pjngBv zoHa+W+B_C{!a~@oifj4|jS~W^yPNdv@nX!wy+)CfKxwnSIKHEkiy7-CQL9&#l2%>p zT5rqYr62yY3{8H7)!h&D?7uF45oQ8GDQ2O<( z*^h{#R0ud?O7au#Mj-`s4vvaGQv8@Y?HHd1dN3Myu^zT($@S3zcKW@u3+}t(Pmk=E zFUs+2XB|hQC>)aJLJzIcKI&3WowtG9B`a+4G>Splsz`avXLfpL6xj_wN6@@cQk$^k z{!ElS!G9lda-Q1=W86Xnfg(hLsM+Q?YCn$eo)Q-sUx$#+i~!*zI0-S@7dsf^2R)Dn zuva!D0@n3Rt?Ds^(=52~F#V;fr6LY)RH?mhIb?_Di7W4BN)0=>soF)kx{}397uN zPl=z%^<2&ySsJdt&Fu-6MmNRoNUUFj3#+B!6#^z^`ONON*RgnRYF_Grw~`lhhKz3$ za>))h9|AuxVfJt`nq>J4*$|=|20kG_p>iI*pB6HjTvlz>FoF^WS`1+^i59mrSkq{L zd{&kNXYI*W3LZC*Jr+pcH{LI;;48=z_rM^p=Z{Ufd!J`u+Eo-Aq_-D+4<_e>A0@vA2UT|1xnG6) zdS$3Q?RP9*+<39(Gz~9emA3c${En_w62N>rqAmLtt1>|dh9$03?~}nGFS598Z01O; z{e_^De%q<_bxb0LXPgNw)O+T;zWZQ*P=?-MRU;DA#CDk8sOm?5DH`f^rMWUo)|WP}E+izKA!b+X?z zAfdgRMWzcWpAjUY?l+`T(5syJgM+FqJR*<;+ARpE=Ew9ey>YUq5AR!IJ$iEKTzaTw z3xf@Q1Fm0s24ImwO0b*~(Jt9q?c;DR8Zr^YQu-}kyGohYOq!>;WR{034{n+7&tI$L zUd~UgJk_01)?HCrVm!%L0c36F( zujwc@<0ri4iDBCi;UC0ja{F$i=5N&QrEYL$@DoD1JQc(ypkaoxN@G&=BsJfOTf%hL z933o4+aHT5)8G^y34w~0d1nl24Ndpky40=6Q~3r%dvLirkFj%?)mxv+>ZqsUCRJ11 zEP55sSHILL|BT&>g2r}&tx{OA1PTv#&vzI^jbSik6BTB}5EX1QKy$Mw=$b%T?d$z6 zQirA9A`8|^`RUU@bi8V?b+RqfS12lqmRjoRdH=3uIh;fdtHf z@4&OpBWN|cx0U0!C};d9+E-2sxTB&(TlC z?3RXeBQO@?GFFdrp@>3YF+joMMjzS;$)-`RcrUUZ8kG7Le3#lSLwh zLoyid)sg7*9&T7(FfY@?i;q3FF7Z3|@c5M^$drhI)T&rnHfF59a+Lo(gM?fJ2#wv3 zS<|=H?y;k1rs`GNfullJ%`gV>ssLoh7V;QawX80&`^B-hU^Y>I+s7QP%i5{Vo6be9 z8itQi@MJ>G>-&o#;%W$0%~2H{*TZ&*KJL-Y@vyLl_Qy_oT{Fdg=_BrQ4mR~Uf$J?j zBefpT5+lwSzq8hi3fU{hMGGP4E4(uTX4I4y2QK8!Ea0=d;iOF9E*lc;*lb#4Q{7sD zv7UCQ86clbiGn9oWJUpWG6 zee!r(umR+`49r`{opl$6=`t7r-YCECbGc9z3(HSKoa%vlr8qXnY!Zob-fwS*8uh*^ zkkl`&Vox~iT9~yM5`jnK>v6b=FM#_!pu+(QYh9~_E&J3T-2)-i!0oVXI(t<0S#NWE zqa}_Kat|?x2ZjQuz*rPh%>X#BC=j+3-XBas9t4qwFrzjMX*kAxeVCh@$rTru%LUQK zA5|C}F<;Z-1uGUF=J$_qpU!lV-454t6e`Td!PWXI;m7C9J;$D_CyJHTSPNKJ$~*i( zFraq@u;?x0`c3@T>lazu&$|AoF5D)9FjSsiUgGz4){9+#AG7r?aR9nj9CgiuReK}A z@oeaH{EEw`4=H)6Mz!nWbkLFwfx*}Typ2WTet1w0vnPIVEm;`O3I=or0D^Ed8MT=8 z+I6uAWuqU5_P$lkq5III=M!FCk>Jv#g(pXD9d{+HTgjVZEA9hUX30x2NC{?KC8r%Y zb-j5g=HXUw7df`^>eZz8zVPC=RisARW~3LL+%MwmlWH|M>vNk_=NbY1)B)cAUJ#(Q z$`fYf!{kjr4muQFuFX{o4W$xS4b>A17M{;NX{UPhUV^bZeQ}U=0AN@wj$o>zA1f1! z^D&o%-#tekD-iIC)jz!ss~Fz&m=TfZ?e_TOnXfo{cX-skmNtyy0DHu{$_b`Q>*s;8 z(-1Qoi;7$PfL?oSTWsPnPq0E7uNz8c!Hs-dt(mqa{(13>ny!JHYFk5*TmL~b(O(sY zz>Du>OxG|d2B8{t6uq|iMSh63wlkJF=c_vhOgOU9bFPQ7_ngCY=3KhwAbX0R89BUY zLSE;vkubd!dmjp>guCuBA+psha-DD#bVmltj|{K%K1E`|R-o$5JNJ64wG; z0g{7dxBx5lO#ec5TM-wLBVz%R<{U=GoIf%%QQsnHwe?B#k^Rs8Kt(~F+(ejk2Wnqy z>?g9DS}t+XatAJlyh&QakY1iGwb5yz%RM)7dPr?4Jp7lo!-21MzO(tQ7(>t8eIA6`preg#q1cEv}%>|I-&4JQyR&xawBM!r1=Skb+baxjX}ou%{x+)T6j zE2jPI++phnzko*fm&Wo&FUVXHeXwO!lm`(w`y&bzkL6 zuZD6xTx2|S+0-;?z4tM>$Gf<7!V(cX!HW)2g1qQ^jgw{e3bu)b$&$J-vR3@Wy%-Z1 zum&&tKD;^nK(z*#kRFzZ=D3z}i%NfH+FOf|0QC*F6I*#dJ$zJs-#KmD;$j3HMG6rp zkfa6I=~*k^ERWThq11-jEr7+l#8lOTcyG@d(_2f79A4aHdeH#ODx=dHf&QJwW^u?9 zj>B@bP`YWoX!nzWDA%dI|Gnl&O_JRp@_d&Y!Zh%BLu;@hMRY(Mf$u)e@glwE~?eo!URt?!UVCe~RG$(8T{L z<(~?-$fw-xL1*+SN4x(Kr0ERktp8Iy|0|CFk;(rG*|ksKg|A)q8>On~h(TlVa_VvB_M62D^HaJDe~!{Jb1s+NUxN93r>)#M!kg-}`VU zK@k>Mn3o~j+!Y@+9w2Vz9_^0Znjpfqh8v>tdS@x4WYd@glZ086gUW$r?)XT@VjV0W zN&tkJk%XGUNm<-xa03-{Of&;F+c*xmp2b1zFqw>Keo%+88IHEmet1>_x`*XwcyPUn zN!NMIoiup~D(MZik01;hd-WQ)zZ@!hdO~riT{G-%Xnp`C`4?*b+qM0N%=ph) z5dxSPeA-sXKM9f2_zY~c^tAL8bjlV^)+XdaoZ8wdj-MV1ZEZ_uYYP)?Z8=*LVSt6p z=PO}5Bj-Q1lh4nT{sFt!)|OUeU~qHOGd6HC&~r35F>(60Zy5lbtUo<0+S;NLVvc&M z7LFEn+S*1Y02d2eJ%EX+o}-buo)f^0N$*c{j108)#-^YAc^DJhPsfWT3?uU&rssd` z%YR})ng3x7`hSZBW&S6I^ZyepsF0nt9YE=i<>k}CBlO7yWn%ry1^smA{7;CXpP&5M zKYuR&Ujc(M{^Kb=iJ$+8|NLy}|3dtvld$5lGm4 z0zp~+Qda*rfuMiqq7b$5cKc+|1p)Ho&TQ+1pPbee~$A1JRs=b zwfxV~{m-pPpMEFy|7x6WR9&+qW`p;>s+lpWpTSJJ^q<*oV<)bKGr)QzKmy@vM)Mz* zI-mS~Bu-LRGTdQ}|1DsB(YZ!((ZcS?pkcm;O2E(uJzxG=WG7OZL31ZF(l0-Cy1#~x zyvQzz(G~#0ykfC%>)#lpUDz|p2PYXx+=O(8vlb#z7uJMyPy&@ubyZ*%cc&C9V%~DC z6*>aGTHx%bzRpMy5BhD5im9n{Js!=W)9%3JSL0j_N)46GzDO)fga^Z6u(!j|s%#Oi z5j$iy5ykDCo+M6d@nfG%iptB`cT)nbJtK-45{oDl4Rxz?$Tiyrr!k901``28;QL== zJEg=$wYFI4i-_-DtwN`!`4;|G)v!bEpyXKH} z8JcNCNZ;C?b=~Xcc)1#Q@MWsoWfx)kZkV<)Q%YNnc5M&Jn0Z(n(;~WPSnv*arU|Z; zB?*aXrUD#+u@1-A_HIo(nd|J!Z>dCQed)5dW5?poUiM;D;AIbj zU~}=fcnsSDB(_UYvYc^)^{(Ie9EZL>W)dAihuv@Q)WAtyBD3(1Bz0o)cr15}wCT-A zU9P#G05eqX2n70(#k8^#*lK>K*(x?r9*xkB$vC}tUT>9TFe0m}Q}Uz{A|#Wh-C9HK z-?$&`wm|ZjSA)gh!Fa#*TJ#$fZEAwx=;idbv8vO8l67IzsgcY&Obcw#2HE15(Q!PL zG^Y~9CeenTlOG_%*TBGdbvHQV%N;fio)mY`Q~9k68k7YQ?4jo!CSx0uP*-wEI;b-! zoG&B-Gni{`i*lBlo5X<~{$&s3s?HRDGRkVHO?@to4`pKmjZnUHCAMABwoV0iusVBz z^JV+2iN(gTbdg*qHp8Wq@4UisK{N1MTX}E=nmv3WjKni?;bU0e$N##AI zF)UV>AvLR-Rr27h3I|XNzt9?WMkZ)wQk4aBF}@@qS?%oBWTk3y=hx21<>iO%@00tV zFCQlj%bgn(bsJa0wA7!I@}FS8I! z(0bRXbY=D5A(nF(a*(;v?NhR1)Q<$OBZ}aX63a9Jx{b zxDRkuDt=}ThQDnwF3n0Xw)e%PpgNoOBwE)IEN@G)@?|A0`&#C{U17GacMA+aG~VX9 zP55&g6sXo3^|po8e%0XllnfgP6&o6if*iPFkh$MX`fO{<6eu7}EX$a#!q!tUkP z$P_pJ?B&I|o71w_f74d@TX~+Za}ex+;92W4AOdYNIOHCGXR@FAwym$Lx)$ zKclnI*u}DB&5Gx*@UEUXo?Nf4HnzH+vhIL#$%@LG7U`jYqLy-}B_jt>QIHJ_zX5%L z#0NnDVsx*frQsp~-;N)Ws?}Wo_A4S=7d+OYi%@#(2mST$Fq)qyFhKkm?BL{|YDy-T zLv=aZvGgNGrh8EmX|Mv)*$t>eGjp9~cGr>DA;;HT43pEQ8EUy1KY!Y)j2)vw4QOR?=Z%g zRyX!N?Ub~px}5d2PBTlodLWPwXIBsB`Oe+VjAA^|qX;!-q`ySEN@eF_iE;tVxd~Ot z>bDY-ugS6;GvCkFR-+o=nq}}>+FPL87AT;DIUvQxPoBo+Jxx+PIO8?T*3Ray_33Y~ zPf9zy^98C^=@ALW2l{}5Zd;$}dkqkfToBT2U@CpMOg}Do5MOwZJ^|)X@KJo6{qG>P z$Rh$AGeA70;54~7PJXzxxYi*3`dsK=I(mKV;UfcLdcR$Z+2eYBtM=2ZVElfyaBE?ct>w{Eqb({(l)l+w@QDjZPHdGU2wSGf@|JqdM^-O zIK2R}oses45O~Z!F>C|^LQ!IIPQ}nf0*^wJ6H%}@1!Fz{G)IKC0BM}q2vlMyKmi&h zf5#j#Jz{JYvk~P&BxF|S*x;Vo<7<|n+00K+u-@ct5eOpjZxIlbf;@Q<^y14B&!P6g z_EGdQZj!`^FT%hKfvLjO_^R+3VO;}N178OA2BZc$1~d@Ej74q z6!VE{%B(8ha!hWs6huq@wLX4s!I@OuIXa?-`o^+zt)62X>Eeq4P_ePnrEAB>hl_Y zguM{s?R4A}{i5{2`;Gsb^LJ5DmPlmKoMF_UPsBLtG_?(N0Zmf!Mlv{cjYfB4U*&EU zWfiM>l-5R_XcbQtQ`K2ji$+eJk8yv)x0bjyr#0-xHVddVk5$tZ)-@k}&t8|MsZH8h zS+kbG%ft;L&u;HV@1XbZ?L{#%Swp7SHaH2`QPEu3 zWY~Hf-!cJNoSCrf8xF;s5n1Dz;F)rq)2-~Sg>8+k=gw^>k`5>x$sM*XJZG;HGTX^l z&0{v}7u`P;@llw+Jwn=cFs&D?Tdj+2)Of)24B#f=YVma62l51PkMoFe(|Ppj_Uc;J zROzO4e%s93)Vxx-!V+w!7gZ-)r^>wDc`JN%dX9YKessQ}x!>6KTzYEKi`7foO5LJC z5KE*!g~vhiKDs)gd&0Zv!TbRs4q^Uz%)qrxu39dmz1DV4K&ya-K!v|7*gFwBu{vHS z-?Kodl(@u*Cjr|4w?lBN+Anu<0%q1mn)|&OG=pA);MxEKUpTa2wcx97Jw5!j{B8Qy zy&DiE5E=u~p`+r|^~AUDBcq>dP-DS-fo*nbHvBki_Q{ajkkasRkURPzVsY39`Ar&; zRD+ZQH)Mch$az?1z;V}cFJ!Mr)?QMcN8^zcRV9VP*Y%_nc=%&e72E6<~YNeuU{LcgS!Te(Q3?GafU3HJziq-(kLzfA@9i z>#09cP+?zZUsg~<#2%WRCbtf<@`8$!cbL9Jf4rkqRf;Ojdts^uH5IS2Ao(cWiqdTW zwGJy~#^)03R^uc}8DSg8qs&M75)M8FVbXJ}t0Ao6yW*`9XjyP7;I90l|3cIx%v1T% zOju^|NA3eA~gkUbitOi3xWk*?IUa^oczZYZJB(vpMJ6K!T zrD>uzSc_3DXs&wHPqtMx*m$__-s)T|JdQo`IX^q!yL$By`VQ8~c1#^)xvG&?udkUb z6-g)8qp#_PB@kQCi z*(H^w<)7L=x0j8Vk5{Z$ZdP4Z-`2jbe_apXfZs^o#NN!`qS&h0X5H@E5#E{DRoY$K zGuk`bcijJV5O@f4n0$nHRCdgG+<78;GIOeRdT?fc_I4h8fpC#|Nq*UQ#eX$%t$uxQ z1GxEp8+nIuS8~sM-}fN@u=!~7`1Tb3jPYFh!u~S+s`h&H=K2f#SL!>(d;5pf$NF!Z z-@ieift~{I-u_**`8PN5&;Pam|D~uDP5xQPByHes=j`?59CrNf8ixlZAs2(=zolI{rQn zo>3|(HPL^2>}q+-oM&TVVuKCo-*Gd1W@URaaDKQL0&{EW{&2UjIHmBbxx2gixs-^4 z*G)|w`EqnNI4H2z-@WSykpuqwXv>!~rQ}pE$g?d53rG*|DPq`Lhr@ zcBWwa!eP5PIe1sGBvBn4;ta?R9Azs4b8_U*66ucVl3m&MVc$rMBzGa8mmJG_Ev|67-b<|8WTU`m}OxJcf!Y zE^hn{dqLyz{I*eX)hpJ%XLFs})zFQPqkpx3+)ay}NoZ;^mHTHzgvK1JXYk@q^^NIU zzfM;k*nc(&Dl2MeMk-bc8{FyDQHOxh);ayc$@hEQ2pc--+Bh(yXu73Ykgcz#Q#$O$ z+a-Cv8lz-dN~#S&>Qj-__gi>469ZXbw!@Jf8+JGWM3S~r-5%>2C?^+S8UV> z%(5?Q7Atc2VV95=;hI2AdLrJi9x78}*iL594q0=dD*Df(G48IdXP)$^S8%g&jh~P`cWjfj6-)5OY2+b z=DKv58EKrrUhku^MCwP;9<_B-mkik>H9WCh)F;9GBDzRQkE@&+7O;Du4 zHxq-lNo@lyG=FZg+SvY^;V-PEvp;JjYI~+B2wDvV zCr2}lWivR1DY)*MP#oC4+?pj)*=rJ2J=3GKFwy3p-T2aqf<18<>5us^a5f@wRl(jf z$~tUT)!gZHP99hkeYex)R*K0r4Esj)93#KDv*oK&BO6^2V9<`$_F{pxpF8sF;Eu@u zZXR1RlcsLZm~Q0p^K=f*(nxWhcx)N)cJf7PtX5kThkSCEFHBYFJSYrHu%%)FHp`5Bg`1 zr#NS%KuISg#YHuLzhU#%X$b)G;8#3VwVugIp64W0xc*p(R%9R9!6R)tT18DDi|kjED86e;Pc0ZjGE$Qx9^a@? z37%3kc?0IGR8v4{+CX3ujZs7!!YaL#J(Gg5mA5wor}T-HW$O@1q0BAkW}@|q^}KX3 z7daQ`7=C1BCetdMrWTZebvW(5TYWB+uAMFHhp%}Wt$+O2dxSA_sk^pDc=R(tjmeYS z;Zq)OShFM@%(*AG?j~m{+O@S>t6AoOx*Z`78L1g99_dsqx}5&DSseUVpqJ{6AY5^- z_MSxP*~dn(G9HS@BpS9aio_=-WpyHma%i7FnAAS66dJ3gG_O>XDu)ihQB-c?Nr4ht zJs+vHfOSlRnkmXC%^^XGIF+uzK9K@v_lkI z^4RtqJ*1XyOC%jpjIDaj5I`8A!TGtO5);VkuCdRV=NfES5(11-Elo}Y{J`iM<h*u|auLy$2 z%3<+ChSl?b4Q968aOyDk&2dx`#U#|%_SE@MTYCTfu?7nYq0(rHCXR((Fo9PkyDQNAv|duYfr*AT)Z6;U{oq)40y=;!2Ga z=;;9B)TsdXu|fMYHWBS=>?^2Cu&bmAbt!{)P%tDMI2YfsQCcKM^kXnH#Hf16BaWze zw}eR#wvU|rg=rdfPJZEz8+ zC_^H}v-TbApj1f*KQ0ojp|Io5!JC<`4#F7(gLVV%^RJLsaMIa08e>B=S{i%Wzfr5U zGS7@h$~LhfdQC0lSwKwV;Zitjjbv`QhFSJg+iap~ctxjWBKsPGqI163M<1h$ZK6;@ zbX9yku`Q>;6A!NWazQ>7m!CIdJ zUX6wyA&ZNnIsEO@btj~=IrvYnzawU|ii7JtUW%3si2FL45$of_^wF`HR&ap*vg87- z0PK=KASjiuI|J0KAF=UzL{HqselF1rSlk4phEKmVcCHsXMJv;F#DpXK5N1*NKJ86| zYRK98W^W~dbWrF8E1@U|AYFW9J&m>f*s7|L8t>~?Rca6p^CYfROk!*kpSJ#+|Qo)U1*g7&Lxel^&REM|iI?dG0 z#uj&Komc33cLikFyZRc|xgq#gZ@p zP#-eu5EsJQA^8;kob4SbY-Ds~6#2<%=Y!Dh-cvRevM#hj&E!V>8N`!}(PUo=G{O@s zeP4c!kTZ|>}S^C8G_ zy2^uEwR|(O>lmqJPpTbTrwYmjz-j956?U$)?+r#!feO8=#)Mu`U`NLsy{Ccmh8lzM zj%Kibv?AfL!Z=H8&HLUz95|04jBh&R@GyVN2>eaTK3NeZYsAxO$C*tfI3%!P!0hK!lUX$hd#vfli6nxpK*Gj$NougxG0T#(_IRe* zv-A%@>5a3-dCF%sX=R~kbCIV?>$lef3zT>o>1<`A18-PQ<-_IAT5hw7loRVSB#ouvs($hDsLt-%D%@GTZ5u zZ{+$-x-Pb$RqO1>d{d3Gz$f2Y>Rq4Ws!*yxvdF>PZYNr|-ULnVbBs~ajRQ&PNSZg5 zh2^4v7Z7L#22C6NpKS7CpYTnibT}$b%7?C?t946|)3Na%i))F9Ql2=9+11Qku`zWy zDICRka|*2Tf!knXU56rpDuiGq!IIA^t^_OGBaE0rX9?w7D0O%!b~hoJ$R@sihEh;G zDCvu5&?q5Sh>#^_j#S3+@dO73Vgl;^8M?y=>|2Z*7&w;*^Rn!NNong;a)cT8Ph*bK zncWl`GGjg=Vks?q%lJ51UF<=co~Fze1mfN6_+<{2_o#xkX!0IO!?2~gIKU@qD&N6G zzANh(@89wcGoo3TIhsdT@*c&2HigtNs0eNqSRj#BPJaKT>5p4fE^fI@O_ziB(4=xG zN-I~PIUnclj?!#aYC|+fRJE!a%Memm<}Gw1Y{}`HqSkDiDu?dOM-f2=(Nl> zH7RFfgNxHrkaB~a4TPpQZ1)w*9skE?Fh)8|8u6@2bLFTR_L-7!X!P2(&tZ}<2Qz;C z;ziDEF?OUcE|_B^n(tXcB}-nmy*TPXXml+i@*MMi>l#JXvFrAMgt8`@r2(%+T;1m) zsjtuk$_Q@{Sp+FvAi7;AI%jT5VxVCKA0EV@Rk5MCZOEk-j68};0IyALU00ayRaoQY z<-L_r@^smC74>-lsb1uLrR!zeQ#M{s0i|(1?bjHZBJ+@UbhW6g&Kpa+)_Dj@a`_?` zW_?ylJr}3TO3sw9m>Cvm)-g+p?1p;GQN}6H()JzGwWG|FH_qafaBMS7#Uu#arTW4N zj`5PYV!R(@^+H1;xA7$!nMm`MQnRqtGlF(Me}1vnY;v-}jF~+kx(H1{jdR>ii}2}L z7IRBeAUoqN4do=ZRoPey**C_teBpVjiSaiZ@_v`j}LfE1Id{xz} z3%c)fpbIp;x)e`Vq%n7^dF*)ok48gN1Kh68(+3diTa~!1+M1L~bp83<-Sgug)_{1A zQG%b1cQtf9(uUv+^Cwicy@MP&w%+%ZBC14i_p+-*!&=}k?W^;-5kmod>&m|~A7v^A z1gzePs|x<<1^^EHRrsyS*~&_q^X>*8}XPJZFVDnwijK%=iKRmV+0q%blgpouK#A zcRtopJ0LY)U=q1gPEoMov;wx@c;rNvS-@oF#w|ux7|;E|+VH*fjX6)Ig(0{WVFHXv zy{tBPvQaE?Y&!4_hKXc}FjiZbgAbQ2otf^{`@_wywO|C9x-9rk?^_HHRRtCU1^HJX z#BnH7;I2+w2~2=&nSfN|a~f?$+^M~+>eK7TEYT#0Egzu?=0iaxwcKvz=m$M%HFRH? z96R;it%lSh(d!3&qh0{D3;_UdHp?%?92|M2Lvd+jSU#SpNs z=8!AC42mX;yFY>8{JGsiGs4+K4k)I)kxRuJkX6R&%^Hdjhc(gFctZL+C<>EFhxd4f zAOZV}4Wqr6*X=?!c1MQUD3ye`cC;bIHmk9<}=#Nt5}x5>97CMZv00YZ|u2{tyz-r5<^4cV2{yC`13_7+l`aw z{MK7m{gt%<(Z)exKI*(qO5cBGeH?cn=lrBoCYhyv4UJ>$~IkAz~dB z#o5@nZK3Qzj*5p;`9PUz?v|c^&zt(ckxWaAK809OGRV)uu_sQ6b{D zH_GaFqO~II8NtL&cZPdywKDgBQHDK_1P7yfxYLdmQF#o}q+ChWM0^IlGEDLZ(RYZ` z`ij|Tq*+HjipU-$QSf6BHA0&Y2G>+-#Fhis&I27~Tbk{YWIq=vzyADQGEsSgeRL5w zvH^QuwMSbU#`fZlIogSvk@zxDWHj7&0s&aL34XJ;qE?F@VUb&f=%{g0+m6fv8fEPB z&!ZG^uPjn+D3a^B7lD&BC{XWglBq~RioV>)iE{H{uOOo;X@y5g%ViVQn+}SqvvsPx zWg0~VxR~nnYjR{J@7sUpnF?VR_ys@claxO=A1&eg@qTpWb2sRsOwid!@rGK`XFcAA&<2vt=JnDYo8cS?C>?HYzy?UP%hR=b?EmiH3#q{jf z2Yf%rLZjkTUhT2>_Z-n4iE@UZtV18I%jRZEh`erc^Gyar4tMwATr?CHMrYXAeL$Ty zH!6#g6QPUEQ&C86RAiu`6d|P{_x&>loJH9qxD3ItOrvP55|czi4nMfsVHjAio4odmJ~bVS=6M`{h-Y_CLWAg%ZzgHrldKmN4E1lDI50K20}dv?$7 zhbIOS<%fpGieME)Snf>E4n#GfnR1WD6!Xg6Jc}&pJRilZ4mWf1R zI67&o9+x3$-g9rsKGCRkoyvEyh!%7(hZ4ZiFe~Uf)u3v#?j{8fxNw98cgxUz6XI-L zFLpgP@iLwx2WLAs7#)G@>v0QZ#4y8+d5RRRd>^!jT~rhe*J;9P*7JzdFziWREMOL@ zkiup=ND`)QWHwSA8T+{FN4N=tQ0vP34vOBmPePicCryQ#1Kc$0QSI)=iP1KU)z1-^ zjsk5V_qjmsjAM&WCcv2c9%PR$9ycf=FV0L5C!m(7d8H3fo@^i6Y^P)z%vPhcvTl;i zV+d$A)gw@7;&1kOsf7s^a}Z2vk4p1Ga=hL$nVdD-0F%wo8-oEA(pl1v8%8^7U2@J= zs~{g86`dX~8Mh3XNHZ!8tEee`FIssOxF}J6U1Sn&Y>F1NrAZFkk(v$o&mHv{%i89oRoMIn8qd1IFsNadM zfi*}{9egu`?-xe8*)77GY*Wi3DINb)m2G{EZOq0R< zdYlQT3s-_Y9M?`}SpuCcnm8b-D5&p}c5#rRD%8;xrBKC?-OOWNWrw__f)wMRepe)P zQ)9lhrWfXm7FA;;Y^#{+K9w+wxw0(9)nGJs8dPhZU01D|+_yvQGaM1PE3BglPGA_T zIQP||B= z-_zmOBH*Fk#HSbp8p@(cDzG$0A3RzZ8x?E;53EI}1mDkM=)J0Iaw`pKfsBAVsH)T$ ziq!G4r5|Y!90Pc7nf&!FCK54ERCXaRGCy@87Gphm+9r6 zJ~%Xue`$>JW(XN#9eAR|v0mH876pUL+z&AaJ<=~9 z&_D9es#KNth37k#L9A_=#55@1MYwBc855zL8r5W1X^D@Kyj0VZ!Q<!^7pa zzla?~`#f6cyi8Bo#4qpUV6=*9MopSleHky>1;%+HsK2~xHRLSs0T%Pvi8aK{JX`r|#aV zLBH0HhLDtBYJEY_rJW&rlbLs;W3TuZW$bP1$=VP{!+0fwA%U;wOe=NYLX!*UB*Sti zZoxdtP9l?1dSPP6Lq@cL_>4i#j)?#itr_Yex7>}J3{)|d+t)%OLs@*9IvADL{ipg5 za$LSna@##QzhCLzhd%dM@i;M=yn_k49KfU+G&!7#5b_%s*oD)at?@4~FVt^-%Heu_ zAW2xjNetDaA_%F*XsVn)WN8Fo6r3F1&g-Ham1>MpmJCIfQ0{Muty8jSj2X@Ev$Ys4 zrZ|QIH<^Yj^8_v9n?m#w@LR|#+*BhP&n7r>>KFV+&0*-|$3~6y5CA-F+&FU zx-c9lW$C{_SGyV28isxl$Fhp8EJ!>B@*n5BIF+rMpXk5&K8|m)RG&xozhY^JkZ-e6 zf~=+52% z8sr-?z-ld*kxox=-JMQ!9c_AE{`AW3ha=F*y1Mh>w)2yl#}H%0SqUfokx_+(7{RN| z$WXkGoh8pvWew_#b_hONzz8wisVMEQ;wCnQg~!2jF%i&s55=tiwwIz;{?zI5aB8>Z zCFCPerFzB_SWxQt-cC$J*gVRinO`6H=0mS=p0FUDOInV+n`8|%Vs`XPnRJxeWn^fX z0P>uEEC8RM7Fp{pP*wO`>udYTi0BsW;o#8TW{YH;AwmnVtLuHc zk89U6u_V5&kA#i_Z*^_)z)bgRd1(k`p#&jzYp}-o{O{IDE?|tX(E(=+*uvDKGW(m5 zWSIzb2=G>;`AoI3_EWPT^4G$KnmQelqn~!JIHc&)G*W@+SSC1yg@}8Bg~6}#e7YCs z(7z7b*hd5&9#3ur3Yu$5?|gs+>G*zYcXOLBGhIlLUu8-`^-I#;WNpL z=4Y6P;zTUB@Yab0QyuN%MN5gC-&vXBE%u%b_3^ek(;Pe`f>UZk}u0uoFRgLy36JpE;FGX zvdFPt7sD;$K5`o@`$vUR+{ld(^ zG`RAXgXtch8;Hv;Flmw8;NSv(d;=)O%K}HbEfTA z>Jp{$aq=8iR~OTCwArz*7e_gM&ukRhC(wSz=ueocjY`WMJjXGRAlEE#kOfCZo6<8f zd^{aoCR2UAo{Hn&URg?Z4t?d_IHnG!xtEP_RV*VyhqyjtVYPg_wA*2+*nQa9S9Xe{ zP6;iv`N3fkG>MkX)d`45C4ESY7dli_w&DlL0<9={Iz>Fo?<*)^E`f*c{UQvnBJPqV zU`P@5Ou=LhA1y%}E=oNdOUhA+cF|U}u;Uv0RB)byz)GUd2k8ljooiqmoiv zaw%!8vtf{Cpkvz169#4u1YR|p#F@2VG>c6nWh0YYM``Fbt6B}+seLV$b7J(i$A>@M zym`ergE<{%$aw8@%sI$qlL8pc^WmWktLL|79m2 zlYGfmtsrch;Rn^*rrAHe*LSx!D=Y5KBJk!C*qK&=jpB>Vd&rq*sbN37bEM{^w*6Bt zDrQvqoP#l2-9d_=sEp%PhO{6xnUi;s(3F9hg+>D5k;!Ehmdm_VE9Yq7&CEICHBnYa z-I|Sa@IeAQ!-t$PE9VHPSh*#@LZ)VebH<1|E}K-l3N+GmVvd$t6$>;?i;$kyS%eJ& zPA34roAv3fDMshC?oA6=^F)+9F=*uF6(cDGqF|fontk%}IY%P{8}^(NC4wZOhFpot z3lUM-Kl58?cqG>GzTg3IQm85C%f47!lbN41uePdepO_T5Pw7dl*5OPgdy6W2HFTRC z$X&%a8bh=umByca+A%mMJa7n0dfxY8anYS~5+=-c?UTVdwDb1)WO9z!JXB+U@mPgT z%e1aUbdR6s(dltoUNVKh2!_$1dIAFJM*hSTqee+FBDsknQVz(4WVAr9IhM7yMPgKB z#_W@q&pFoILB=Uy^RptrCJ;G^IWCbit(0?!ibXMJAj?bRI63!ExR$9P6N*HPM>grc zPpJ;XaTv}d0y*(HMMF1CC8P92TPZf4(>;c$`Uu98tEaL`*AC zt}42_uP!EsWt#I)U&uURD|HkOtEi)mJ;zMQzrya%m&|+cnrGonVnR>v)x(DEUItX zl>5lEWGk&ckR&e#c0T*8JkI%)`Hh|Yvr;LWa{@H0S9&$PXwK;*q0YOdg@1ju+Sv|8_e&VNg*GQO&^n5;;fo zdU^TvaDnq~X^AgybJh;wHsBnIJV*Og^Q8PaMB}67oFEyRYevyyM2AswjtIv`c@hM{ zpDZITj4wwOyk1_#3d1u&y<7WiZ~vI5yoy)_la5bJ5|pZYdy+k$-9I@!K9DU1_mhuv z*o=u@_9eVxC6@-+m>Hx|+aJmxUK1(ui|J^HU*tjgmpkWJGIBVN+cqHo%8NIuXwI=( zX^_Tqa!#7|kC!jQmnQd+_F{vmgZ3!IM=ZUfFCJM8w#nP>M;#M${pN+HP16$zx{UP1 zY%;Bx4K)>BIY`M15q!!vfId^zYi80{n=gCyfYke*-mA+_*x1lj*uzJ}gx0pRV?FZY zyLI_qDyW+ZLr$sgQ<6xIxC47eV9(h1$Ld=!5%%>qCmVpGyRGM%_QVaZ_{jzzD<4EJM{s!j{@ImL?d=Y!{VF&=a`a`5WyMlf!5GJm*}ZvnOcoYo5bWOOk}PHqIcPTrCkT9ya+-an z-_bkORn4@Ssf!0tkPin za?g{^W5nx|q)3yI_#wHej0DY?jLcn_OGUB;Vt9dbaZ@s^HCN%ps}t!N4jHYr*dfu& z8V+{0JS9)D(!i2@Alk71o~O92)k>V@^wb*wmCuzio<4pkc006M2KQ$=CN(b|Hw-K> z!Ufd`*J&e|;P}JYW~Gq}-@)Lq0o)n8@}|yvjRvR_m}k8Lq)ju!#;kxGn#9ZDoIV;e z&N;^Y<$XrZIX3Rs-z{pGgvNXgK14#v`)2Mo3r}q+d4Xu7JZTyO=8^P?cM5E&z;a9j zevWL;GUQuwg!%1Wb5oudwMcihRK7ahv)yfZ7{Zz1QsOmthb72El2q9fN!l%NqLh2Y z4qZ#*VP6iY>)F{f{73~Ayv7?t%!C*MIlu1iUT4mc4(6DdIOjMHvHors^D2~rI?tap zNuv+f*W`(YT+K}+BT7OtyJV%+8d|;FLa0~EVG2GG)+#Bz}kfm}~g-`eO^0Hhpvsji;Y&J}pXgJBps>M3Szq~xO zjBm>F=-=X?WxYK!9LKFj&9`nK!g@!$Wl2C>I_{>v!|hw4Wixg>FuvZL`?>9^We$U)E~b1!s48tPfy*R`tRG@x6PjI4?ajqnn?-KO~)O1c_UvN zoO4Q;S9g7Se%|REo&WvCMZ^z4g&!LW3!T^%wQ$aP(Q1W|WOtEK%kX}0OBLsr$Np?u8eR4<=5W||4h(?~2yQp*pB=MQs*F5D-m#U=X zcpR}x(%XMePb=iOiGW<`U7LRm*M@UkL60VCmZ9Dvqs3U|E!ILLG*>InDUr%` z&)wbqbYTf3{U**i}8bt#AJdfU|CXL zZb!+8@l8wcLg^)%Xe5F{&D~+!E-)ndCdDRizpK|1op)k_3Ml1mN4W!_6Ajo%QBW=A zlH06kGl*p6W&$cHj8qyoDM%BU_3HC+PG3pGu1qr{d9B6^UTT*6i;Ei<`Hp|>^;~t% zIeYZTdw{pT`R4Tey!Fd3DTtB|F!JN>;bBtG*dmQ3QS4wU-caD;E5e+TIES1iucUBG zu>{r7IHw(cz_Zn`W(%#vgYmvo|GDhZ^mZKiqg#023n zr9;Zw?)Z5FAU9&UMu8}T`?#o6Amp?!!U)xe8Ahl_WG+J7$jzftNm9-+@gtCPZftF> z937E)y*~BF<>lk)>9deoWoo1sq#?1_M(oC-5+2QdOELz zjzyCD#@d(WweD=KcgQ3vKa))EV5f&hulAcMkquPf)!KsxCK^FjB?Tx#E>$WkAuDAX zoDca*a{<+E)M{yo^JRhkiMRseI1z-ag`sfk3aT0 zvb;SpptmT_VUb!7w!lV;^+8aIItZ`fZ{d!>uvX-Gx!3fbf2DB~p_ORN4Wb+c z9iPb6W3+*sqv)b(H?4Mv*Cv9~nyp|Snf*<@ZgsxbXta~-nn5ummzxiha|)ZtX-nUK zUwiR_zkMGD9t#mkE)itqLJ5EZIp(!m${H{gjW8aWVz0R=Pn3{NlZK0Uj;IhJmXSMY zUs-^}k76s?O5-5Sv!quIwiv%W#K^U0O8AY*7kjNf`}6aKaE@jQZ+VjB>0>3lOe2rg zE=82Mwt{dpHfq@CHZu((R}a<->R`HFuc1JAd;9ipAAShu@B{Voq0yfXyz*8P6Bmml zsgV|@+Vd>hh52vAIfFMn!25AL;9t+#2A|BgjfNXKc$j30#7xU>rD4xWQ-CgR zf75c*BiwF$J91Y|5vSxmKp<`8YpYbC2+pffIiQ>%lIu3fHjr~NLC%1j0}04FwMrF9 z7eO4zA1a;f2k=NGT2Bdifz*EpV@mVaN*&Vn)Wd*$fJ%AV3eejG=} zWATZbtuV893Pr|Ht(h5fCHAU{YGcVc??3wJpN+;}KmYu1pMBO^TKfIYop!A@sFs$@ z13Zqqw%%{|c$bzspJcH{qO^%FTy(k-a_JKGr2*T0ODasloB|B6AV*71pnYd+eXqF* zQM*ZG+{D_tzOcFWYtPC;NEp1oMS>x%-?ebKur^`_M7JV zAFf{}&Jhu6a5}`9XUdh8hA&csg!Jc6x`sz!&leuKcNxv)97}(nnK_x7O7D>LO6I~} z$WZtNdQ-2*QkB6u)=EfQ*MxH@F49o2pZHu07VZ>Z?mBX<=H7+-yZa;@awaEDn)=_T zpQ3R30^#WY{*zC_KY2Gd$0uI)lRSb)yxePQ5|KE}8IIgxlO*s4+nDJ?2#u>8`SL0i zC3Wqmk00tq?{06%L(|6tIY(m2>G`?q+KG@ek&|->)Ur-Ou`ZsCR6u@Z#3f2zM!m!_ zQfcXGy3Bxd4S}#boDq}$$Kjz1D)^4{#BtoWFIp{e9WUgZ%+2**!_iR+K_zld_#VsP zoP4t<%nNY#@Md#OZd1~93iQa1D%8tc4oRju%Am`k=)jmDVNqQwTVRaIR=@HL% zx7|E4R_a1&bIV*d|H|``FRxNj!VR^Ygci7Bb#NvROP^(C9P{Ik8f1aebr-D{)|}BafLdi7e*%lcpIS_nMozIVUC` za+n>`*~5DEGubdKb%sFBe|C3ct{T26w~ZO+NEqB$`|{O()2f)>)a&WvDAQWoH2tO8u8Wz1{_2V0hbB9DUcH27jdeCx>7$?oO69d;t(tKR3b*Vv=Z-vb8@IV zzS*(_KyNl=J0s7Al?D{Rpacs-D1MWVikgC;N-S;7%!J3`cH}Rz67rxK9%Y3EaL(Pk z9Tz~8lZkz#gQ0g{ef8tP!H6Pa}`> zmdrU=g7;3PZX&{@kAJwZAdvHyKR>d*B^Ftn(DF0X_kiPz!J844h!9U}>K0;NTr%eXx-zd`sCBLB<4c_g zjho*-+ufFL-_6Z+78?=_X%XlAFh9TQv>&2K+tK)O4ldcPF*n!PILD}v(_40`{xv(R zJEdg6xs>rT89&S!Zv47z*oM3226f)>Xi({j$%{Jr){8pwDj(+%=#;*27GlxJPoUCp zq(RRt50OM6-VQiE!O+bk;lYh=-p`52xd9`ei1U(Ui&}|h{E5{GQ{99`7B*Y>$ZorE zbJpqI0uOszD4BDZNG2uOnf6A)U5%hCef$v4Y4<~&?(%9iHGWCHh#xWg96!!!C$L%U z5|gFp&w;DUD=8(_H7Uj)ZX11&veMPo%c!Ldp0#6vGPYbct&$Ij0@Dt?3k8Um$F2;UNB~l3gcJ#~@c`%`QW+c&-x1 z%;z|jE}t~8#Gez%y$IbSkYq3L$lF#{%vlH*NYYSN1Fm#QrE%}s?zRR!VqhUQAc@t= zN}!Gm9v?+Y&fOJe>?>Zb1@O z2NLA-@;QfF@J2Q+sPFhn&g5n80tw`FY12A1Yvop})#O7A(mW7lzs@J)$~k%(D3EYO zNp+1Jqt}v4GX6@}O9`B#2u&b*{-lXB3Uc=nL_aTx$=g;|w8T@qCnbpiQ|yfXa4IE> z_(Rf{>_12bm!t!YU}Iuw%DQ0iAS>+FZkuanc%&H2$~ieiwdiJ2cAD>?G`(^@i8(R5 z(m3bFjZW59g%4|fC7PU+SKe%8Z-LJ42>E8enAa7zKzK2)hWiT4*BY`sjO1_*7uUmM zt6hH1Daw+G`Qq}F23d{2N63{brRe|cm$7dfM;6A_rV60^0av8H?i&9AD%`mVTn7gg zK>#&r0t9ISkd9=p#gmE1z8Hp)lg~K79oxIgMSv?=#a&z+qADP8#i`@{#E*U&&g?E{ zXLm`8+`$J!l6PljXWskX?~$9J&SLLFeF;s;qLEC$eyma!R+>PhqaL6BT-0$E(8RT*}9;S( zPh>7jP_S6eYd;xPtE!~)q0xACKJeOs);SlmzG2enT8S4)ddXW4;Q zW8EASAyIz*dXoK6J1XDQXP?Q~CX-2)WnqHwNB(Q>eusxFp}*s+ga;Dj^bPX2j&r6? z7vPPO7*OzYI6#pX{9IOhkyA&MY_AiPL)N#QT({?l8i9ncf$i7mVar86hGEbuR(V<( z=P;E5*j>dcLVJ~H%S5u9Vd4Okb>A=>mDOB@2i1Id`mp|TlobN6L8K>s44*g<4o6j$ zL5XSaIuz9`*C4&SY{|@`wf+p)(!j~IGR`qqVX%Mugyb`cQCM#HH8)Cm(uM7mI2jIQ zY@^XguNz?VgZ{#Q&E4)NbC)>z5(;4b`mx6}LD_T0b|A!i?JMosgz@X2Yx?~Y#2*q((b z1jrz#emv}s6rAp5!5#|H#*dM5pbG-1+&bRUa`kr`7O$H+(vEUD=jHIUW)@qr+<5+X z-_^&$m2u8AufP>iMD8^K>Lf&{N1oe@wGWY zsCm@DN|Y7-u>?dDq?jiqDOCm-LY>jkQLERQ`^kbvyQ3R~P78i_^m@~Rj%JfVebZ!- zPbGDI)rhg#<;eXo7m!NBrg4sh?T|Y%s}(d0mOF-m_qbyU*?C{o@1nlUfQPr>5!6KVgvN`nG$jbsgS;9L8ug(u-!}Or?B$^XAQh2lC?ne&Mv6$~lCD zN&}*TMBeX;MY@HtRGxz-v$)q!=N5<8#XBm8qvgyY-EgYfxwrz=GzqiW%&ek6*0f)p z7@tY`2-%}*%Tq4Rd6u1ov_??XnK^_UjYJ4s4F08%Ge;eFIGhtB!o%x!YvQX!l9H;* zSti`foGq6@K5;@=(m5(6qb$0*EZ6$8p2gOxS`)WhZY{t-mR^L}GSKK1$9Py(g7B6Z zEWuFXfg$8bF1uFBGL|+6wi#lIi?w*e(}%KIN(|A}ufaJw4Ub;zK-?$gjiitJf~B8gQZ%hKB>cwX|G#Zh!mw>o)vqcO?5FJ=3gp>YxJF=T4$D51pzAa>O|$FP4Z6 zyG!#q%BiI<8mqg#Bpwy{?d5Y~*P7PQ%R@(tXR}9)SLFj(%Afy}PfBnAo-x$X;MaHW z-jyLl=!cufIk=fY4&h<7iGf8&-V3hixd+$LY>R{)^=FV{;lvM$=fVqhq+JmMZZKQX zkJ&)zkX28qNJXWve%YEj_~8z2ym4>mx^+k1s0mb*y#5RV-HW5?OFw*P#6r&ETa`G+ z8dW?T_k#cp(xA}#0Yk#NIa}vi&0TG@#~ZGQbBi2&qkck_%UBMzz1A*B7l)U+8H&@! zTdTEWWQRM60=p9D2z6xam7K?0I=(b0YuKv$I*R=ET;ZkijTqm~b=@+1Ddil-)zuX# zsRZE%eqF*4w}Er44%$WI#^6LhjvuCZkYkqBb~YL1E`2?>=};a;e&%#mU)=B`z>-Uq zC+2Zy=H~_-m=OWR6S;>Z%o=k$6v!$7B+uT#v<=6pFkb>}37Bbh(cFZb^0T^Y4yaatqL>Gq0<<+`QFhKtA4 z>Ux7e#uKvDk8DMf;DO!AQ>=8=5#f$vHHY{aGKE>zPY0nyb{4l{!{I zOE^lf%B+~J0^XRs^_!95#JA{UZ4jRw#-sI;S7gLaa0#`o{*Ue2-;x=tE8gPbBt@nTF5=XR`sMEHsk3r4N}5$8XC@L;J+ z=VEg_w@c)^62Bvy+ahi%M4j~F>x{NJXX+Rr5e_y$>1L$K5on1(s**vZ9;sC4Sj+^J zj}l;}tn4`FHY%*$dVEvADXq$3l=S27lX}GtJ}kgl^XrpYJuW zY0U3VXjx4DdRfe2uKo5qRT9ya8zDultF^0Kx&i<*H`=LId^fMgjoyv+v;DpPp6S!! zr{l}UZHEwb>@gPS!xHwqbovq9#1?8D>li`viJ-V&27DiZ=r9`mu$y>PKoJWoCH|)%1hWu0gdgH;WVcolcEk2SLkS{Cf9pJaA&a zibm?D3oXObheezt#HBT`tZ~aOt5?|ehH*~sZOmi*eN^=lp!H0_R+eFWfVU$Dx$5^5i99g|ebqng}Zs^GejIQ~3!-D9J8R_VFr{kFQ!KW`Y|PoOQhY3i+Qt|oX$z#oc5#_$(VO4tX zxnZ0$z8wFb{j%qcQV0CDGo_*5f;UkcQiVYUOFj3c!h0;4UCv-BTqfZ$#pI5Ga+o1+ypDGgk1R z^w_mvoP(`DUC*}8!=uA_`qXL}Sl;dKaLODp#~sO*r8EW3sRB76s$s+H+dzNVr?j?K zW z+s#d0T*Z0nnd?YL&vQW?2#4x{C-PwknaSWELe9jMU2C_sd#PEBXX!z8MNy+gR%%k{ z3`UQl(ibdsKuU)jUH5O_DivK>Xd!4P!Wi|0RHY7(ECuhvHEsdn^Ekm5k8`m0v-a}- zvYI9zp8WgCoE>Vl*lKSPez>tQ-Oz0~Csxl_R#HbsmU*r$h8$Tw>X4+xc}ijuB-zLr zHn^~YK%?Y!DsG5&gE5L$t1|WIpj)&vg`%?QUKV_?ze_)51hCQ?;hw<&>#!Onl%lEy zL!G@|qNJULAFW zPw=_RIYgQs5l+!uNwr}$!W9mwg~58x5SOyyl(dkXvv3A%_)wWLRo5})s^Od{K?UBh zA9ohXg`EBUm*0HzudlzBe%jw>uX@MF!(WrA!?}||R#o^gOsI_1eN-ao;0OHk)mO#s zTdg^3RZz#U1SWAS74CGR$i&-K4Ry+riMa>f*lLgObQ8rYO2Q}Fy>||+LQuUyXMtC7 zkE1Cr1ym1tVwPabOzju0k7ESCW|?7*H=Gk<1J04CSDY!TSRq3-Th4J`dhVRVCX-xZ z2^ukFJp5&K7et}3e{?Vo`*BJ-kn`&D2J5z<~sTekfsHhV8 z^9``0)m8b1OW7%jUmMN|GKX_!t<%ammR7C>j-}Sq1vwG}j%Z;>7w68|(_UV^DlYQc zb8yH|htEz^-rL#Y5lF=Z<0r2tDY__V-)dF3ioapAF=k z66wUL6>g}L?-fJcWWycR$tqs9=*?bvQ_|SIhI2x!;hdz_o~%uka|{niD8>g;RqZ%1 zr}Dx+K^OD|+4{QWuh50Z(FX-Zox!y7+k5W~FD74>mKct*E#5mfMR1On9{`eyoMS5s zb?^r!5k+9nS60fd1}^H}M+aSVQt!o%m0is+%$-J!?)np1>{}Z_;Tx~aiyPzwVsln@ ztZbmbjAbj$wu!`BN^#CR*RLDuOynHt<@xi)p~8l{s77p539UwpARQ$I(=8qRw=T{h zu-#HA8;*_B5lGQ*TCgNo z8g{o4=lt}^CziAHLA#iH4R!jNA{%gy(Za;J$=bJjF)J682)#Qm>n?7$RB1*y!|ru5 z7T50G)qq3FX(<-XdvT0)*e)9wBgw&$0iJ7BmKEa>9&TL)b+mE>G$x{d^yTX_XFlH8 zNTE(3=M)PH@sJ-NzX+@8{gkee=t zt6JtIEb**s+$qV%8GdazC&(JknJwqsy>g|K8BBnXaoiTZsnG?rV4cV~WLQQI6@sXc_mQURmIW41Jrc;oN0s8b8)xG_nPPmIY%QxyS| z3VRYEBJ3H0#))%t(~3q&&0HvXOM$i}?-rYqh-BW~X0Cqe=7NTELTnK@r!Fhxoc6PJ z>#&8^pRUi5bJUVjoWpFQ&?TKG?i+p08}{Q&8F9|vu3bxUMfQpKGxTi0Il?g3G>GDZ z9+AyN8b=leI)+=m9M}4K1kPIzn%F?8W?&L47ne0$Vh=;MaiUlXu%VbBN=@`AYGMVg zh6*OQIO(mcuc_$bT-<1Re5YG{vPE(&`BMsYs&fvpkq!7q2i=eu({U@F`OD`;<0AZd zzOuqw)Dqq^5Yb{a0Wz^lMG=SyRChJ2UDdO4#v-odJRD9DMteJ3#qr5rxf7g0&TtPK z&Iz$o!#RQKO__59=PpPf3-z+eVg`haOUH>vt{ltBX3-ImeuLlM4~W3Y)we ziLorP^Gz{S7l=F})5Wy#d0qVJ`c|B1iy&<&l>|=KLEPKhk^k&T#Dat$Fkrj7XdoKC zClvf_d}aqpR~)y-F8(IyuHl>zn zqx5;gere1(N9*ebEvd?%cxW;hvJh3)w~4YYz$+9Fr``tCNpW4HT4lo{h^q=L80{-g1d|&o`Sq+};&^jHwt zSy1A_&*H?(yC;O4zK>b6;hYegBj-#(i_bZ|oT^o4;~sI&|33J@Ac68ia=EQU*2j!Y zFi=0gy?Bv(5kOLmB2yJlm!C|6pc=9s#AG-3csr5ucIrULAylMsFeiiGD^RBv&VeQ5 z?LqgZHIYXLU4419y6U~lMi1r=yNh^>V+l4&H!@s6iE8UYrnm$*U|=>8zmdowsPJj- z(K6BPzy2k`oFeB_&N)PvyC;Ne!#UHnJkA;Q%Ber7R=p9NgXqyVLSm3lZII)hU8_ZH z*CRHPeMACgdWVO@i;s_c%gfyU{;(E_^Dgm7()>_%_EFSY#s2a z7_^F|v52<-@@-8}XFR7woi8_nWNa6s1di2Ne+Dw@c#Q9pV3M4f`5WsE5kYTYd7l5@G$R|8ac-zmgeOy zb!%%gx{-<8;5Bzax?~m35j(j?|E;|isi)J#mB0ABzq>v0I8L^xirN9Tg)>EhoQX$C z@#F%OCens;LTuCuhke|3&w6F`!%aSDs(Q0Hr-~TCD1c0<{H2>D5F*1lUdWN2y?CK( zL7G2bx@6z~>B^O(l@(jl+sJr&UTrLUy;o$kZoId*;}&^UORDG$BC63uaZH@7O80~P zUD_Fc)!DIQ1L;!1?^+qokw3tRnkcF>32wyF&iIm4 zjo4oN=icqj8}5Q|R5a%p*QwRoNwHVy9$HPd8z}=sYkbOFL{w09IZ!sU>=AHdc25ex zkJ>KQHDiVGNj01kVuMzg?&eFUmvaJHDBdc+qjzFWAVeYO=o_eJ`@)6G7cUU!FTU0=4;+ek=ZSA}OaEyYYagNSN0 zQ8uCy7}kg%-x)UIXWlxTx0=^Va!y$pr(p-(n|HfA{MCQUvb1&f`?qh|R=_+2u-a3E zdxl{+W(yjelqC~;SynI@!&|M!^i+Fn*xt&(wt8u5#=`j&bwt!6;)GjR2k{{>rRJPh z%EpDa;hYd_wLE=%so|W(}56p8f>@&@0hPf zvVq0D9#`|OFOc=n{tG5&&w}F6sD3Rk_ruAT#TFiR))ME{C)@A7E0G0Wn=6^M%1&qb3# z4oA#ch+sRh5Ia#&kk*I22`6L)e+Z=w=LFcpOJ`0J=k#WVIz(5TMt;(!sX1kvHPs#QBrD?)NWy97}P_VH@&4Gb^YTz-MDIR^K8WiG$r8)Zxk`2 zM&H;QbgQ_g8GU0xK_KdME*!CY;%Wg;pFZ`}oxG=bZNxbQKi!1rLePFRHsl?LO%NEL z<3QR#B4t(jAif#un9%{NxJSqIKnF&G zS2djb{MzqM0O!yOnHLZr{`-gD&w_K34p4~X0Gil>DK&1@VgL-U*w*w@Jw=gEaZa3F zHO@i54l-BV!Xv!gGHuT34H_I9tL{cujbnoXY2MVx8D6Zon6vvo`(^JM*Odiu{S8VR z;zCUds}li}W>tzbZj37z!5D)BYK%d!xG_YwERQ`_L>$KuOg1Kj-6p8sN`Y)W)qb5Tua1*`MY-ojLD*z56~!Qbuz*h|zoZ+=JjWAA zo3cLYenU|NS_bDFUAnZny873B1S*W1tX73E7Y1RsS!6v6xS!Pwq#;%$_@=UmRw1gPXpffjoOv;9ocHuZxgma=AI zMMKH9nFN$PoCGCGf+Ah$j)Z`e4BuYb4)}?;poJ>YKtmCA7SXDj=ubO2fZi=1iz330 zaW-zpIqz;?nF7wi)5Hg;9eS%rmuhg!!uG;oe^3{4)U2dj@k9PX0`ice=A2W@u^!ll zNoHrNr5R5UQc`~?TrtTxj_mI3CEjWc!qXIWbR7nyMnZS1KZM|4uI{Uz$FxI80D64GEUvWjOPXJdCkE z^X|$O|JAQ2wSXV_oFkt=WZ6%E*>dh}CcY3dZQ6jcQ%dL`t?H zW;L?~-ChG$+W|zIno3aN{4x_2RcEREY~1Dm;*eTNrI<=-J0`kzoI@#`0?x77PcWlM z$lKd#Vu5XT_jVDzkMw-HTDz|nN?W$DjywcVopUO(+5;o8`9Zb=ex9KpL3uTC=AfcFO!{MG4m4$T6|Op0RNs~as}`|yVs0~7Svgo-#Jds` zd@d?UnR5y+Dt|)Gh0i1*+?&WG3iRm+C&y8#gG3e?rm~c`7a#s*WOk`t?KsZ%MjGcO z5p5Vz@p~et_64QQ0b~y8*Xq)@7HMb3Ic3f%CGhR-y7%$lFTM!cT9$Qe9pe_E&6+PwXy=WYNIZqklEg*@Upwd+e57qvAM>^)6LRccXt> zTa(i&cqk3>sx*0N$!;x*%(RMv;$uabkK*WGzC9QP95w1oBl+j`>jF6ki;Mg~l$QCS z^O;~#Cz>>5eEB8CtEeeYHn?*{6X4G3>f{p3OwO$kEDAX!XGs^$5^1yD@kuf~dz~N5 zhJ_^3Z9URRNv9Y2D49tI(X8cca{#fXWu#J?nsLtD9H`(hLt&KqoWnV#lf_t4#kg%@ zCx*lA>KFu?SeCy+L~fxx5h*%iP#i!fyQL~e(qH%S$JHx*@ImeS>3S&Vym?V-BIIUD zRZM-d>`TmX>UfwsJkjcjeMJ|GOHdb_>~DQycg?Zrk`^Q$86c4B`DPssql%mm2T;Yz zG@_8q3E7c-<*0$gzPoF;&Qg?t;^^O4XUBncmmuc^!{5%d^Yb}pcmk!);4&+-_e`cz zq@*%lZ$A^~B!Ps3_ri#VEm?(9$jxZwDm9F8(I`U^br#VCm1r0{IRMb8m67%e(c(y= zo^j6bIA6Niuc&T~I7f};3{b=`nra;=lGB_rkzlhZX%)D)U7xrW6TVVIA<8DVr~gD z;un#$SFjE$EQm+(m*2Ic(n1d3hz}swt{op9`S8Qq))_m*rKQ@ZmH8;1 zJke{RcKrBF|NF1|LXSEjlXFH%rW?)?d-2IgEN$|fw_Isv`^g>3XL6+hVvI|zjb_rh z6jm#9I9@8xFM?4hbHON}cv)Z|H`b~zOh!e^A93L}eO1!<08$z4*5i%_TG5St#n3ErVLL(pP8`&ip%d~sXXv4hvqDU(7>08aBKg@K6@N%X zF1BvW#{NbZp|GASkxJTsvSB&rMgNK8rHw_Cf88qYh?a(&I+7!f`{8@6f9B?xxXd^w zAuTxPoJzNlGnd6VPLUEB?C%dlkduWvRXfh)JP2v|A9wHCf=+_fw?4OE^gC;7{QL8b z8*i7F`SIz42jV9TeoT~eYzwIaR0|dtutX+oJbXxTeA(|&G3CiGH*cmla_&S&Csiyc z)JiFo-L_G-m1|kePhU8*T>JL3&7Zz~j=b9H$&;Pc)ix_*N9~w$fH@)flH(+``mdnL zf_`oTb2??lISH9E&gmR-=Bha-h%v}~uQzg`@Y`_l3T}Glw>p!gkl~>M#L3SI(<|pB ze51l!?8x98>a`^Nt={7wzv=(|)z)`kJ%wR-0+#smsS5%*Z(bZv7C~1QX|@s7a1!_L z*UCx~%QPzx*hw`%6Ny#)7}++y7G1V3Vfuz^&m`iv>m$1R{_8%z_w;eW=S1k48)bsh zf^&vR)ak4YO`acMd$Jiqzf;P_)5JNGfSkD+obzs!J&rD2y0fwu+59=LD^5-J(v1)%*;FR4;by5Y@!K3=qOGZY@6H8bu2Z5gz{O+vf_> z%?`=;D&(l|v8-S=CqHwGOi>DyikEnQ z9kjIZWHr_-p?F6Jj@^tU&lr=m@t?$kL7q7peS7u2*^$jT`P|Z&b40QKWTUQP1Hpn? zD=BAeM8)K{JZu9bTFqZ?KWkIX&x3GF|Gs*a4!0A?luLNge`4*6pGffLufus{DS;ei z5_y=De|*8aL>+(gkB4WTxh*CrU2sln+4d)#JU>Y0(F`yK=IP)Zu|u+2Rd7Y6b#!?sXSypIY&8JV~W?uGt_Gb9khJP*F*>w==)Ou^qQ97M*PTR7cTy|$?$4{z*!Jx@T zMo6%-6bVN|Ta3u?^Q}}OlGU3ReW$T1ib4y{!Kv^@@*#gK(^sv@wIu^`&V+N)2g}{m z+bFS==4T?EznES!bD}UoL83)%qG|o^tEZA`(%D8y16M=^#m$XJ`Q&i0xHvR<30+bp z>u4oSK#OKbJ+K0N?7%PY-w)o5JG+Z*0W18SbWU9nQZ6sDJE4{Z;x~k^WCmKgB2@ndErSL;t2Ulrn zJ85T#Z(WnxC&8ZfTNTQvc5vj4{F5|RB-W&@P{nkY7nW1!oZa1BO%XVNatLF8f4}xd zA>Xk53nF&(FIFa#qoO|9h;y)l+C>LXV%E``r~|ZaV#=5_;+)+1&hU0W9nMJ~_1CLc z)#BFNinWQ}Rv3UvWSv9`6R%=gvMjaF<|zCy=VJBq#f#9g{;c)1s{?Dv_HQO_4*%`% zSFWfJb563o&>iDn&!5LUq|$p!BaJ#fDUO+I z`lU*$Ri$FHU>QdGrW{3ACd4@l3&*dJd-sl0lNr5UPt2#$r%1W8vr}4pFc@CDR;)8W zpKnB`fB8TAW%C->bp`O*XHl^K0Mk`Bb+Qo{<9OkMDcTeha7_YQ2{l+L>O!~(+}SvI z97!G7l9l?CG!P{rkd4u*(5@VCaTf`*X@a4Npr)d@!88jgo4)?$otdNi?$`U6H#3r> z!(o`Yb3e|#=iGCCSCUDHb9lf8UnP||$Alb~)MB8&ZhzLTD{P_?QQFV zm~Vmm_wSd@PNpjRne|2AFCorhX%~umFW*l;Eqfl%-h5N1mE6Lo?5^c7vqC$Ix}8IX zth9;*C}L!sv(qmDw=Z6Fr;>m995&~eZ(HS@$aNE6RO1{s$U_J29(&TyNLG9fk8|9U zG3|Mp4PDbyx)&+>Vla^N^QF^`R>Ras=R9YH0chvK1%Dy%N|C@QpGlw;%%F&K@+8tC zFWhQ6vW0{^T^SIo>Z;}%7(?3rSd_z|e3-|=&uT7qQ3r<>Vd=P1C?&UG7(rOD&e}v9MM#^wzkAb>!}N^(b#Ucd(*;2<=saB z$nE1~q{OgFbJeimXbt4s!+tE@*dA!PTiOppzrdulqgd7bU3w0_~b2Bi; ze!}%CEFl2@&E(`D^AMt33YltnJX}PzR+@$Ty_+l29-%KYEx5TivZ-nhJ-?2ObGEkV zzPr0CMw-TJ9^Tm4=uNx6UOBp2KJi{{Ldg3np&azQHUQ_Cu0g%%!cT8!IgNTb$Ap|* zt|-bIjeO2AQOeYfPKWND?QQSBE&~J?Ey+0&d)!uKT4Eq_Nj0Ky3Zqc*4#HodQCeJh zwlIV{4z9Ga~Dj57hKWMF!;jf)z$7UB6T+ZPq zjr_G^9G&DPmiypN+Y349gDHDaOM<5w4Kuq>;GyKg6+a{1-w%p;7OQY{rAi*1rBG5a zYeYK?8?c&NvEf|;+Se=%N)3n&hgw;CF356@;z}g0;+zw~m4nMU$mZszCRJ=^znZ-} zY3=JuRnAG$Ja5tq-y^_428iXo@|@GTbV=s<`1TET4c~hk%syAOB$=I|K549%bEJAo z&2n`oo9b)ioFsrcdne|gP7dTWf}HcnDUr088*#CgmfVs-rgh6PjgcXQkhhK}DLHu0 z_QBMY+wSG|Jr1wQU{1h$GV(75KRI*8atc3bw8BQM{oV3!&H8nsbwa;ip3n3o2@vpd zM^=^=&2{5W&*|gi91e{_S>Zg%;Zo2`ae`vNfOKIcsN!+E|JB7{>j1r%C<6J1Iy}`^ zj9gezsolXlo9gD=Tq}L7nFH6_OM?tVymyvHZXBjEGoO6#bsG)m7_02=?zY=)H+tcp z%*+!Ll|91NfIX57pG6*PWDGjLu`&1HJsz&LE$e z?cL7RH5JMzDuu}pIWNE5#miLd<(!%aHiDdEF_OhsN&k5$#g2=>_kxE^cxcMy%a;yN zd6iqHqqwvB0O7#J9cE{+ftHT*TxFEpcUih1{gyR2bTXkB03f~A3Su=|O}B)Jel*=k zd0GDh1cr}u6#sZyaw|g?S_PK_mr?Petm<}?;jI3B7ja{Yy3;BUlHN%X zVBC8VjDT|>maKp}P>osw_BkwM~;jwawaS}N4m<+-`b^YbPN@DruNc762xxo#qZ2c@LA&6NjQ zV+r7^SJm3D;Iy;_{mK9~ZS3m#^WC@|r4mm4)jjs4`k(c64F2c4@7jNj*i`h=CF@k< zx@Rf_&z^leJ4+BAZyom*@y}ih)_3YSkq>MA@1>V=oSqvPKF;~?E3Zh=gsBShMg(#~ zsN?MxpCmG^ATD`c-?JzQUnU$zIUA)$Hm+|#pj0>h$=%h(Yu7?$u830dzyS1_kp>UC zvKMZgRon}{#Y(BZ6ilJ$02o6W=q;w9feOYeON%BHkx~Zb9B71;64!u0J6ov)C+Dx+ z$%mws<>=AGM=ufLbvu`Z97x-W^Gq zt;rtQthsk7UK~r=wSWOqqM0F?WI#}4&Kg}nx@NYrCedG$KRisV>IR1q9^ez$p!^W2 zYwkcje;JqyIigfI9mq~Y1Mx*~^7Mj>-dpRv2>yQa<|s1e4@XkYk?_N_IEBw#htfei z4-)6=*O&1!(JnuGubynd5)zQwG_$!GD!Wx6CmAQ_JlxtspYQIP_E+4+Fr^APj7bLJ z9Q5hQ#^w25{W{-l{P9;Y$f2AqUb{xne*VTA`A*Lbi~#3w#grLxNrTHM_vqu>HwNUK zTYq@_SnF6R&XLK5b13d+trs9XBl_@G?QIcH@0+4QuV z5O_aIWB2wsML+bt1}ScN<$>;41K2>F#3|7HThH$5kQa}0>|uS*G4H%xo}D<6@ATZj2y+f4fI?tZgw44*>0s7e&72V^vgQwU`^mK*mxUZrz1{EKToIc| z<10&xQlm$lYHHWWd6Kwff ziTn3yze>q4B!?V|TV8pfGuGTuXM1};lS6W#I47xw!9LEhASX!+nfZp5N)gNL8ME^G zggK|T*6$ZlUAlWsvUZ{vBmaJU`^JErGl)tdL%^HiK**I)^? zTlfCm@#Ccyl2H~6@UsFgYEj$dvR!g{`qiW&>zto3t0%rYy}^Z~m{% zS^J)I)GzGa1>-N)*2Zo0izNq&b25e1PC>o)2UAntc6UfQwPb*))SgFal{9~%`_|e4 z{T_2f>T$2NwYe{HK2gpQ$WeWE@+9nmlBOV^qD$oV32@G{6DL}Zm~ds*T&j##v@voH zN2I8u^4auso$ecx#?O;@_+Vin?yrI>13?HgNx}vdU_kQi^5Q$6++DShsRCDgGdbzn zrq#I!%HC-<-TV&|4vTZf8#5w4&LLWq81Jpw#Q6Q{YL#k!C?Od+=g6xEhnn|JuP{kc z>?*}MX8TiSNL%Eu3gi%~#kp3MnqT|Jf0mlczF#W+c248+{QO7L(>w7R?W{70$63og zD{+(cuVrmy)SzlglymA?BDYU~b5Q67NT6DweBr0pPBE1Za*xKyIZ2wsDS2Wu_r3dg zsl{vK*4*<%VPK&hs@3M)T(!N66RG|#3@~k}sic@%Q)S+jWoBb{GSHk21+8iPTlS3^ zpMjiD2|%gx(!z1o=B$0sIf(&{9xx^;9ZHR2a1M+?*bh4?F~4||Ck6z|jZ>H>D{5>| z(TWp=^;a$AVE0eu;Bro9a`Jq$SwAr3sTXq82L7F0SnzUAWK`~fxD>~zCuh!ltB_#I z2sp=!F*L?I6bb;_R6(8EIcIhOSMCk`)6`E3F7YDi$TmwRl*izQw{BQuQWgg8G@FcC zzrJi@$@}Zq_uiFdW>ZQrU#Td`yddYmz+#dm@R-H$1L;BFoFuJ=Ld8AyYc_$O?q7$T zQ?I0)!)cqbN67IU8=m*|)oI0f@6w5+JtOpDYKU`k2j)&?MeN{^V=nOsZqJ%``{Ko6 zaL&!Q-ZD+SEIe7p;L5&^oY;e@DcB=(hJDvp<4LoRg*t4R##2|WJT+rXOi28;ROI3Ws((- zlszw}0s~0Fq7>>VX#obT*{lK=`dP%VYMfJQ@9`pDJLk-7ZkmK+p64J-Kq(n=D14rH zWO;55OK>q`g+L;lqoYc3&g{a1i8`#98E(qfRu?m}x>v}YzxH%vBbk{A?%k9@IEN)R zH$ygLYHpxC)F;xdGub%b?0z|Q<^SxLPiP$19mkuC56aELClz!M#64ILh=LJK4mvDm zAqRsIB7r>wMJcMmhX~P48-+x{l2_W5M$)1`78qe~D%3g$ccGA?o@B=bU&>;rO%L@3 zRP8}A8w9b`7N+0b-|pKtZ)V=S`M0yO=f@W#&%Xcf_viD#-Yv*MAw#rEASdRWpoF6a zhX^l0Q8=U&pX$6*5P=a>aT* zyghhe(EuNr*>TR*tKODs3n4arEpX`USz$4VQ{)Oc;r$5wDNU;s2QVB4mzuhB+D!^3*D%DH&B zcjO?lI_H4scyPtF%G@vJoIvg0@4ZLI4!rr}<}zt}UGoF{{(VCzbL)HzCJ!5#*&jIXfQ+2NccppGlfabe$ouXlA>tfy2mG!x1S{>z&s=e+TT=SBJD z92km)vGkx^Izmq8bI!A5a${xfDJZ_y`#cDQGQ8T3aA6_ zx;rs}(eN+Igl7f=1K4E}+f-v?rAnoGd~Aw@NKc+Dk-*`bQ4`o>i#jpq1Zs~y`iRb) zK@LgFX7CX{+-}DJ-b)L#q}T~m;{kGXH=J`Xl|l`bpjR8@jdQ%A)al}!UrtX8sACwD zfARS@piVX`uBT8Cn>~0?;~D7NZ^fS`C&liotA17#0O#l;>ijl+@w7t@>btqzzyLpL zv2e=v+p$7@2VVZ`(j~_{uYx*z<#M7>pq~oD`y$$%*6qnBpOBj^%w@Lni?>&`{Ng-) z0drb+j5#Mzd;0$Sbl}_T8qYcNnH%`HD?CEO79gkYrFQO(f)zGY$T^3t?FILKIgOm7 zzFMEVi;8nVwhVRrV4gulDI~CsU>p+E)YqpPgun?j=KBs0i)|i1Hq7pP>`ENsj6+S- zSs!;GoIihjB_t;gN~o=r>oT*mw^kNVZZ@B$=dRonyW6trb!@>YQ+;q@|d10=1)e-l5aRv9l}K zT+Y!)Jb&8f60g0cH&I;fP$|GZ4{H$*qB4dZ@$sboN;;V0dbCAL!H(1ISJwP&h(~d($A8~ zU=B7kl}1J=t$n@LdeZ@T=%dQ|W6tp@Kn{>n4ml8#gCgX7{OPCcdtW|ZXjYJxMZ4v? zt$+0u_PeHKnb$$UY&wU7W*Rgo=A2-yySsSp8XY)*Q_E%+<}&o(jj5^j`2!#a$(%De zH1!|+*GlFW?J;E>kmABIej1A`m4A98f3 z3R|vL&*pM=zqUV) zy71D(fAjg;PnKQw_6&`^IEFvY0e#g?5ponsgBPe~#$>zW<0^fTnuL^fe{|Hb7jaHz zc9t(UJ2#hN&S{TuYjo6`%Q>99CFgKQZ5#f`|CZGu@(o(3Zh!ggGursqYZ~)9@HV~x z@~u0@oD-~dchkWl?ci;N>;aWVMm!`gkFtWCT3v|9Gv#=IrIjib^Y@1?pQ3GBZTmsd zEKFyID-w*6^y(BTa*hW@&N)A;p^hAKesR6$=5K!GEms^24N>L?b*yRG4J|jHxuM7# zD3_=X9vF#{71dc%esP{Ysyypr&haC5>9ms&BpZzjNoz-T%|mgP*7k8XIV#STu~>)#d)QeaXWJu9k-<6+w>qg}X5|CGU%e z7{e7*Oi0US=*ttQ=LQC+mY550MKi)Mrngg#-U&~SJabMhW$AkL_*eyXqRMa9B1k>~NSCCDM&T3Mj{e`H^(@DsHGrLc}*r1{JZd;~tp=RF)8q^WtklTI5T)6J;{ z=}|AcaL9Dv;8I+foOARh>NNgK;j6$oo%I?bOCjG>*)DRrifyrae3D&~o95^;| zh>%t8kB&APY;B2)i6JMpu&rA4<<)z84Sn~WIP$dsXB6qIBXTDu_x_!h?&-4OAK#QG^`JQw{9KUkxGo)AfN% zc`^ZfQiI=*j{a_9;$fWu@CUR{&Vax@J~kuGe$!R(M~icKi+0Oobe2_h;BV(jnD+|>3{^)3^Wh>^KK!n?eZ@@$BK&6ySqS^ZRxaF_= zgM%-6d+qvGkB^OE5b0*goHz&cPHKIOIVV^PjdS41^kn0oT}Wt@k@k0YTWgSv;|Mv` z>e2oC7>%Uw?a5RsDNU*(`9!fgP$5~lOpv1o#5q5{`DWv9q(=~x4=Ytlt`MbADj?!~ za?Xs=P|i+zd(GPf&&-(YPC9MbnAf9JiW+BTXSHmGo=%O|lT%-xdMP;vRfm6G4=*+K#UB8+(yq1j92V z%mrhGxCojTSED#DyOG_AiGOTv)_}(!JTwf0V5rGS{p?vb*H7oqyE+PIVQ@_0W6iD* z#X}cM&H;NM@bSi>sKjMi?hc{Kl05#vH7PownUT=_aBwi-!#AmOI0s(6F*U{ZjuZCA zCsCbbONoS#f%$#GL(JLR=-#ql>n$R?qngNUI0u(#MtxYVgvFc_pdop7b%k`r6hX*! zJm+lX7gagO338r3eVWVV%uYhxEg=UgluEjka|m_J;g9H(hd=TRp&f53?^DkmE!T;T zDrw5Y21#gxDTKZlO~oa!BT$OBonl^=02SxOu;!)WsN4Kvja;rENB74$IN@6>3oPE3 zb8~d;NB#YF%fn9|3M->!Nc|ZS_(?``q2}OKk0V~Fr_?tyI}0uaozi~+7;cB+uLCju zESXg83z7#0Km!A|`+E1?MjI=geDJ}+)vFNxXnd?Ed0BC3o5-v<2PaIBkHbepY`Fvni{uMVs&cHx;SIl5Fzc|0Wt|95+DsiV&Qfp6CW)csvU3-;>9C|;};M84) zF;1N0E&N#p2iozESp>fPpzs#sr(+!v;O*(iHi&z1%x{bwXq$BAFpTNw5}p@~RWZon z?|O#bJ~q7=J-WS-mqw^QluN&y`O*P$kPVFH4RVBc4FaFe9N8f9)9|lO;2hj3^Xkpz z9Eu}@NyX;|6Zr-LYUkz>-4_^@h5|aOkHbudRQv_3;NIVssM%Az@5{=U2%OqGAdEo8_cu|gwQjrRR@NYKCf%Jr+a|LmkI=3KI(ukP?4i6jthQ0X7bn zg!o$1vg|?O3YO>rg}(g02z3zS=Jwbrdvo!IkF`S>9+nlCw#hz}5D~OsAztY8{LRv4}LB-IxF!1K9cci29?yq<6)3YSYIvkAr-aYr;cklV} zIZ@j*IQ-dZWsv$Ld9L6fd1;8G;Wh+284W2+9L*Hxr6=oE{R44djtiyR#1hgF^U#D2 zoQnh_g>E?N0yB0f4RJv4-M(}%_%C1}K(BMcMBTSX%&ksc%Dcjq-#iD-T%+JO< zI&nG*oI}?NoRG1AUN7{VIS+83DAB^7bC|n0pKuQXqW1qT+6*^PivQ^2Il#eq_yj1! zUOSnf#yN`1z;tud7E11tG2xsPsn>^mvzi`GPotXST2Tjk?M8o`!{DSqPAKO{$l2(Y z+6>`ugL8^^M^&{M(sB;!a2q<(6z4Z3zI^f<mnN{>l$+)6i3Hf4L#T~Q23QGK**DAht^RY zb&SKL#GDhwcuBY1DMJejZjU-y>cctsUZ4m{6%?m|i^z8m91HOO_38>LJA#6qIJz67 zBIt}m7=$dL0R8uNMse~+6I7op9(&Rh2~+yH)bs-f>PRt;Nd}~Rg-e0D+4Ur9D(C243hK-K z9E@?)sgHqE;hL!Hk--==4{rvzg;wIxyZ9_;#HC(fDxwCp5xQ44HEx zC+cViXeg!=&e5e_*F7KcWh4+hlnob&KdT@Fa3eW{Sbv;zbaYgyRCojMc7}g4=al|D z{xm?&&2HL{1n20G;|PDBke6qR-<~BEkf0c*(h(af`^;?U-wZ1JfO&i%r_V2iVu#AQ z4#*+J`76u4xOT0b19Q={XTy9xNA*a9Hpt;9XbZl=wnSpCv;j%Z8CP|44#x%35sLoZ zJ7xB|3<+O27U=WSfmNL(dDH_rbmtpxtHjZ7^Wq#Rt}3Yarpz&XSGp+C-n?C9SPH{|qCXTmul>gF6GYT(V6_0RtLU=8~4^ShLk`s9$&SA3vsD0Xh#$Q|u; z1qJE(-`!fm1baiZLerEjccrUD9p#lHniQRIj+A;GXM)C-u=eRjmE!BkdX?Z7cPdYh zCmPCb0aq}u8-nM3hA3q|+MIgq2HqrY!$$@s!o@HULOy}7?1NzR$bMEVyH zRP1s;U%1eJp7ff;nFf6zr{n*ZE^+2ZsY+!2#EfYXdg?g`M@K2>i`pvZMkuf+OB;AH zHFM*WSy7V6d!hK@SRLGj>YzZ8QXg|u4o~HICp-;~q&k%)z*7vzQoj9`PLHna!CFPe z7vTpOK%O&=T(7Rkk@u?=dY*cH$b>YK8x~S`&vH0D4aUY>5J(U5^WdD0Y;IC~=^ccU z)qlQrt+U_+awd|m%*?>wemQq;kV)RA36gQXb!C1jsS$djI0qRCbf;z{uY*nJoJaSn zGB;LNP7&vfva+j}mzHoT_)Gup7K`~WXXl($$m#f%iF15y$4ufb5;1FxFC)Fs6TFfm z(SU?g~G zaiU?7k{Y4I;2c|b<5tu)B_p8=^FCNB=WrfYvMbIO54UG$hwc@pSy6Q8c5183bT41l zKclh*!QJJ&qJ?ujLB)tG{)Wx{=JDmZAxO+QQI}r$24x=sh?VDje_^2=+AC!_5{4A- z{@x;)>gh(69yaIa+aPh~439?_bK4|nqb8g)k>N-#NesHUsLnhyGqAkv)*#N|x-2t) z8y->*P0C<72PB4k#rXuylqc;dki+7MIu6c>y6LXGwDe%Dg2P0TT&vun`8&*15d445~b2gis9A4MAuLs$V73y?32Vu^w1n%z1dX>{ttag}{eld!Gvn!{Ho#+5s32 zMJKx*fFfk1Ah`{FOR-9lX`-hPueORs`F6Sdsm%OH2o$qFUA&lnBT@wCAWOiU^f};Gdzp#3@%o(CSKAdOOruC38@XbAl)( z;b7sABW=e)x}0-3J#CDy$EA`Iy!aOnvtnF<)*+jpz-%&26~8@Jx2gjK#}^?hi{lpjM+RRm^z2!=ijqj-W~0eD#(Mgyo1oM}9Jy-o ztHGuIea_EP1 zXii;hbp)6vO+W~#6^F74x+l49Mjf{;6+0W+{W}SSMor}$Qee^uBD2b)d(|GE*=>n) z6tu&)lWp_r)zm}t0+tn{)K>{tLP9 z{droVNVrqdKh4LRv{Ibey_N%VNOAI65|>fmemmhy&1O5lLC>F8N1izo&CGP(lHy-| zLV%MTbNzbTFw@>}4xn^=FT7Mfr_C-4Z;xK>f21`Hl z0PV8PGnPs&%7XJb7RZ^Ja89nWxM)0Ncb-@3MJf?-ZStR!l_L%rth!QW2SfBBG4?*h z=qf?t?|~*megCM2s0dK}-YOQ2oP(pofGyE}dX{5181`hns;~++pL#3|SC~O&q+Jok&Qm=W z&QWfG1j#cLQH{q(^H z|M>W0+5mpw-}ZBKF0Z|2JY>#e8(O-M6*dYfVV)zEA_Avk)KOE)XmgJ3NG0dk2INwU z{mXG?&Iz3#&f_|k&>cFC*W;8@=XsS(V)!>W>Mm;DLIFr;?m)EN+SkZ`5 zMwMlS#er4Y!RF67d=&T)P#<)yawl|S#(5azg60{#;^K5XYhJzTbDMg70q!1XnQGAL z$2rRWZNqMsdcS}FXB2VQoEiKR$Av?4;W2+sxHOy1Mps&TyNCWRUIHmd_W^fAd)9); zS*ILCj^^h(1hTkj9`nNw?VzSH@4s)I3a@AkCd9OvMEO$Xs}pb0G5JJF>VU@o z*)N08xQ;W9JICOILCzsLsgO&c1bQ$8AxzQ16dw#aR0s-!f>6LIhy>xH;>rctT}6^t zTFHwg+rlxi9AmN)@A+SpvWXkY*PzTNN5%$u1v@9nO% zD>QtdeNQv=MxFggi7*+KW0}Ws62z937eDamI1qj!n75{;Xe^v@3@rA7hQM=x zD-1vAjl#yJB@^0#czLlcJAdNw2renOuh}L{k)H?@?;1`k z!pLVfJcd0z;#uF`-dm^ zW-7cC`(RoJ)TtLV>5ZOqG>@(09K2zPgzkV?xLDzuql^Xi(7&KfEXog7a;E;#;lKmb zVHm_NRuEfd?cf|Nfs{_VaO!X3j%t)Nq>=ON-s0g%v2-g>dQU=j^P!Z4maT+LDD z;9vF}U`XKg-o3FvFTLU6gGmX!xp59{J-C9LcVrcJ$Z^R}zBM3p97cqcUhekw)q8?T zg7)<3)96NnXR6hzSP$^Xkb~W%`wO+Lt*v^ed|u1}bi>y36$vR}&jBuCtC19aJ~(*H zIne^%B1jw< ziclPYwU2W|-b=?h6#3M=#CivP!FIq%XwY6X&Z&I~wY|Gu+e)w&BW~i%>2zN2cfTO@7kjf+g8`TM7*#rH_9r0X>~pyeDuP72St!@OMR@Muf} zTq_{=q74mkt=L~$`qzyc-!3hE8~mtMFfvYH{8rq+3`z?yz;*;I2O(y6VmQa192{odXv6rp`DABv8$c5n^}4TR&tN~qVs9*V}kT$U|{nA1EEDP^9V zd~&r$djY-U;rKzqbZctLG7uy~u8N3-wb09wEEZ8q%{h=@>LacfM#@`gl+5ud5$C*@U!-ZlOJaOd=Kuth zO4(x>#p&g$aKqxFr}TuVnZ)R_oI-d^3^^y-$T>0J)hUZzPc`Zg3`{4)z8Qza3Ed;< zC~D~-#}DZ1qsd8&x6k8*$>ad0)&7@8dFRik@Em+jsjJOW{0kk_G|*{1*9vHasKTiE zyEFrrvMA?7)9he5$0aMgO2xEx%@SfeXcy>UP%Bbc1Qv=9(M&hb!B{&u2Vz921^HOX zT}OE*;XvCI3UxE{#KMMe(3(5T@mj;*6*!j4ERDc=0M0RQEr9veoXv8_nwtjvyr*h7_?_z#z zv?&KdL_c%J=TSmK9V=qGy*;TKmCHrBGrIL;v&izl%Tbx8ncq!iw zUcLW5-Z;8y@xc;3?Wou1jvuU)f* z*bZqqr@O2joCEuQa4?!?z`y+@hqTFgw4B3wXBpTe=djK$XXl`UF#K?271Bp=9o)u1 zcsoD)DUCg3U>+Nj^`&z;2Z5r9A#?~J!iZoZq!MSkaZXu4i$bA5b86Em?CF>TzzLaT zL|qdRg&Kearo0CWnTSZ~iATJoo1jTm8Wh zMmzkF2-CcFjc>6Xjyb0}11)TfkJn*iY6|5&Ddd>uaU2L7B4K)Tdr4uHQ15icLMbv5 zn1^1LMn~=6#WLyBDe!E!uMd}w=venvx668ZLS1f}a~_TM{`%TG^!vlVo%`qSFC^t0 z05E`)47E#vKrJ4l{lpjBe4ds<^a2VJlZ2RLVSZk$2cGBAB>Ba8>I{0R z_#6-+$Mb$Em3B5aVO-5Vnw)$sr%G7pKKrcZd_Hf|1&Bgj7pU#+{pXu+Fd;f$o|v#) z6@5mv)2ACB)m4TL;~d{AsTz1N{nz0Xm^wp?5!)}~Dua`mmzmTod)+d$Ymv7uuGYO3 zo0W5v%L|Sz4j`&)hjR{2ZvXT&7W+7;`FK=hRYI$*_>vAy^Scfx&)ozNWz+Z+IAv0VDAv zeqCc@f;=^gI--4$`QRUa`cVAVl|P>`b=D#T%2&uRfguhE9d|%*cKJ#O8ycd8l*?u6 z03KG7q=c)V=ZPg@eSKYvPqmnf=+ZzPeGUjccfb5n+-tF#DX2rB!qwhI6ue4>>KMO? z;T*320q3k!igU(_G=91Dd0&s<&3o5pSt5JklxaaL(bC66Y9$ zaLhSrqkY6U2c`%OHK=6lKMVu7(t1!)EwD5wy> zx(4dBmU9SoN`L$Ve<&)lY+yMi6I|Jrvvcf!bkztT!l*~Ma+%h$v9X~|lGo&>qf7 zG|KpC7k?6+M1!}TPY!-pdD{-oL2(c{2RBx#5}nLB;!4FMuZcq&I0tnS)$GN{2-G1~ zLAuM1Hs`>waZD`u@gjg^7sEyaAy|nZeCdd#t;V8XJHppzXJ=+ySPuEj>jEt-%+2y7 z@oNlFr{$dEZsaHMhAY_TH*@sT8y-#&Jrs&Y^E_{DZH*>b$Y!@b`6N8%GU}jJDwVCR ztzxmby}e!i?z>tCp3{Ijp`AL#pQN#u1ccb2of3NB(heSJ~XX%**~h94H^h>e}`DwVjd z&byp?U}S8!U<0)dbji{Zdy8}T=9|$ibK;8nC!5F?rJX#fcDQn-u`XwSMhGV>TMQaR~8X6}5>iANpxElxe~gencz;&et^EQ=He=*!i4uHCowrbu`xal&zp3Z zr-ipPE&Pv-gNwH)dgLk^Tt#>zlQ?n3-rgQu`uBwkJn(O%orl&CE!rHJ^7(+L#W-B34B4T$r0BDM5855y8ee zW?F~aG3Ruzp^Q*|ii+BfhSXAJs^FReUl_*?k zA_+tp8}m0dY6=cwVV-FR>YP1G@0fEQtmL$uatvc@#+)e$?JHGQ3i$WV2bldvZfbiGFcYz?jxH*Q4s!&UN~v6a> zOum9YLG{2#o8c%vq%%L%@Z)ZWElQu^S5-SC(rJ zwW)=1sZ3}a$l^gmS_j%Z;(!W;Y88<}t3|LDp#-w<^J@Bg@4PwhynFB5xp!tH*@-U) zBhQwqtxNs6)$ZHms^zAc|Om zNxn<>g(4=X&q-vPI> zFrZb?x-Sfe(hm483ppAF|9$q^c46QPAXE>+KM4e~OE!;b{AfrwCF2?|3+k!pAQLuP+6^lA`@3cmpogHq$euZ8^Z}wMp zEcN}P;G9OtNe8?*=fuxy@L}j<;#Hp0MG8{Ia-3_%O6Jag`x zW3YuNwU*#)7kFg1n5TFkB7{(fPZ89y&u=*?1!&(8vVo)UBymfp}fy=h`5fmySqC*ab=}N&<0)@4z-DB(BciX z@P0KAbrke)O~Gz;cWJ3{uQV*MmZ^bo)!892rxG~_=puSatE4{oNID323Z*EAQ0U@e zK$JSrk}`;Kk4}>%U=rEb;hZu7b(Xi5k+=SQ^4<@QkGD_c^A}%Cf}HAoNA5rW{N0yd zMoHZZa{_O)xDJabb?>xB9XM)oeOxhjfskVTR23z(Ngd-_gIMeKvadSlSeviC^iq$U zWjetpUh}dQg{^q>F2gEs!&C)vKBN%w&qmH%QF;)6q(sWRTcaU?` zrENHe3J^V7vhVWxTNbMXUFMv;xG4HYqtWkQdBxAx!C){wac?hnVtvpCUYN{5dK^~C z?Webr+^A50a+PokPdGik7x0k1_V%w|o?ZlS(s+6!=b*$XlB8Z=jF&FdYTAA4bJ^Pn zjRzzU1*t8Kj6dRL)U~8J?Dqt>2#E4p7Km~jA;iW!=Xjjsu1N=<2~D5Yf8-0;pw?ww zpY7>(IUm>sb>eu&y*&PSp3N3=sF`=}*fC1#n3NZ{lpO`?4cX#*T?(SCHiWCVB#oe6(!QF}%cP$c%7b#LCMT@&Uxxc^n^X4DO&hG5Y z>@{=Fc`SYKq4KU@lQt=45Fj(qZ#X+UQ(D~o{d*aGdvo(9PKmpiIsQl$Pa_ru(U^am zT`+In5a>Q^lh>6RD&X?M(GV_pF+3V9602!|Lp$r_MB5vu(d$r9{5hqA!>Zh;%>pqb z5y)>{u#93Uj%HzHDrv4>MX^P1YU=inGc@L3pAu}EB{5`v91%C{idi5=^WiFr=t^XA z{u?r$x5`{oXZgveB$1?qkEiPVew3a@??Lw;k0B5yxFa=fm#zH{;uI7*0oZY2`r6ktmz&tP=|PW>h8=Wb^a() zmh)qMZ7mJr^^44qlNJt8Ut&LWw0H^pB-9dhru_G$qN#^jz%@efB7B#>o1ia_@+Rf) zKADRX4)X#r8SB5vGwg+OP-nuuimkJ~N5U3}G(UrWI)ON5N+kU93 z-4iB2Dhbe(b@uk|eZ2rwHCscJ(4f68?#WOPVu#&>%U*&D$e>nv#rZ&)B=8IBcLFC% zx|nL^pE(shnmO6~DOvHEL9Z0kqTkn!TB1yl-Pj?*b5#-yo8$3Vpe9pRh)%bd?! zDwCG;o{t!pdjfQF@d$~iL7iHWNp#q>mk2cGC}dq&8F8i<6110O^p`TvwgLHrcUwR? z@rtm~V?Z*YH@@BsA%}{aMbEx_GGY9Yl%8vf6u3?9?=db-pNvNCAYd)E^=CA26sC%A z;ie}QK59ML1wR`KSSSCJ?|Zn!a)K^gc{G92hrzk zp6c)G>w}iqx&Hrp_hggncioNfqk56hKX5=Ycu^rvOr;MQUWT9U2%tLN2)ZOL099M= z$;q6@Vpl&XrHyBP-oMC`oGN%C3gZ{-?buC8A2lutq`?>w3G8o|eMz8^#!|FE7ynGU z*bVmgHf#?TiMB5m+A(e^Zbb1uFgjYu(2q{(5^rL>uE;-Vdt#lOVLI67B4UD6UMpKvWVGxUquwl=1*f3mYPAjef9Ho-Zk04cHYY(RC`V+TQ;bkOAzlJc!gcp#mo{D8bM>M0t%I#*n`GCg-J)c+_lF-2k9!+d z@hKu7y#+BzUsPyQRmxq14)g?7(;LWCYG;sLN6>RYbguKCoP@raxwuSa>w~i31}$3$ z3JBl*bzy+#uO77gK=7>fh+B^bSeh?~nGa>$eVT*!qtuuO%g?O8pc*#L24pw-bDdlI zuaV5b4(cM(@b2~numRtG%LJy!i0*u$_rg*)sV@`wm4?4$Y@D_|gn=Z^)OCo|Eg}x~ z)IvrjFimddJ6yL7-fX@VOcu!o)~-%2T9VW9MkW1sOE4&xy9YY`nm|C9R9yv{a(++- zEC9VZd;Z(w`tamHlh$5ub~T}XtpQnegow}ddtvYs{JFR^AS*jfx0Yx3MmZba8G!*e zcYy*I3WIy=`mE%%#Dme?*C`yBk!}hv|IUIIBmZcB;F5bkU$MGQVoeI?^2Zx=Z8dN>cVkVTRgLaia?g5s zX1shf$7H5=D50-ky%aVWUK{&Z zjVrZ?)&o2r-ywLkDgu+N{r9yC_(x_ z&?k4>=hM5h3#KZ$$uMaRe|%5|n}*erQ>rO3#r(58jnhGJpa%;ALtdP0Zf_f+*5>-r zQf(~j5hn9L9*mt276WY^2!?gA#&YlObX!i(7t+;rK0GM! z1wFeK%#rqoXCPwH*(DXB5LZw*x(1bE;(uA%(6~zdhQE}y)?gAbBo)p?j1(AO(5=&} z_YPiJS;T2ZiTRm^+^r?&PSv|{ib*^43vq{aEuxz*GO}ZLr!2d zY^l3v($n-Ltx5;U#M=sGbB8P1{+co+A7Dr8>D-l8Y3yPsBJ_QO^WKpUN88C&!q?we zHnwi{QAU$$BI1mz>!jM|ums0pOnbrRN|AnW>o4pBjPx!v?>jlDq-yp>D(1%gkJ_Tq=BKFH?h~#FW^rXI_&T`k zn%}!SUQem5t$4wU-;~AE=ErvlC&fB{yE!!tMPLBzNFlaym92{u#ComR@`zK`Iqr;a zGZaCwK|SIOLbrI65?6@xsj4j@!vL_{k1mvOsT7FP66_ zG%1ZWG6({?jUw189%I2z5eyrx?RmS<7|!xD3P_Hb#&U%NXr35%x5X8{;CZDoxs$Bl-6HBqP;xQ4_kD%kKenltI34F(9O@r5 z3(#7per;GKDyQ`ypRFoU2G;lb12K^P4M<)Wrif^Y>-XPp86D2s`T823zq(ZgKkPmh zeONVrf95g|yswf>QzA}M03Q4)OF8sHB6>O#y$)*c>l0rIx;wS7c;s=Qp#ptzJy|yN zhi2*q)pv)1Aejx3E8%ad=nU*7N0m4BjwS4E68P&8Sb>v6h+Oyxt`|RUEL->k-LXo? zu!EEdMO)ZSJ>*YcN zO)Ywz8N6)xTq8|;2QT~xKub};15!`^@Y8U7?)m397Ah2+i3lfe(ag{61Kw?nZit$V zCg^D~FL$*+9MMCCr0Iy0!6*p%A6oHJRBz@Lc!T(ODF6%N&$hu!R{nhB7X2?RuXh{i zbBigG!TfxCKUml5$sw~s42jx*p~2KUkIDZH3LWT z^v&%GGc8)g58;#RM&e0L)-|#QB~uY)48Cvq$&w|V4Z*jM5Lx)8&&`*iQRxYJ2xkUg z8#MtTZMrSHAc>E^>LIt=?a}P$?`IY zE1wcatuqtqO^M?9lT#Y=bYHn%-z`f;KAL=~Hmot5D&QTJ@< z9nT+?VnhO`y~t0lsV&=+WkSlA~mJt@6LPzx>-eqeQ5%j&oyT zS2}gJCR@bffI5Zb2Dcx)_csgnlI#XfNDTuaie^OmA&AKI7g8HlleqOh=tf3^u~KHt5oRJEt9@ww7Z@81UfiWEp&)n0ejATb>R~$SRBrUV`BKsCFQ@v9>2JX06k0Y z-K;pTr(c~0nZxR;e7RFYRtreKd{OT^El-k?CE2S}tk=_fUdU~=bbNgvGGY{P)HN-j`5* z$IQ+6vC{n&4^UaY4m+qU@V=jZ=~8U$IZqMQOgt%q2aM!Z9M?^zW0YBg2=9 ztT5L`g$1(o69Am3KG>&^_%lUMl=iI-o&fN#ha|!+-zoO>2=a=gyDWVqITpeMIWAvL zi-*N5Zxmu#^p@6E+0TK2f#S8sY^hi`XF_#Lzb+`1CjiGRV^B?&%F6QJfgwJ3kH3h; z^~=ES0OS}%%}ZY`aI+l6b?4y@P1DqmPf822m`X@ud`~2p+R4CD^o`2A=|{i>qYZMo zBlK^-=&!g49{p~#VCNZ>8l>bmsMGg^zepu(#jZ(}Oc`%z;FvQN0c&fpNPuDY*=y?s zg(g1Y=Sr&;9@ffp4GmOfctDE1{b=B9 z-(uCH&b9S)IxrTdgoaCX)T1aVWlz`K#Ar}x%;NGJX%9D{x_%7jWD$kM_=;!pkV21x z-Z+R3R_#;8G;~sP-=J~zhx*Osp4LB(#TkGl*>~A#fEVYLs_N>L^8;Fkkm|{p-IG+% z?+m_?qs1XEg3bWe7%5BW%8ptr^+H=$-6I)7CIA{W8AzU5Xs zG^V9(gEE!3DWX~kGiJ7iylur_iiQ2w#&5g?aw52qIbluIHS$`&VY<{)PiRR*(`E5M zf}Uqt7ty~W3*aZUo6PR`0L$c(ym2UXh5;fM!Y`l*SC+bhB|LS$z|d4 zA|Pv0v}ETVd6gG*V|v!u3w+I3JsP`$oAu1uw9u*UA|t~Sx|I>}v-lCj#K@>1C=X#F z$VrxmIdKrh|+d?2x> zQbks;-=KJ|bc+gu<@YdCC2v}}PigXO@b`Uk98ajz-YxoglGf^_M)-gnK_@bA zV&}X;zev2(^Rb*N^u)C_we`>FxJ%-2e4_XqSITlpCT9R`!qswyD+$$RLPD7FIj>SQ za z5`-BeLIrVz)wL12qt-HD6lfgaAuvdj$bOcSk6^Wk!%Le*Nc`k8H8a*uLZ<|EjyXzbfIIA$L(4FJOL% zXAKH$rBBPUqZr_k#B#`d%O~qYd61^Jl%-BOI+6lgWe#tSj?q-s9(tmFVW-R?VjAsi zcE=nKzYg#)PJ}r`-6Y$+v2}{4-4liDrFkmu>mHG3&9&>uqQXUp4ufWrf`Wpg;Frra zha!KGfZrw!gkq*L3AX<`jVRz9GHwZJVA44k_dely=?*#HzHO;zayj2Ka}#6-;Ea>j!0@!|^ZkgZ z{R^Q)Gf|(QYDg~Lf4s*q_hU1>Kby|&(Ruumr-przRi64|ahTOl&aR^&g%%|!*16jH zuqfpG^6@WS)ZFDBCDnm_;6WDn?k)@~xNPiSbbl#@XywHax!efGtAroXyn$hR5o0P) z;-UDy4NLFd!QqwU6lNzU+ZN0tn|$LjYg%nH2I#PN_)vbyou33%@EaFUO-?!RJalsoc2rXZb9~YUus-VvsSnh0T$E$~WBSLloS!#RhT=*fqi-}+@&KB`5W7eJg z%3L7zl82sVoDXfW20d6CPPXs-vSt}y;ul%zu6Htd+Lx_BO-f9TbgTen)sju`uMv)4 zOSTnk3Wga_s=RIge&Z_~MyK4|mkP67o#e1KBQymj@@7%`3M@+5w zXgq5=39+O`hS!!0BL_xSgZ8K=)|Bea49|7vmj1)SG1|K~!pPv6PJS{?Vj=1IDae0KmZmnR0nN8e| zQIv1tffYcaiGKx-vy_!^U}nh1=f(`YIz zO6DN$<$nYBkvgR7Ysha?L~=nM!oM>|wcM}Cv>d$K^JkSF8WO*^c@G|g#0!w52$RY$aU0p|J`<&@Dx z4AkJN^P$OH>VrH(MfPBc@>h;I*ju7?<_0Zv>LEtBLzrBEBKagP!A0z*9FmP#Q;_IE z=Yz;ytPC9ZQfK7C4mrskT29lluc34O`p}<#-^!*S#t$7bhxZ0m49iG?YjoE+UEnEF zJ6-4_Fh4_|c&E~PBHv*^jH#)^#hmh5yS&JQP*53a>xqQI6Ip4fl`<~y)KjXJPJH~_90(yAI-`;{p^|4C7`aXP4{9DFTv)Y5B zm}2ax?CF`Du23nWpFWkvUaYq)1^<>;s9_pr7{*4O3t9veBUa0HR(rhlhJ0a+Ww^-q z2b?$?4wKBR6G{Bo?4Y;OP3Y|0<)ud6^$Z~Qc0KD^M=HJ9=jwYN_4YIP1|OgwgjgjwsaY&$f(VW~Wn; zir3erV2gekbBkf9lHgVEu|%8#G-w`g4s5&X^BxREfQ8#*ChXn&a)o+(K~Hvc)R3&Q z{nY8S|JmXvx{%Ns`0D)`TsF2*j&@>;i!Q)lGdWqmZYxt|t?ZLc<6(HFefqsro(ynB zri&HkTD_w)Y8PqQ;QvWSs*FZm)qN8!3Yfe#kPq> z$V;d>uc3Q1WhcQ_-iOa!nzUL>^~S=PLR)|NNyqdma%}hetW=R=Ku|Vlmo&n-X5&7e z=elDdA+{wV|McuFR2@`v`_KApj+cgQ-^CJ-_4m7&?)LDTE!Y?#G6{T6wIq`SWpbiZ z7j=XsA2L6+Cwb!!yk0#$t!H76_3|u10{8y3O!*|y>&J8MxI0*XbZLLUYRcvv6B%L8(QdXJ_jmZ)WQ2%3>PlYO z+G+2t0c6=Q<`CY7BO$@(Qrv?!xE*Cu8#xv(^c%iF^uVWI=A%8M6!Pk#qdoZ~6s6fT zybVrio(CmNJUX@M@h@O@Td;-!LGwgS)GKcoUJW5rR!kU$vG%YJj-e}SI+On8b7Nrq zO*tYdKtd?mL1&B^8AURlgX6+I4z8Gpuxs5FBElQp!;e$B zuEoi`f5Eu3hW0Z~!nTxh985yBB7?fU2XA-(kzK{th&iBF7O+`C3V>R0A zw_*PS+$Eb2Q>P%o$1L~27n6B6RFMy|I7eR>@-SBAijd1Iuij`?PW71z zp>chnDFxucs|A*iUElall{Tq!j-gy`e?^vKO3&mKt^)30g~p)L6oP3RVF13_*&t>D z#V&V0&lc0WlA+LKtgJ4%_*wDYM8W>K1fYEsl zEs@syqXd#VJ{Vx0=QSMw)7dy$eHB+k<|u$x3pp8Qf|7Dxs?N1uRpa4q`a~`OR8=$A z3WrYHqQ@!W;hFrjFAM}XJ+tF^$A4V=1M=PG_yTYak zfIXn^-D6=IWI$^R+=y6y?rsbaE?#4@*(bBDGEK?;T9G!A8t-e3iRrS6{dSfAzx;dx z{e541l3TV)9P~RT)UXW7X1w@u)%&~daLA6KuI^{PVTbPhxm=P4YA1|nJ4SK+UgNI3 zQwP2bSI*0A;%xlRRdlWjPYvZi2p>P34I~~i%bl}xl(hHz$Zs&e7-%sy#HdV_JwDn- zV5hqx5jhAu&e&TISmRb{*f;2~3prbaO+!EQMFNAYJ-~5zU4{?)8MHJ^npVdu_zQ{-hG@?$%O+eiJ+fI}r=M+229vJCpWUHzdU^VP`gB-a=4!f$ zmRNVRVKx{tU5{UU1aHPYKi{NJd+dYE)%2lP+R;wvBSVlttB|x{-Ri#o4(&zgOiYer zJ-31(^9GXuxh0>*(8^|_yNUgbPa-BF!L+C53aMV+>=GI?-B)g`sD)3-s1PG-4~@|e zOamt5`=e_`EwM$Kg@qEY=iuZbV7jXRj1sFvQK7ko(ZkC^MG@riS2EShhEqqNeE@*< zQ42=~S^LyY$gd7j8+j;ytz4LZuOq?0%gJMF09t;jvJ}Gp+UZkj$QI|DDNsrT*MVIy z3gQzjAct4oZPzk8%mtx5=Va4U$9T=Qk)RR7Xv+i9tFyN+yE4V4#lq^5%8W~n9ud7- z946KIbzx{%VnGzAnj#@e3&dt9&HF2fGhc73bDROUJV0ptCcrhUJUtgtxrhX^Hw!bZ zevtIzwx0B7NQbHp=z%Yd(9iavut4HmIaZc>nPqfk#~CZeYk?f3Y?(^&ydo%a@JiHd zzJ*VY=aa+p>pj88yxGI^^UoCvvwfnXq95vjJPLKowW}vkFnjLC#s>6Dz?p=R&lj$l zvOnJfu;ra`?7p5DuGX%utgL>U90-aU8aS0xBO!nYJ3CX?{6QiFZT_zR*6=NU^`Ko2 zG+WBp%V{mpDMPM28fUHrFD~|skw9~iuu?1}y^MMkQBI41mxt!2R6uco>=FR8d0RMStJeDd z^KTfx{apn6_^dQ{%w>yyM)wxuzVG@CD2CjYt7_gEkye(sACPWE8hKRIi@|4&wJ4JN zs27|0f&$Cxr?&x(eFwA2dnMl%0W-Evby7qqk0Q|C?%+szl^d!9rcscd zqP$~D5H`xGdG|z3U$*STvcDv_GP~Z~)3cE;_eTCW-x6t#o6lkn2cXrX0!*h~xoeb2 zg*s%8LEYy;+%s_4jviFz=;7O1TV!-ZTdwx*@pQ9t)e?1M797dr;_L8fF8RKfxfa-8LcAfB zPrn9bUwAcS4N&zj<2FG2Y*AL=(Bb_c$6~>r*Cel6F}RH#2MoKAj1F;JUV*)M&=;2f zVilG_`+@?%Klu7d2?goviDa+ed}}ZYMCvmr77_4?Ts89&sx+NA#EmdXj=*IcZ^}DB z&2;v=wNB&#_iCs(U$x)qW?D#6%P*&dVf`^MQ)Jc;ms3#9?! zAp?3<<^y1bk!y|rznh8WN%#Kd`bn9lifr}hgbE&U+LxCDJ3c$&yd2tdQN7JO?D62#NkkW8Hl|;* z)PK6p(El1D-nPfjlB5akAAdSqxw;CquoBZYP`2mv%U;qN zD98sr>aoY~-R%q~t=eTpu-)){3LZ3e$^+Err>B$t(3+$BVbGwR=y0GwJl|$$)#6|8 ztB;~7fCTFEqkqWzhn*V<-qeB88cc>ISUcp%6by{EPSC=MLDH-fX5VimaU-)=PB=B@ zk!)c<9dz`p^gv^rcc;}Y@4JHN+|bk_5q?d51RN&v_(sCp>fI)P#q~@1NJL!m9CH%+ zWtxDxeLW=PW!~oQPEs(Gyh)MOWqVEM!cckX<)A~%>xsUf)eWHS zYD1A$R&n4%*wxyWxlc%W_;LH_M=+Si+)J^^_RCp(sfN;MMOg@E~jA3DiroDr>+w4$fU(;}6uay8;#?*^DpkJ@yYiC0KfiXVf}#KOlhNdGCLM$y(D$V*{oQhM06 zZ$uKwGG(r5FF%^I6_G$_h}mp5?VXd!)cj{#7HRV zf+ukvdzRL+2w;~&zz!bZF5vHC1wtaxDh&ccFzV33sO7utNg$$6SXrHFE5;MX3p;FnH=x}!B;AD_FdMJ4c%6>cZp zfhl#>A_wgeNR}PmWF=JQ(FxHW+Lnd<49klny>wSf0j1nErEe4*eKaHbu6)I2O}=QvdiFdfW0_KV z?g|)%)RWeF#D3Qy|HLSM`wnbl{d3`B5vh10=S!Kj-U-q|pHh!Hr zakf;+V{toV&W+(_Y^4Nik6TtKww1OK%Iuy2+Q6wzLZsdLd`ID-LHm4TH@ihWae@r- zJESfB;tU8y-ybd93?{y(NJC|jN_DFt^6Sm1+CWdwt@*Uw&zK837R^hh6{D1SuD!X9By)LYjKcgtwq{;8XNd$7#UgMtcVw z-BZa4AvdSzUJ zu|tm7&LbBvR|I8DO7wC4qS#kci*2aZo5YQM2E=g6$Q2(%c8tu`q=iw>I%!Tb!}V$> zB*b&}_UF}6RV*%8hSP$i$!F&8i~jYO@hv$C1i`& zdnBu%i+h5VdyMKp@N7f84?f@@h)r@EQpp;Qj|#UO-?j#dC1165b2d^Jo!V=ck9T*i zYV_a|RyNFz(P{e{Ya*0kIy9>z1_<|~b?joEYqp5P+rWVg;4r@YDOe>@GOg~tCqgiw z2(+YwC-3+V1YBEA0O3Y5aTp{zhbS>bR$awCQnjrpa+s0B&FcJ!7s z>}yP4b|OYiJ90kQjRMkDK-W;%G^Zo3!z2!yAv^oz!ZUzNMK~=UfKVchnewI)Xp)LC zi@OL{Wf2Ed6R)WmVK`5p!aiKsSw%+nLdw*RS4Xqek)fE&1zK;22m5h3#jl^%#{oaf z|2SLC&7}lPcqs)P;g!4wxeK*9l+C03W6ARLkU@tOV_%q-D@w6A$aise$P)KNw6|!p zbUZ6sjE_I%i;u2kWdQaclgQRDPy3{B9tiLy|Hj4t)p~N0Log-t(}acF*PXDj@c1It z`Q-~b#Lty=#K$NGjqv&-52>W7KqkqU+z<`+thH)3HGMEa56qy4E~; z)csUP8m7XK0Lf$dI8>SH*Gw`6FaEHVZx&U?v^F~(g1Kc93qX8p4|J7Prx;Z`)fs>m zy}t=`hWo3A#+Vlu)u$np7%>V6_48$heVv_3y4(1~*hp;aC`6NpkY1=-fb^2C%z{*w zH@xZ-ust%|1xjYQc;WN=DlzV0R0hUwQu=LT#q%TPaMiLC&;AY)Pon53losYfFPr;R zW!IAS_!mvgc*5X#cua6zU1ErUs4Dyg#{=zU8MZ{+iqNm^aR7Nqa$TQpcPBBDVZ*pa z)EkOZd_qXjCE{#fUoXaOZVdg@b*Y=C9j~NhAY+4OD*VvOF53TYQ02M-&iaqJ`S66M z27@|3Ohm*_Wm08Ekp$yu)DwXBUPV?9OwU81Z9g_P9M!y=4L9*-Q+{R7$xEySCw2UU z*eHRedqm;Al;hCCa3reJ&x<>Ij;>L>ay&#< z==)TXo_r)VVLKrmN~ zBPHEPqRr5qn}(BYEBC!q*4DydKe3UfL>(COsk}V{?I9LN5sWrWtEzq*`>W#2I+oi7 zZ7L9A7Z`+Rx9;3=3`Jd58m4DGNT-()(YFk*;4R;(&jf4faq>yAknUr(cr{g<>K_M~h6xI>^%~%96UcDP z>8nqW8EGQjS>v|_7%zP}GkR+#8GlV%Ce9jja+zN&lb#lNu!nUWTy_9@D3POiNOzN# ztj?jLqN(mQi7SLlpTTXk>N_we=VGxo-WHy?J_0Bo|w^b)v}*w-c`^X zzQ;(S3J&`ebSmUgbhCi6dJNBH=Mg;W{^DK50;5ae1p*CI_vEMJ8AJk}E>w32HvQmO z@2&xyIVLDp@=e^wO~nXb*Jp@;zy-_t5K1re+CP9YOjVSRoh?$NNk|cd;NG0=;m4ym z2Zh+2#l^SZXsars@%PS8wlxe(o5hfth<9i1*n`vx>LIv>Gw$&kjE?cYC<`c>;RbD{ zIa(cbV4Bcoca0i7+DrlfGAj_w469IoHVXLHNE=pnWPHRwPoajN@NnO1+55eqMu2p| zOx?C7{EFX;j$Tgy;A=AG4A0rgUdEl{r!z-z&@m8+I@RbrR3#SXo zCdMDmp(p0V7LG4JpvOvM`C2t)7gybCP~#XV9!(p9u8om{3ztaWJye-ii_UYb2S?vj zaIxNQ-^vmk*lyx|6(sN6+R7+t2yh6rd9|`aCZ=0CSb-ERey1j(yz}Equ?i)(#45y3 zjoFB3LLW2==kK&&f9lUzR|HFohGH88W?wrVcc!BWBApc{?BWiz{DI6!uoCW%HRF6o z4-L{1q2`HkuS!Ni%n86##xA^s5UJlERc#&D2BqUbl%T<@mf$q{B=x1qVgn$R>~KJi zqs>m1E9T0YF|;vT!N37AGSf3N^HObdgH|3fAt^+a*<|HLhNS#WvoE{54zL*W1GWB_ zKkd|4;;iTS$2|50Dx&HINJGgfL4PAlsO2{rkTplXeuwqHlo#Xg=*~K@6QGijntaQH z{pugxhI+&w=j=Sd&VB*JPKy;8@ohj~mGH#8f-!DsYV9hT+vS49l!P2A*-<1yL;THl zqs8KmN`(w%OV=Abxjol_{72ON5UMYk4+-DD(<+j9B$wktf?h6R?sZh@-(nGPta}FKQS0e$C91dx%^7{-^25; z4>|!=e@4+3WUX0pUfq!U*g2nwZHjd2P}P>|k^i8$yd`W0KYs~W|0{z#PWE)+;)Cj-6zy~cR)BZwaO7m&fl(x$HE(JOH%a@Y} zOlKA5PKAz-n44Hf{OU5Pq>jY+Guhq$<|=xdJLM7VT=2QIrx8jp`pQjlzKeN%fsj~b zd~QB(LE!-otR(^i3XRfdK&?e{P!V4009a626uIvBd4|sffUBzNV{Q50TuVwk$d0lB zlhSFWF06(ex21okMt&uX&xHr2Oy$6nFvwMP^h1eK^Ui-;vdr?Zu*Be|Lt^*xe z@GoN7WG~%skp^{9VY-bc9qxPNI|ygbs{{M;$mRpUiSz8ZAS`O4go9eH-MBB=)v@eQ znv&$TySSgh9++l%{W9#P+@3lyR7Z$Mp#X@Q$8<&)pvLvbK~Q;D{suOk2kMlwFlVKZVCzV6x)j0exmMJV+ z2;t(Uo?LNh{84#uD3%JMQLq@0p&IUkq2J^Lq99|@D88<4bk zurt9HU%H+WRsxpv|;gTj?*dKE1cX4a)trmhMu4bm>_oW}b$Q(P? zeQ;z=Wd{8!#>&2a#;E8d?&XcHiYArejzy3?`TpI`^gFv|T@a4ipEiURmgr#1fA~sD zf(^|4*px|dpNHuXV%Sdy;rj*-l_ngZO~mUG66H^a7ZXN&_nU!`E#Tba@5A-+5kPNK zlgcMV=Y)X*xxiY4<$mAMpIOKdjJeq#_JQi9go+!vAFx>_lO_kvDBkYxHcA{k?_8z; zj4YsQuqe;{Hu~fzuNeTsK5}j%p*ME6+73BLMbIXDXMCx!rO1$GM~_82p4uV-?`L~Z zA!dS?G(*L=HqITYTHdc%Jv+r|)4Hp6<@4Xvzs8YAX-^dVaJI3zLCfO5`apE@PM!b7 za@E4(LC;(qUF*5K+$qbtNTsBANZNvvE8t7c9#fI-9`;iQ?d@ehGhrkTzBoCFmCN3R zz0;L7mITo$!-zgvAbYPJC>1G`rxk&*%sVjwhyfg7y_JB|RoI(qDhlTOW8H{=588B?>!=q& zoK+mOCSWTrSN#ItkCy+`)tK>if;baM5gPrm9Ic2hLJ67LEY?+SD@wiA+Ey?)5jfCD zc_PKWcgf%!xZB}Jpc5j@!G=vi$g7y$0zdl0^8D`&H>Q#uk&q_s{2QpF0bxhJYCn5cyGO5^H;ADlF z*}_7mXb}*Qan?6Ql4`kS*Lykw1V%UH-fwXU;~qOcBgc=%ip0X+V>f&h^h^fqCu_~l z0D|%t`@Pq8sX{j2Z*))L91*mM&N4nF5SR!S*VAwZ_3d}*5~gxu{!+J4JV^YNK*a

    P;GDlNXQ+YgU~#3rBU7;EksM@!cZ;`^h2o40een>9C)K$s1*5NYR&9 zr0#I=$BZh(D(?+HlsO3a5!#dlVFrK7Hg0fQBDv+DCj@OEGLksf&iB_55)u+u2Mb%s zU?K4j|3%ZX5{Bdc`uzz=0nz?dVGE2cjiIAE|06S4>5jvNF9DmRJyz|>(*8CPhkDju z+_$+Ioe`_CUR`#!-BfNhMwMWv_>SEWQs?gGrt5{rHdVkYi+M!ip#*Il#b7EnMl{-6 z?;2&b1#NGk%bwDxtf9Z>&&kW0k#k4d64VQd3#sX!e1*2hYl#8KE%k@;l?`KchNibN zVlP179Ln>yMzJ{4|EwMaz|zF9TP>9sA|>+pMG-K^PE$FtMIciDKhD zMO+rmDg1uohX-&WdRWk0vS>h8Kn7`%&8T!+f=!4L!JJdf=Iuu&5?nDm3n`wB9~r*A z$;nOqp|a&Q!ANZ@{=KQ`;+5$I3oG$Ki^w1{FI|IXy@wG&LtWc(^~Bq*D%@S;q{ltn zJ^4>NSStn|>si&gC(gCim&D?%y(zK%g7#w2LW0y>T0>gZl`|_LT_2^eFSSrE8MjLp z?Ei&;3xeXGm597nzo$>cegN&h0yUT?X->c~OjHDuGkUNUB?!e*#J!#8szetlLs3Uc z>wR&k5$K15o5SDpr*joi55nghmaInbD&XJ(tIwz5BG(~cVyIxcg+-9``-|zShy6^Lh4srFp zlZQVmh$HCI2mmrHkee)b;!jnRsbU{CPuMoEgx{$>Xk<)snf$?Bzmr2GdLcjn2i#bh z@3ualEc9sb!9$(};u~L~X!QH#qs1hr>8fo;>0oHq8~-L?#K0O{ay_D!5_{dsd`77!jwwM<4-C`-m!3X?N-SQYm7qn*VV?5e zEbQ!hDr}u}J6!+Ver*vud(d-bCHzgA6aDP)t_CKd^a1_N1mZ)}+lpyi$}hLn3=_?> zu^s9P>T=eXr2)F5F`R`Uf&&^9 zPs{tz#?1vgSYvPqrlL4fek+;x(W|D=+DMx!+f=_(S!J z1>ZO=Smhcn!XYA}g3n>|)nXcAQ*V}OqAshZ-X;0v{el~5k_2TA0RJUX9cpk~N z7j`j@J1HNW+RrWe?B&R;hov%FicGE2_6XyanUR1;S`xEd0cV4%3^YceHX+{>ur;Kv zCKMAg4de89PSrwrIWMd!%V2T1QZTkE{u!c7jT*3ioC=DOxg@ZPgpG`9|npfHJ z74gx(kt%_cX=?S)R@th{UdqQViSiZUza$e>1D6Ijde=Mo0R6)!S#C^WdyGhno?!IQ z%T02@=B6esmIgCs0;mtI8{q%6<+FvMsTNKr=tfBv5%S*0N(dDZR$2chGlc2^_O#MY z|B;k%(ntv5f6;;japve*mjD0D(?Pd;eh$-$nJo*0d|XhVAo%7b*xuLRx|6k#B{2(X z)vvq7f@sY~eQeC^R0;R_OT%2wgKk-kLFt#JzsVh}V7wC`d5V(W;UHP*rL=!Sh z+~J|gTamZ1>aM{q_uj)lVBKAfdQ-MN*18%+QbP@aoka3?--)U*YHl`A3cWhIA`31@ z2}Mjw{|XxO3gmVf@+ggDG*Wm@?HKnqM(%F-O*n}N?XaW@P_%~BlzQdhKK_*bsX7?U zeYUVkeoZEU9C8F{50W^=-NlAIe31MK6 zPO6k)Cvv7s7LRN&Xi7EELvu#SbX44)vZD9sq# zJ-o5=5mtlP`_~0MzHf7=(KRPLuK=K_0|fjd2ZZY%=InhBZf$OaPA>59l~Fz@CERId z4+EMMQ%EJ>PdjA-j#e#1Rr@JsH`(wdTOYRzB<<>hB>^NH>7oEcbMyAJH0brwqu3m- zqZd{r(f*7^$bH4Lx_Rb3G0(k zYf(1vv$}?kaj!Xx$1alL&k^WwheC;w4e7z3^!<1?vO4}gqKJroacoFECqUR?(IjC8 z&_qVG+Qs&wi3Kf_OC&yIY|kFV{yzW>LGr$KF3is2|CcgjgcLqxdwRNsg|oAmYO6OR z=Onv*-AQ`2TI0<(la9kzOm5EUj$y~00oF+!ce^N8R7HljhPCpYYP8wn=8L)zr1QwL zb-z_cJlYq(g*C_wIRkFgNfnpG_G#aSicjvxHxkUT5iO*B!z@N=IS192ieq76p;B4_ z;aF?S^aO;^^Sx#a1KSW+fX6UZs^&p@0De#pm&!#Z6JQUXfH6W%1}s5cVTrgaFj${3 zpoUTF6v17eo)EXzyupSrAKgAW((RmarttG`fq91^fs62O^k-HzEYm3k9^(#&i($ z!U!$oBpnhVXcD&x0!h|KOD(w*Nmei-Zcp&U8>bf`83zKbM8kO3n=yDXHjKuC2}YoW zTdO;-bflx|`nq+$`nKd-9Gcs8tIn-DRdwE{_UP410S|fyay&EVz+U8Xtl-d7sOo{T ztJ5-QI!ip4C&I1G#RH=bm#vYJ5Q9NjI8a;w9?6AEJAKq($y}W%MUx4WE2E>}G%09U zj$o*kSs+t6=l9>epMG)z)SrL$_V$7zJ9p zNt{zGcJu2|lA9yvh>$bdXqcSz2ikS4RiA$2sSfl5H@NjB^q+ z-SUZVyT&I@ zESw`k&c#Y)N4Q~0@X#I3aSx8v_*$tX*1zoQBkjvLCn>X^-@QE*S;ggW1X^{sSls@a z&$o7W?ce3$d0i8l&XRG?nX|`*0wDoI4%zI?3@Y)~$AfJ%B)|8LIA^_F#!y!(bK>|{ zs|z%*)FXU8?d)?70|3C_%kgm-{S_~d$B=U}L68ZWiMZl25^lqvAj97f)PZ5o7mGX$ z{1qVw8o_tXPPMIPz-|xhvKho`81bP7b~+k&3=BHO$%@6Aw*ZSZ^f&G>OuSV z^f3B4Pr78BbLPy%IrFnOqi~L8`wi6Vu)~BLy&Q24jeosb5y!t-Tgo^mK|A}L<9J0C z--={nyk_c*013Zpr3eOrM*FZ(fcjg;g9i5LgouyYfHy#hZ?Q?JDOQS{c5I`gF{F;H z2<2dS7^%KGIY~Ja4~Ln5bJLDAQ8>r9dqn4nmW*@GoIM*Hq`kedQc)q{i8ToynsFzD z=l=lZh;!(8)|Tc)&RMN4WSo&23B&RMkea+x+ZP+`4V zVL|{u97s1Q)r>o#E1ZL4c;UjMu`wF(ci&VAa_+5GXJ=+m^PuyFm|1Klan2iWbo1*` zIL9-f&a0!XZriJ!pALZy(yK4W$59D9u#Xl4Gr@z;;j+%uWx#ebf7#E&;_!0>P| zJgjZT_T=-=f4P4BZ(n`Y{NjrjAAMwLd5$^fMJ|W^0K-F!Zl4SdF=pTa{<}Z5Kl}TE zG!o3C=1+$l)v?)xde;mod4qboSgm&Y(RzKquaC!%6Uv7aCEmr#tYf9b^1%n){CX6~ zk({Wmd^f5)n_|j#tX2g0M=WhQCib43Js2N{HHyVp zWDauAC_)7>abf^J!%(M{0_O+T3s-V+);Wu2z|NZt`(2lVZ5Q9ZS>Lrw^}-x?W_(se7Jx3ouwmr%r_!q9zgw%B>|)plXepgZv1?92@P#UQo* z)8!oKnu-k;DswRafFz*2E)i*jM~m35S1T|?m}(vu3f+ubB(_t0aRU$gSFd^l12I_Y5$;V>Ij4)og`#672lvSM4OK*)jB`9CL=x&4<8%44 z`=UG>92D=Wc!m_ZbVM_$BV0u}IDs5OVxV85=s2Y$ zFg6JvBEZ<8w~mC>z2!bO5p@zO^?C~P>V3;N$7BveWz@+y#}k5Zu~Jb{#~2^y6=5Nh z3hEQ;Xfhy;yF+BxyQPxa7?o;cK#rCKTsQ~G+|&#rwKf)jb6ijdL#45SCB9#)5`+*} zUKz4eXEIfp_aAsl0ax{DkwP{cxbHVrr;=kS@L?STQ)u9!Al2kgcKj#G&N!y9%5 z)ifH7pqVFryK?1Fc4IVgQdHh~C$-u2ru7Erbn%nUkr7L?*+DW>Q`KtKNM*G5CMLXr zf%9*|nH3}OpRd3E%gvj%_)Jax`_oVVSt>y;p^3hE5;{eh3QFpzs$_h#({|L#pWU7f z4yq3Ctr{It0tefO0x>^(Q_bO=pEp)cb?W=Isw3xkI^j+Zb%=9JT!FW0m!K*fh>NyH zMo8L3?gA*1^B1|C>OV!!(T0MzC3Zp-w-SxuZ*VhK>!hkE?y4HGV^D{LE~-N{ft$pg2}+a_Qj?zL9EmH4%1i(mRE0^fhzIZ$ z7{?8-q{i4k^lEW#7Q-OO!SF0XoFeBiHmC>}1xRp;X;B!$nVs+|%7A%uV$4_t7~&lI z5NY?46b&qQIRD2vt&_kx4$q%IKZl(2{`(o{*i7P_H{VQuNI}epl(3gsVLSJ>NrJ_` zBIjtorS&ka6ySt?76+Fu(b){z(G#?w3mq{|A{?$sd)q2V>xbGlw@pV$0_Sv>Z zM(7OET?ryc9Zh<{#+s{kgbc;rkrN$Hkh7>Z=BA~gUM&5awIz$#k@0m>1O}o7i;#Ij z=0{`h2q(TBeH~)@szVhN*UM#!GjkCzMmt}uHESPxzH@7F`Y@6`$2s1KpiCd33w97| zANI37bQBO)VHA@Jqh7iK;IHLS@KNHOX<6bFIR~a*8K%t$=Wdv<1c&w@1a;2C#zTl} zg4SC3Jo#h*7SEJDm8nK)3VTw#iQr6;w|2XI4mk&4lW~s4Qso?z8a^x(PPTnPrQ>F8N$f}|!Xn~KEK)~; z4I~`<=b()01TCwn^6Fvws#ZiN7>O_EK;w|~Bvr*UebYD^Fe7n+D;LppcZj%|95TI# z1*+}tISI~jG~Z}oW00i6TteDADx5CohyjH4K8+-5d>wE+Q+AU8l5<77-9De3bNO<{ zIW{}Lob$4;Ps=&bT%gWV;jXvS-yel@B-DwEa|l;LbR=A{+=Gxhw44L>u)s!W&LPyn zaxj2Z-*!J|Lm7NXk5n@$6Y2>z(wFyF5J8#+X;g( zfv5_4)fO?TOp3)Mn}CXl@Prc)N060PU*5>VmOn6#t=KrvMZnGD;Uchp7?R?Fc!eO1 zmq$nnV+1bbRQrD3vpycpoqO-xuice(hd*MqJ9B2{%$ak3GrVF~tavn)oD(67bKZV? zkh2%cIhOsf*amV=&d+D$920e*1ufrquYB8FSdcp#wn%QDTt>6>>p(7kgac*Z`s$K7 zaJjh{JCKVXHZY8=l#a{|-q~!)I)ley`R8w6ePu2C>U!S}xFz32ulX>ms3QZ{mYRtMLM+xmo->{U!3=ZY4o`!SQ6P}?xzt** zHW)k(lW^q2qnDP2vaH6Jpw#eh#7JsboHO)|57?_iUs9=Jl{@EbY;@c0A*<7|>ukw6 zmMqR0Kr>J$3*>Y&bB+l)oAtUq+-%=`;{!ROV0&1rMQ`NKW5=AO|3Pq7fq}PXX9qSA z4sovYm(!d==|s#uvVyS1&Y0Ts=p zt>hd_gXf&?KGeygESwVqIY(^Hv8;uOZlHpeZ>srDwd%7Mz{482JvB9efyym>5L}7Y zvr6Uu^t3tMq`VZA+OTCcU^0hWn zt>hd_L*blmKB$wLbK)WAh=X&waj0Vse=P|LlIzSp(&H+3cfd-j4J9A#a&YUA;l1u+?4|3=b9OVp^+&KrJ z0`myehqYQ5TzT5TLUuc*;6qs(M|OBFj;&k^P-tlzB;<%THuf!`>N1<EOigOkSEv3Q<_<)n5janP1ce3&8*oeSoy z)$N!a=_@$^&KW%9Ai%ZcoCsN*^Ugb&tPPfPx(QK7CVg6|42yH*xkq&%ho`eZRw0dG zguh8sA8BoSYKpOWy$}Z`t*0C3m+xNr_8(W*DPdB-oF#IkgxiCVM%_tdBn(16ZhS1o z1sDA`o{V$8ZhWn^>EQ=|Nx?a?ujS?{r>u}eSmFT*PC=AJb`)|l+HrdR1@EmiIWPIl8D+#LhC3f1 zNyB?^Dw=bK0y$`GtmK?1S)7yI;9xl?@nX`AO~g5LMrY2LUy_04F`MO$rkTH=vgYUc z#%Eygkw}X3e9eJmWG#jx)@;|K`~^he#2z>gn|hYhfjFq@;y_^$P{ z1-ybfJd^Mooh9$)#nwn?)RzvFMJfzXxu`hee}B+@4LFCeX#_YYf?J$itCVWJci769w3OXg~u3k!|jJ1&dmqEuM>^WHsNEgHhU zO3sN^?woU^c+~MNl|BT-*-(OC|&N-Qu2k_@&lGxqe|S?5^d`Ip!&&)SQ#i zjw$6F(~XF`nUQnCEMID^I0IlLR80HNci)+hJ3dRW0fJ8Dzpr=S-M65Ub1W6XIY$ac z9sgzI)lQufgU;-_Pe+fwh%q!arb8t7$+y8brsu0i6CjMF7Wgb*I^VV`DMq9T>+=|%ks;pas3ngSDmGb?IPld<;>6nU zHi@0To_LvELTHxrabpTX8#Ty5k0&Q(Bb)^hg%oRp9wySX77!A2mQov5ZIkvlqg{t+ z>|rVx>bXFU8J{VxXAyQx<&uA#QqGa*+iT7_;imrm)o_zEX2|)vxTdFV+Wa=tl`#l)6csnP4JOXfgwnhbOz zpP&&b)EuITIyGHTp|pNk&Bvy5INwAtUS1N1G#3_N8+8a3C)W8+r2v(^V=o5sH+RS} zH)u7!bap0Y%x7;uGUw*{02~k;73?2ZPiRLT z7g$SS#}XcvAQT|sK60ruUP+M{F;fI_&P0uD1XZANEIPfmySuALChp}g2{0OR@<$!0 z(rhK?Sh6^0Yikgw)9F0>-~*DOTtMn+%yr|`qC+lBW^1?8?ZTNk{ZmBPt4GpTQqD2U zC7(4?1B660Z6@t6q4Ka+lib2UhKB`tcEVBV z4Ye98No0rqPCHrdjs~ICvd6!!mTSdJ3hCua%J2gcr6iq#rh4t?pMRG9%Iy~UKu(^h zGd2cqm7EhLi*wR7cqU>BtL^UYxZZ$K&$MvPd0MGhk+WwQ>Fn-C#Fb~%Ny2RODn3>I z>iVkGsfPCpOR_Xzqjv!#Vqtq~%Etn%CH;9TDCF>2zI47-RS#Zz8@7?LJQaOPtrRa3 zmq{$7GO^}7EY0@jtyiYXp8CkxOo8y5r}KlZL+UyVW?WLNc-T6tOzNpqKu)58xzznb zD8kZu5T%0?3z@K)1#(0Lo3%F5LM$=H%K7?eB&WLrsa|Wh+p=XdNk-R{s9zzLb~~{w z$SFA|s9ZQF3FN1bAB5icS5qDPS~$9_EEJO{AsgZx2C2v%i16l z4P!VV5v9=WRn}T*>VClt|5PK?K?6}RqO3(3jUEUOsKc2W-1027JJqVL8NfpdIbknB z=8_J~a%1B>Lx3C~rYcTaH6TMX7iFnLiNq1>vsWuuM!rf7DkbC;D$ZI^oWEHpA}>3Z z$Y$8&UVFX~JeP`1eGGy<19&@(g!XfenO;(I4v-ThmUOM;97}m|PB#*DjMhGTRssWB z(7;$ob-L?WSuSjJFUl}-}Elksu?lfpBK zI@(x(Pa0$*>orFs-Ng>Xek0H@6_F>c*Qh5`sYt~r$J`!vf&=3m5gx+Lu{e zd`?rPWi;QCSE|$@Qm=J79l3)W=a(~aPL7b%EjcHk966^OjygvB;oP|wwQ{9t7M+_H zTM{kqD&XQAGg|g~Wf6K2w)tXaMmvmMySDrMIfH(>e_#9mIx!(1HtTh<_NZEQM&|a^ zR1nuwa89>Z>8LEQ3e1zJDw)DR497^)ib-i>(g*cAYi3ja)s5*)Mq=vNvDi0

    y|; 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
    7JZd@_ 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

    -(;{rH5k?GB28|%uW*cQBt2g+KE?j7L z!JlTlS+jU2hg7g@I}`F`4%wfQ*hrwcYGxMpqQw{QsU`xs%X}{rnZ!oy4EQXxb;xF= zu}^N-^h08Sh3AZ?s3J;+dWXWRJ%4^*mDn*QAN$YX-HsAh-SA)F4(hrUKQE3b$#2RM ziuCF-NhT%|Pq0Rq`8FEXT98+i#CNiNLtm5z4g zC{!xQu{zg~C~eFta+Q?g<^t}c*VEIGRl35f!GPJRV_wohD(5=oL@uXkARpo36+^A- z^~aS&yvU$})JXz~A`hV|~6+_)-(QCE?-&0vC;W>Y^v{_0`$i6zEjTn~{(!{Z{`1!NlRs^oF`1&& ziMlf!xi61};B=cBApQoa;hQ*`4ZMtJT?(S!Bg+u`<8O{PoUQI=IPanvvZ<2M&#vV3 zDd0vxjn_+A?Wjs%1~vVL=y68_Ef$&Yteg^n+z3?g%6kEaiv(Kh<;-qmFB!q*N%B5m zb=^W`Q_{FG;M$ew-V`O0m%DeX4V5D2FTIZ1`RjMF6`h%CjguWZ^WqZbE4zk2L^hQ- z>A+h3<^6x`ln9E}sAdX+6cn(ndEpOp|Q&GMwq zX82rw=dPVtc4-vewjhlPcA4-cm)7^^gxTpF()cZ$Vy>Mrpy6n{=^EAt$yu-|(+zOQ zGZk`B@Jk;xt;%|c2Yno@9ssSTZ^5Hl!emUeR3x71d4(6B2cPFjR&mw&2t+>d;f^}> z9ze*Mewft-Wu}sx9=&C$c6Hkx54pcqkZI`XgFQjT9Xsv&v%$t(SU=CERM(&M^d|ut zg=Kc}z-#mR5C;6EUK+)Ox3JsDfPx=*<6S(1YGetlvu{>xpXh6?l(L~F9ILsX_KZX#P!G{SPl#3HPDfCi=k7&qtxhgqX%b;C9pXZO zGB^cUTvvuzqs79J2WIOT&%q*J^DAryW`+~+6CoIQ=&B~6Vkoo3n7@XA1@yn+J2QVM zVH~Gblp`KXc~QA9?Ua#YK>)=2OOW0&aLh%fiaw|2y=HIFhVK!x%;&3?QvK#bAMy^5 zciY1WsgYQ3&?LamaDn4#S?II7Hrdm~M*Hl9*~z?1*IGH;oox8vtj|)Fc80?P-5F8c z6!FEo{r%|(BbKH3+konl>^i59$}9FKg*S~Ig(2eQuz)J%buqorpzN|zg}~Bhpiie< z`vy1kBm<{`WubAzQXQT->|v6#`q6hdu;cCr*WuO!htJY+yD`f*W5=vCwKNMrxzs0b zjF~q-iI?e=bHH(YJegu{4blN(44GXp2O&H?6B54#i=F=;NkKU1)CJGZb> zu3wVJJbl{p15V0a&dkG73XD}n*lszSzL3?#V@o08)bM4>g<>cb`N%leDRvLNj@zVx za;OS;=hhp8WCjbcGD?hWp5~%rT)e&gD=)uzBApL`LBThe`KmabGPT#;70}v-b_uZ= zif`#n!ZAMKDcp3uz}$W+plFvIb1jhy1~>dFoxg~_Y9^*XN#%<(82h(3uug3aKodMQD4BlQVml<#7yL7rJF*}cw=lnPh6_D89{l8rX>cxA1jXkJwfl^5ySsJ?ti=f|D8Ow;8|<)F>w?h*-KbYSX+hmna=8N**sLDDxq35=>{WnJV5(IH zB+s0kJ@xnU`0iY=Kzc$xg8594=+CQN6IK6nzB#-;u$au!c+;);7R6c%kb%Zhs?q=> z5&MfP`csLpH=R_TUh3m>9Ss-#@Gemm?*2jOdb1OMBvL1Xg<732O;E6lM$Ojp*Zd~* z6h$1q-YJBbE<)7DBt<3{&bKjKTvn3#ke&8M?;p6^AKPWpp9fHp1dr8*FrN*J;bhHi z_m_OCXHvTk7K5EqVv-idJ7YemYZ)^eV;A?uvQAmgQ6#{!v^zWv?Gr-Gg7;SOE1x^v_~@!R1U zKAL3xyhKm;`%JvZ7s&bOIR(v^9i+tdG0P^v)_eY>-`GE56iCNU)+xzO=u|~yKDI>###wP z7qC^IDQy_ZEs7t-!E;Hb#X@=4ugxX)^v}8E(rl-Ovj!xwmSG4h+hc&?{+JOf)Bf1B z{trpcYDXUzb6$s-Fce5`c>0ODUq#7tSF>HcKrItKV% z;|ynHeH-0YS%7aH@{fsEnKnuNI$wwYLbStzj&G^Yg0}CeJR{DOEN0U!_f?cXLwMLy ziKJvi8AN+qz9VgQy%E2{wWvrkhuXlKd(rk|vDG0^J^Scv6pAzDs?;A<-%Zh`_tW3s zGI2IG^3PllP$j;5{ELnLw&f58)OIWaJ(3reydDct4-;YKf$*3TJEo zZS?z(95wz}v*)`xW!ojY#e@z{AssIW)iu%yD>fUu(o%Bcj0m{`rJqh+TWnRp zUK~oX5|6@M-W%tese9;zN_F(HKca?s#Mft}KCXE;Fpc};=AIMtoSR&!zU*a^#~D^poPA2jQjv*e11cK!2j_<5D^F*Z9l)>OcQKy~!{3WmCqo&*~L_&v1u` zqdRCW)UpB8T;7O6-6T5x?q};dli=KPU5A66{QhTgd{gthN_Fgs3-U(Dza-G&# zdr_DZT#HRG2viwg&{0|lc^Ifpbv2qcUL&*oi_H-bEhBgH>TwFPr_}qhQ=B@6DbKoc?t+)uJ;oTO@5v4wqaHvfKvIcNJf%e*J3nDPP(r5 z4ZXQ9MIyXJ;tfbC{Sk&}(nR~&C7Yp_7~I;J6NytsXQQ$rCKH?QTgm1n!p1UUbJT4C z4j7-Y1OxeICvv(=8N&Fn0vZUIyB4nAVPL$6P`gKXG9!82MItacTyS z+iA?m;}?SynJZ`*)YT*VK_atc%^DaOkeQ?lUvD)3z8JYYsN#<>Eh>%me7dyX6;Gy) zXw!TWo5dS;vu23RL?<5N4AJ}|oMv{W%*!*;q)71^I_cZ0ZpY;c-tg$J*$=o?Hh+9& z3#WaCI?AE24imks4030c^AVGYcEu^%MLg8HWz&HfmWxg+uuS}bXxudw3=F_s`tnu6 z05KmWdf^unra($K_JG=QwTaEwM9#f`hFpA;t&qX-U%W$ir%@1?fM)yKg0WJRgrVj~g6y}@-&oh1V$@QP~O@w8FhXzCJCAjdoL$|B1DnbjZ5Texg9aM^&>j#rY$FJXvb={%U#DZ;G+;)@P5=|WnrH^CDLpjPD$ zx4^&xMvl7*5ZS7z!sfcb@_jwV3_lgdYJjfGR2K2eI<-o~>wAon4M@RQW0b( zgV^7DuWpONjjdw!pj$+FVSN(U0_mJ`Uhi#Bx%!~KIt)E-*?)!SE&nbIG3wAzYw}9zT3qhg&=FwAYg7! z_n$Fy!1+mkGFSk5Nnrf7sEHHrOP9o{9vo4cS$7KK;y;cqmIf9qoDgnx2<2tVERyFwp)ji(W z8W9lw+;rd!@rZ3k#G!l(Ey_gwPwDL73|M7Xr#(4~)T2Kj`w;C1WZ~t9l5_o~m*n3i zf1JG&?*-T0H3-dk={D$AN%3HkpBnNE7v3V)f4lBAnu%|cWs`BqlDTW=Te_4jK4uTv zk96|pcWASCh_ha*D)${^g>~Dop@GRhkJ8tIKoi)`*GB>MSzQMv{7w(EbRMR5mxmO9 z$K==OJDn&5gtHe#e6a9^Y0nvJV6;yLg<6iNI8L4nk`29S{7lmM%;OguTd}3K>Ug`i z7_Lo8rt~@c0|tVvV0z8+&24hp!dA64@%?MT z$33s+rQ|4~jzbRNFu9p*O6+a-te#M2jcucis9}Q^dz?desx|i{YH|F&%4)8CZL>*7lMRtq8GhBUtwMD&Y0ttqR{` zA6}cOavNs6vcaR3eCS0hA=L=rlb+0QEAzB2>afc4tAqsor&EUFktMwE#ki9DQaoPs zd&=8}GYwJ;7h10x5(b13QLZqccaS4J7Hv7YgkKD^9k=vI($7Jf*=qZzqmChq_`6TD z_(szoU%bGs4+AH`zNSQn^5*PWbmhL(oZ3vve$UzIM6;)KZ&dfhlXW1K^z?6)+bOWy zxV7ENc2b#?(24_}gNyYN!Zge^<9SGVRB_}uhxZ;&ThRWz)fPW$cPj_B$TB(lu~|UB zRdN-fNWWpiGK}eD(VT+yUSQ;#AX&ueO$Oi^B6OZSx77`92$sf%M#~mdnjc`SlqHK7 z=pXXc{FEryzdLGt(><5i!JyPr0>U4}g+Nm3N=gXHg-2IMy*j&I>5dWDuyd6 zd{h5|x^a=N6K-8`k3?h>k4C2-mxJTGbUU+3##AD63kI-d{sUjQH<~S{ldWW&Wzsq( z(6>lY;t{pxI{{hb-y+MgHV8jwRQ5*b>ae=6{ae1CQtsrl*zhE=31e_pS;gE%NV#f(Euqs z@S*)r`iJ@}U;X;H^{?;cjy)A#>-dCKT8`~A$^7ux$x(^CLMy*kXt5EoQP+gE7(O3;iB&V;=vn zF`B}3dam$oto-%A5Ziu?nY*+u6$GEMct7~d9ra7x@bbJ*hPS~#ZvKZOUoPG4$ny?| zi36&-7Lh537dyf-e=!w!kAe=)poTJ)o@@k;N)cs(YsWta?Ig&-65*y}4VK7V?*L~m zF2jX$AO~{mxLin+0i8Cxd78gji-=1Zg%%gvG{fP3lrZtw1&)b)looB>d}2rcf%P{w zU`LFJ#YCiBnEgf|ljej<{2Md+U$={$+|wm;faEbVDQ42$N~^=$Y}uu~UF!7Akwek3 zJr?6I=L}ihh=D=nH4=)~;Or=CZGM3lXvrP47(TH3(xzsqhkEA~3W?}a4&GNNm$LWZnA7Oh;`fYu0+xhtG3<#x2?l%i~k((xJRsL ztU8--_e!8YObN}n(mWv|F|&^d`>Ks+a%MxI6Qitc7g92aO>>i ze+;wONv}B)cu@7-)y<9`hJ zr#~{2GnHZY(;0^xlxJk!p*b#Ua|n6+xTk?Zch2$O%AJ3uvHZ(>dLA&U9?%#0iLcYw zn`!!tT#?}%(WZC2QK&1eltUY>>fY;QsnD^NAeW9A-y7)I};OVI}GekN2gtvf( z93_uT=k+_ZzzQ2bCYzsFcH*vM-kXyrj$z?2OCaYtL^ZJ@Eu|ovnZIHgyTE1B#&U+j zyX@ym1fA#Haw%c&vSWH-#;P99ne%!wDL4yVjiq$mbLR6B)6d0&?fXIO!0~XI>srF% zH%!wdBwQ`D1P#UwZvV9^fBFCuHk5P8be!QkN_@u`OA~O+$|vJ8&nDZjJ%IjHthH`9 zR>sG{$PsR0n>q>1NV@ES*Ob?XXA?OM=gRiV`F&82o%MBjk!7`J90!#; zruI$X_@MtCMeon|LjgtFWr{4>I!)S;Nt@MnR^$ck?PH-M-syfU-%_iyCu;uP#W7Vl z&$~yp2|uslvH^vsN3JgaQO*D6!sEae?eI7G zwd%felj2MVX42DV62Ce>767MgI`8cex@Xzi$StcOTJ0@~|8Fw*OV;kzE3EFS*WN_9 zw5gN~UQ9d*p;_YVun^ydP+hg|8q8pJF=XFZV)-sl$>TduG_z-c*dT`Fl*!=-#XurT zzxSDMD81*VN&(!|dN1gUzIc*NV#Z|WZi^!Y%w{23PM*4o9a61|3zfuon?X!MOz|hn zZNLZgV`Z3w+d9L$`YQ{3a)}V>JA_O0;6T3WWiu8ixjJX(yBId%I*7B|pv>!Pe%Szx z97%S+LR-9%hKSe$|qig9QK5bw!R*#yB_T5C&~C z+@Klt{$m^1B$Vs;XqCI)hadUuw^Vq8KcYgm((Q2g7TH+cxy7uB8a2bZj$0#Do}3{| zwm!{v_$~RtSRn8H;O=MNB5Con9=F=cE@Uh73yI%yycdSZ_-BDues)R=(=#sJIOEEk z+2>|+zNlOErF?xEx`TFb3ZKf*I3v==rYs%pUjp&PVr`#W{23!^D=R$MX+`0 z)6h;^hrr;}9EocIow8<-#satzhttOIB!5mKC?sVx>kc1x_{N4Q2={B)hMF&9S6F42 z5Kn6x7m26xr8h!#9fSoN<2t~98^X5+bYNNRF}fJYbixQW$gbjRZT$=74VQ=;+Fj#y zf=@Hi#jaCRhK2O|^rx}rIZ_6}922Qax#=cpfO3<7=+t}O zcMoEfePMTqf_&hX1bJ}$f{I_CN`N#~mv7uP#5X^pYtoK(S3b2LOc8Ml5EMnY2UbpF zfmkVn%-rEma<9i7RSl7|m~^5hO}pawLQnvE+)je;0+V`xhx%}1*J6kF&{0imU0N{o zL@RhU;z@?>6H0?0nlF%O@B!RqcD|jlvh4q6kh1n6PT)H&PA04;V;#3TwCOj@I#Lpn zyVM-NZC~WtbVL?QClVP_aCt#$rgCNc)sB;_(374^;E^P$xsJ~!p_nhspSRsl@>qD! zaGGQa3Ii$|zDO7KSGXH6otF&h_q}9DaW# zGPkF-6MXsL(;jz+hV<+Qp{wURhf`mwy7d(h-$!g$-1W|?R#)3oyn+B)vi{OCp*>F>*lE`J5>^Mtky>0F8jiS2e=Z8rlPF!$ zJr(HxX5oxW<3vM`1mBM^rSm|uy6#zhknE%8q(a?#R!q7=Qr-5Ip>qFN9nP3UiCl1>bm()L{Qen zXJaen>nx+#J^I7mRqqg9C>gR34G#dOU1`t~ezd3T>8^tRQddX4BhUY=+1QVcB){#a zuiw9*@+GGS{rX)Y6HT}O*;Lp1P{Ev1iner?143FJVQ_Ex?_PBO9iI4npvXS$dON<> zFU``TNE!TVdwiH#fH{0uSD1>PeHw+eTRyWr5Z6*)npV-f@H-0`FuI*nBT>AeJv4od zWSOLsHttkp`fVsUqKcF*xOVZuQbuR>%8P6(F17Mg-i5Pka(R~hy3xb?WzzRvHDaH< zu+4@1vFrEy_0TO(Dm$&WROe;p{}7?y8dU!F|KEx6ZijrHJzAAprtiS5--cS`CC|$hKHoYl;BT_lz0Z9vNyW4*hbSwBq z`OK?hvITkf`<;Q}tl04;g4tM1#o)McfqlWwbR#LPgR?GGUF@j}L}hsNgvT>i-0Q_o z&)mLGFrQ29;s~P$3ZP=d?cHmtnru>xhS)FXqP%3?%#w5k_0*|%pP8uOP|stlsjprS0h}e<&GvzlNpW5( z;MfeoIf*h~i_h|cnxY4k&hTz1+qquD+7HqNFEF($_L^0giPO8NmN&;rS|zb~b)JzW z7n#r`<1KMnuI>Ve#mJoe_QVK+krtZ z{3lTlnMy5r&>^w?GhUd~Jc{nx=TdsOlp!nVZYwGq4Io0=t#AOQ6MbRFC?`TX*u!=r z1AF}ZSBb ziOpHuMjf1;1I$QSi5%=w%*ff;(+?l1D3%d6Qr!7erFQ5k9+>uT;d$(kuX(8O$NdOFy%zOMI_6TW1&lW5DH0bCeaX_xdK>o{7pz ziG2nuxeZ8IrpYNM6kY*!H{whHx>~CAs^o<=;ei=DP`?s9-y;DY0EZtuwWAm;L-u0I z__PmQgMz4tS}h;$p;A!15r$6y5orF9bMF4D|M`>rmuJ1)-S%43JMDvUejxEksC0qk zjV) zP4)yeUS<7e6W&2joH}+zp=P_8+&hXq2zOsg=WK2A)`RWzpFrK6?N$$DYfZ0OtYwrG zGVE!v(TA>$hSOD_Z-%qcE7StT@qtDyFWy7_=lk^?bJIH|GV?67oTgk2P`>}6S^}9L z7DhiPcHUZlznQTAVTKK^gIw|`zt+oEJbC5G>^f!!A(ImX(n65w{->X(8Xy_K9qXQh-;QwRoJHVPuwuVu$ zi;56Y5KuY^pp<}sAPS*`W=KLH1Y9Wsp-L!9v7;0dqy(gePDmg@O27m`MT&s55J0*} zM~Wa#(cM3m-P^vs<-hlR9>Tm+&YUS{-kCF}%wWFu)WjuI0=?fGe>JBV`8FJ*Ben+C zQC^L`OTYXpx3r^Yc>Plm_}w;NLg2y8VPgA`$Bf)zyH<|^d6|?mpp@6yC*8#+s&YYG zhsOM~4a7B8#N=E6q66AAi=q4Bd?y-0b8mM#SWCx*><6TT0@hf1aSUs%(m45yySQYc z!Mr3TpVceTBr;|=>n8!cqTD%mNB*s|4+&|VFLa=GkEuO-u=Vw!h9ZbPaNMuBd+YgD zl#Z#Cmmv`+Wj#^7#{+5UODPORv^xpcVd=STW`ZF~D@HZhS)Yd|{H6mFGi;q`m9J;y zCLu=I*x@S|R4!Qe>R)Wak{Yw?DFDiYqs0N;f^7IpGnx@}6@t3bxXna8kXn%k1j*D^ z=qMU9$^EoP#JHC_YHg^735+r8;Q2cu? zF)qZD#)6zA3KG>zq&$eWg{b{6=e-*n;oHVMm$R9tlCQ&$ht*Hvsz)IB;Uf1>Y=@x> z3`FOvIV=ucNMOsm`RqOXK4ibz%m-BFC-YKRy$HIjHNi|t{P}@|n!Ejg&-Oc%n3pcbIw$74=Eejw zgS^=t>LA%-672)K`hmxhZhMoo*)y;n)Hz=$nq2Z~8aYifb4t&xYvFTY1(_JB0Cj{3 z!ykKZjc#hd{V1NRWfE>NrdHB8thb^lG9Bkjb?z&(wk-A(ei0 z_;xVh^xLJXs*UN;I_Ck9JuN6;FZqnhYTE&w*}<^R)rD;nwb~i}1u(V&z3vIZGF^II zBSu6hJJ&_26(cX}IRg@#%!#E=_>IBje~^y2#`g?4xT{Mg!N9N|l;rlEG(4K_ zW|}hPdM}}ItIP;8@JUzWg+~2+Lq^jJ^FLdlTGuMQl{P^a-sBQwa|XB;(_(vyYchhW zIQ8bo_-AAL;Y}0Uib!IuUWhi8M@{_Sk>`rheP7-relZ;?iOCDo#->vGrBGLD*HQxP zB<)1=9yZ|ppH?&WJ1QMXn@3+L`Oz*l2c4&4F5A#r1Etlh0-Hz@h;sVG?K>`UbUMA_ zEeF*kBWZAPJN|+i%H)feZr@$c-s`v96&aJx}ynQr2EO!G*$g;lp;Tw1>;AY z^?v7}VV$SCu6#pd+$GmnSQSl`sxh^Cu#sAjR(FwfWc(&?=A^qLd3H2DG4 zM+hsWy5js$PE7McdLnQWxlLNAX;u9EOF#PP_N@}Fk~Yzu#1~}9a+p!aqpt1*8xt^v}pTwnRbS&dB z81u&dh0daU&eiZa#!Ha9#BCiI=wh)-v3AN%vmzGUZs{4ZsNE3JWNORj0j^yM34!^+ zl`LKCJDqM5fif=IU*mr>|U(l3D5q_IOe!nAazhmeAgM!{~DPZB`f_|lYH@k04R z)~(d0NqYsKan7pw^1GaweG-dob;74}X9gv4m^$RN(U!IRnb2}cS4w1D9hKu}G+Lhi zq&ePhBG?xa-qJX%YhNtb^XRa9TGa5ah%_W5v&XqXQ^KVfk3>b^vuhnb)4%)6ki z%Yx#j?kt`x^@ZUtL7JmaI%DL&U3a}ZQ+j=qX*mBX8b;13e6g+a&buqRH@{5>IkokB zRi0A!&uC~VPn;xtJD$`{&rU6tC1`0}a>e1or?*vZd#7ZX`7QIGJ-cWH)NyMT6Xr|1 zwXyyIN<2`LqncCF+`x6Qy3rpI`LX|*)DE8-FB?V$(8VEf)-^jXcj{TFw=1o}jT9Bp zS2{?BKb+R%;j#MlGyXoShmjna^qdtx{2r7MN~(+RZt)HF#nU`=)-}=sgde4zv2==; zqahF{0wvUMJv`(D^L&c8LEogb8vF zOD#9@NZ|`rlCSV%Yt98rdn1588DD;qD1~fh3@!*q%e;R5In_lWP7^bAz8;rEGYhb` zkyBi~z&606&ac$^Q)eUsZ@5|AzL$~$b?=+_RLcplxtz1Y;Hpaw(rm<)ZGb6t9m?7qrAd2#7P>gyIFuiGa_ z(oW+>V8~hLo8EKxIuNGDv(WCk`8#&UYMQ7cyYO~JAJK9b74`d`7KMndV3fdzl0-8| zA?8vm(=;mi))Na3*CTQ4zDfk-a-}pxdnB9I9vd-82ZvlxIQ$u{xTz6zR4VW)_+naO zX`cQ?FV8s}+Ol$At#-hJ!#}T8{%mmnEdK0Irv^V)2ScvP$UutnVI#vLOm=1Ns(*#b zjEKz?GsU47+&Tl%w|pWH!n&cgWvaR_Z0KzI&XCeg?yFc8*H8nQdmP~%UdU*>d2KQ9 zVvyyOzGI)WR}HGU%mkG72pPP3B;;>$UC#cbOP)S*jp;cd-FBIybF5GJkrDU}Ch&+z z*-89+Ne@>e?L5CU%dqFELtURbctzyTS(E_{3u|HwA_~)uFPTSG7TJ*{2RR?m#Yn@= z2Jg4#3o~D)zuAo7q9Ts1a3D}GbCmL{g?kE9=CFoq$+VEOCa!U|@_l*E4Rjru{eM$n z&ksU>cBSHle@uWje-k@-)=IHTX%adUR~qJx1*7CUTz)pt1MaD_{R{<^XyQ*t_lXr_ zPd+uZ?>rRcKfRHPSNZ|=T9T`{Du64LgyuSSgGY$0{iJR_HNL#aSdA>o-KIxdw!?Vc zuii-+d7u-#n|(NEqW&yfYU@uAJNI19gmnceKNH5T2uQ(oZJ*7UZ$zT>{-PCD%TQzJY>0G1d(*=*=h$qcUTTYljj9jt}#*l{e9&fW_ z2@Y$LxQ++LgC(-FvsZvyrg?Zi-Tk&4?R+h+Cw}T542%AA)c;65@PFEn|CY?_pKvlP z6e&l^E77E;U$Zjc;n|?#&5AFGpEGB8-CYebf`DN1dAPp4U9{I9A~z={5^Xf18g*J; z6k%O3nI^ZZOOl!vS4?3SI3AV7-U1Y1?s-JFM$K+_P zs_2Y0^{Ae(da%a8^e8zvo5_`eg=Y@)Y;h>exy@R}Cr7M6G1(_kryoCjmN&8(v;0_H z$aTIIg3OPZ&AJV8NkO?0P6I*&cpZAprNaUMdBxW10l0B%IsiSKqZn8V)0J6P=z3ib zamFLtyXUtWl3nNLqae^gnIj9uC*m*HShK&%=fCV5q0R!lKUdf+qP5*TR z{?AtZb>u%IRw7s=R38;9Yv_G;-)q4JODk*R49edX* zSigK%d_#9G&cz7)DR@&>DB3GDDC)?pZMYF60mFASw)DsZcVo|Q^=R$i^ za_*B(hPbqLI;EzM@Zn(>y`S2W>tXJbn_Q-y`ZUD1VT}SFF&Faqx?MM@^E_#!`w3s2 zb1V!b6-drK?YBnXC!)Gsnlxh$o()VP7?ZeqA&ggPjUw7wjYi)29@U``dp81!T$-A|1xgFSn92F*v5H*LJ4 zaYN-L_HWU|c;P2Zl)|>sttWAQOAkKswYD^tX#S-tQo#lrr_Qmwv%My`923gdCPk+@MRZn=Dk4UOex3-@>goFgiMgwo!{8eXyRw^lzHJl6D zB2V5rbyX^DeZCJ2P-bgj=!TUHOBHMVLnZWJiDxg&R&G7sOLBk26uCNL(ao zW^wa4^d>i#I6&oo$97oi{&F^S8YGREj%~-mbAZskitBsTE5XR+v$e5$@C)!GCc}FX&m3 z055o$x4ms&=)3;bv+@=DYhl4=cU=cRqURd-;yM6P%so8@3*OF&Et$7>WWvrKm6)*_ zKQ|Z%y*G1VmI7;%$U##H4X{kl;ZM@$g7XwV?-gyUsmwf2ZKip!U#|}4&9kGhFDh*# zp^QqWY1_S7_Sjw!l6y(n7Vn+kXo}d7!kwBvv)$sz@)pzbKJ7AyLkT@On+e@WOP8C> z2z2~V8J1)8s@tz{wiu_KR|!-^BKg_FFk4UehoVUFLPOQ6VPT zLQcN8th>xkUi`sW9#0Cdw9e;XqMv%~mtJ=Vxue%fCiaJ{&cflKdY)8X;rV@(3kid< zpSx2^5~cwn`ig3?hO^vD(I-BFTYaM|Lz7V}UYk3rFL#)q=ip|q`_JHF0jF_#<+nwua0}~CV>h}A^R%|>ECfFqkaQQnF9VO_%GN>cR=Pb=DzO_z zn$_}i20}Bl$aycxrZFr7;fGRcig{xY3)StSiH#DjSuGz87?JKJ`LMbsU; zk`qs}4gjr1W^O}-Ovy>#L ze_<|3$X{28JSeQgVTRlabFz%~XP(@z$4%D@Th`idf{lIOC9U7vIr*#cQ(`K-kaoe_ zDHCDt6SP}oE=guFK*{0lMVk(;xZjXz(i5Q`wIHjBh3%_&E*F022SB4gvTT>0t&s)O zvk)jH;napDReirFI`baW#DcCOLgD*kIXbKDAkSK<2;&(>RIvDd9RA+28` z`RvfYUg{QGdG;_K?Ektba>+Q^BYuyy!dev5eZ;((U&4gcJ^2M@N5bowdoEZFSgzzO zw12^GEOD3Qk$W=F8%ij54QfyD4@R*xTAE}HRgAFg$ys(`*G*O1Gr$<~=}nX6+MH>$ zYlYo_cl3Ep%mjwn(vx2uK(&?iGFPjr%)SJJEBbA@^&5{;-x_fwcJD|NN8J3zxJ*5( zSL#qrcDDIA-Ap3;l5l#2D{~<3Z9}%E&6-j^Q!9Z_k&HPf)L(Ez0!;r1=2RZe;itaY zpoS>9mk&T7W+gSHh!7`xa}T?_s6fgxiAan`K_p!0)(kk;gxASe;)JvEhc7*A-~2g=VXNUhJ@kWlC~#bUT_)Iko7 zSf_Xjw7ep!fAi%k0xT-1tbIqNF=T%0T>4J7$itzpZ`!#mbMIg0T&ZqiN`Lrl4c8qr z@iQgv(V|36nD|!Zjh`3yh}i-UZRxsy$Bh{{^l?#>5bB=h`= z$K@~t@OtM$Rdp^Ifyou2i$)jB9W<$Sa!B`rZ2K1{sDDWte}4Y^R+JZM*m2HWGRNPK z%J8MeQnP@ewOjZ`%+;?{rRt=T#Y3jaTu*R$lGEM`Np9GJZW#KFO##WO^X^vhny4E9 z4JvE|ayBZpTVDC@5xoR`Ey9{gDkWYS;xY@-~R_Rt%c%kAI{OfXd8# z(TToUfL>hX?@|^f7>vb4FwO7JhjJ{itpX#$^kWp^?8;`ZzS;M7&^j+I;Fa206;4;L zJ@Ijih(xp;9W!B1$W(n%egRQksygljlBG0C7`Rrnn)`RpmT-?tRN!Y zz$KjDDd~1-7?%k5>8!Fhm|x-d!~AQMZXn*dLIaPp(<68_e!P#4*q@Bf=sumyjZ-5# zay#S4dr+_GyQx(R8Go`PpZuGf`a!+s@5rkbY80+NXZcyNNR>h&wIEhIwN;@XUc$FW zwxk=(=fMx4d!s5-b`<}ou)o(O`UBC(bon=TxJVux`tcs4PG#AJ`+qbjHqkIM0?&8d zowf3T2AGIBS-AsOAPUPtmP_mP&;Lmp$z(o9u5} zJ8`Ug4lxbBL}fN`JRd4Ir>PL$X1ZB4=T==&NzbeEl>-4QZ>;r8ESd~|x8zq3$`aFK!kQ83ji2M`30eDIw(Z!&k zaK*H6*@ZKa5o(<^-Omb=7*h;((d{S0xwCH{ysi1NmdPYL?uMzl9Nqct%QWcHNK;V62~8QR~T3uOoU*fUtD9E#XGpo zlfVp|=;AA)TGav@deKRm6e~dtug~X`eVDv=4@RqViQ3 z4hPRp=|s`3$DrVT#uk^~H~UiYB6$T-)w^%2|GsVFE%;U?&lVx?Pb$UJ18cj_>)sP| zf#}D+qE2bZrDJ%09`#Si*VuEuk|R=o5A41+>7p$v+09>&jI=rt7}-7skZQ-S0A*BM z`}dfDxSD}arEvCyAyCIJF<7}VEO>fN^EA*ch)44BlN#2dj}g2^!RKmKaf%%@&%B>B z5m=cEoy%X>#F9%Cf8ln5UtM1;dIbWRMKnF!vc1&TNc0S~A^Rk-z%2WE?UVEqfp+8W z*Sk4kv+vw^cy=jPd@{9LNdnk`?PKLAZ1hg%K>jQuTGMKT;a`eEQ?tUUQ!H6Ym=@43Q_xaHto$ONQDxN0<>RX`9q+Ud-06{iVCGxkb2?zhRSQ9H;uV|&-$Al z^{SJg51td&o^XYQ74Moyv7k=|d#zaMNAF^_X}h^?{BNGK|AH@Zg7!?|{WH<)=gl7W zj3fq(5T2co(fd~6-&-aQFB@``$aA`=;6_Qyj`OeF0}b!m_rPyWBDN=3_^7-SZY&as zBihS|3<@Tcl&nnmv{qlnivn3Jo|0JW1NfGe^{;W}Om+(EO5KCUq23qC&*p9cf3(b~ zg9R=qFs$5fq+zx8L26f3P!sWn;mBj&%C@RXQR@t$4mNcghj!m1M zv!Umc$yIih3RvJGjy!F8`ddZ(iBNgD6CqL!*4NS=&EdLVncVyq%^MaLLXpYC7evLi zWTDFLnQT40zHDDLum9}$m4EcW@U6su@a+8OTp9l5k%C`>OIG(8NsPkmJ9aQCC(?t) zieH(;sSJLWpGv{H!xpZm+hE2_ShJ7btl--W{8nVH7973FMd2^Y!#8oa=csvYO^A-F z*d*Pa=nx!No(P3%NanZ-P9rgI31xi)iXhRNSXI)lv57FX3d1}Ey!sWv0C$XOB$?hl zv9%6jqpNYUYh=wty~;{rE#J6hXT!wjkY8UN%KYA1-mrbztf>1gLh#fVTFl&A;s(<_ z*;&F-1n!U)UsT)uJrmE?L#Y!(mi<>FOMeD#CgWh!1qD&j>?nvync5*uObOMMa;)H< z-8^nuUR3J(p+a=GL!RGE)9t+i<&)6lV-xQRNj!!eG-bXwB)B#zs)~c5hedh-yZv_? z1y)jZ7TVVHaC4@{KPvq8d#u=Njm_%4Mo2HhR>%vlo%GIS`_G~@KQv5Th4xT4H#GA{ z`ZYF5g#9xT|%^ zfYD@ssi+urp$T-k0M}0;XYStIJDu-L&${L45ZXq?y975i`{iC0wGrPk^_W-mo{f;zdIe3ik~Tg(wiRby76OO;jq z)@o-1&-iNL>Zc}mb8o@r1x|mN;1@LKd#Op>oqg=CZVjDwq5@n%%Ff^Bz)_|mt@yd<0RIY_93C5AZlTddv z`QJx0Y}xKhh*4>1U1QgFoUlkBP~XqPCE~#|1U{ zEX`~Dh}!?7TKDyDaiZTqnzwzAy7L1hAhq7UhE;PfPsXQCY~tdzR$0mZJx4J;>dr{N z(S!XzpdX4mOa&Tp5bq5$k3Rp83H@&i-1uFB@Nc94c$1s~W$4%&?1{if8wyQ7eE63S z@Xc>|{@x`ahGA`QXHiFpC173mlsd`OJ;eANxtA>iISj)T%wy*Qgx3l;-;$jU5rVke zKD?Ryi5;CrQh0(}?kx-pUCQ;<*7s04C4ysMLFaY(6Q(YAfmi_va}biaLU@2{PUb_2 z`4Ji9SCQ+_4}Hgp=O5XFRe_b#&FP)4dlPV|5_o0@w53G=%5IqRZ8muj4*=}TbIOf{ z33HmMv3tw=FpVbK{nh&oeJ@DnkxD(PcLKvpBz&&if8VZFL6rCQequk(&snY~ht$tJ z_-M-Arv2|8@qfWT{7diu?e@o!bQ!M9*75DhM~2?~-5%Y$>cjmi5EFY#N%_Hf#pGb2 zS%Ht5qI=V#+24$eZr$2l&Oxlab|QZAhFuZiY+hj*g*w8mloxTNL8eWSp(M(hekG+n zATugaz2<5x*R~VR4+_V~1BrI~iu~5VO%K)4-5xLi*T|*RKN~8wg{b_j_YX8%8-!xc z9XIM&_yjQ|+M`nobwqi7e)oRo^ZmhO-w~FTn^=K=eLp(3HBhHQD6=O9JN<@Zl>5L> z22fS$YlX|~4C^0m1lgZ4rm&+XZ*9{VCe3j7+Q13xMWkP&#+xroULXL2(;egyd`{AV zQr$*u)67fz-;ESkfd8Me*mfpS8B_{8BACbTz3vF7H8d_Go}p37AiR6Z|8y|jU?R{I zG&Uf79|RK~vRok)a4~BHD!)ctR~sV)pK=Djm$$RqI~q)t90*5Xjq=L1O0awoq=jdd&m@9&gO11xwQP{}it*QmrIsMM?_x+TWMD5zUKo_vO>jd|P!1sHHZdDpucDdMDId|x8z`p%a z`FRTs$0o;O9_~K)Bl$1esAQok+;| z(zbr)x_{Zt@74WpezyFn1i?4>JE{tQaV)e4@x$`~uRMhXY~`jxNE@-31t66dEUj>G zcot-R`O=ayrOo~9&>kPIasr?Cy4x93qmXhj=}gb{^Qn!h>~nvbqW_v$6oPr1Y|Y^u4pKVP80b06)3$4KiJRlzc|h(=0!56anHtmG&LHj4Gc!+os=QJ#Z!Eb75q z=RD%>t8&sj`YX_ylt<%!G6BA9FAYaZF0nW{Am?h%mAlC0Tj~RNRjxnEx@Onf^D<+{ zyoeyVNk=G2AfWPqMg4N>&_c)6#0aI8jke9&liVz!Zj$xN+9W; zHQ$HpbDOoN8lwvb?lgvQEukH3KXr7IVlp%4mRFIkCkLRyEZg~HVQ-VpA$nF~ool#{!(^x4fFHYk^> zbG{;Q0?BL{AP{n_IvulhyVz_ws&u7QVZt%kJ+taU zQEB0F{Fr=atHRs=4MF^X^5gKE1FD+ z6Qr8iQj@)G8E~5iKO8t0Kq)JuTTH^2Eb-!=`z_3#!pCAZkB5}?n%D_c(hNexCbdP6 zp4ZxhAu*raD87%vo7Yda2Z&S{$xsRMOH;ySg@=N>gQ+qTOvKD{zKp^GGR_VAnC_{_ z70c}yeA?RreKU58--Xz3N4mQlTdw5lPwA#ohaoTQyGBKX7#8;D>UFh#<9VgW&W@{S zl+em+4fSi!Yfe{0U6s21x`IKY1mobQ@hz>A^g^)-*0I8k;n!1?fTKPkH7367RU~^b zdljeGJVP%cL#sWJ2AqMwa`(+8rX zLL(MmJ@0$k+hq7=ES%dDpSe9T54VP<3}9llvgSiAacF8}IQEIdU|P-4kIt~HvK}Ar zykZ&W_$6ZZ^J_-3TiDmR}+sZ51ZF zQe5z@#6@fBm6j)BaWBoKLd90Gm15%i#MK!{MKCt1pepIgd1hHa7y42_DQ%WTN!yUR z=$sNN9+qiCS-Vb*Wt8ZAhMjtJV;jd#!+ejs2Pv{^^oY82^LxY|D)mVaV7=+Bgk&G< zD#IPuWi@fFf!5_p8O<*sQoouCl(JZ+yD2zBMFon)C`*)Rk-Jr_yR4l3g15Q2n~8Nr z!I(0waOfnndDWVZX0iBM9F^rAje8Y427#+t72DARWt?tzpm8z6OP{DYhXKS{Vgyc?=+B{vYy>Ol7<39=zPzRY#?LM}7!sdQ(E+@=WJKu;yp36elOt zJ|*8j5?Ht@;STBMnDKy!YbnN9dGuFr+_)c@yCdnEf7fMVzSmx7%Cf-Qq|Ign2Wk6OJvPLTXX)-jF*O9_~S3vtLBX+

    g5^?s#%$;y7Y^m&mXo{(Ac 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

    |Om-D05TYY`eMUemkKQH3KHz|+=QIl1&d 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

    Rd58^M{4Div|pZB(M7Be(ik_UxIjNH%e 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 literal 0 HcmV?d00001 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..56a2b4f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.16) + +message(FATAL_ERROR "Це workspace з двома застосунками. Використовуйте idf.py -C apps/tx або idf.py -C apps/rx") diff --git a/README.md b/README.md new file mode 100644 index 0000000..e3a241c --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +# LoRa генератор сигналів (TX/RX) + +Цей workspace містить дві окремі прошивки ESP-IDF для ESP32 з LR1121 по SPI, I2C OLED та 5-позиційним джойстиком: +- `apps/tx` — передавач. +- `apps/rx` — приймач. + +Спільні драйвери та утиліти лежать у `components/`. + +## Структура +``` +components/ + common/ # базова інформація про чип, роль + input/ # джойстик через ADC (резистивний дільник) + lora_radio/ # LR1121 (SPI, busy/dio1), налаштування пінів через Kconfig + ui/ # I2C OLED (за замовчуванням SSD1306 128x64) +apps/ + tx/ # прошивка передавача + sdkconfig.defaults + rx/ # прошивка приймача + sdkconfig.defaults +``` + +## Збірка +Вкажіть потрібний застосунок через `-C`, окремі build-директорії краще розділяти: +- Передавач: `idf.py -C apps/tx -B build-tx -DSDKCONFIG_DEFAULTS=sdkconfig.defaults build` +- Приймач: `idf.py -C apps/rx -B build-rx -DSDKCONFIG_DEFAULTS=sdkconfig.defaults build` + +## Налаштування +- Піни LR1121 (SPI CS/MOSI/MISO/SCK, BUSY, DIO1, RST) та радіо-параметри налаштовуються в `apps/*/sdkconfig.defaults` (Kconfig у `components/lora_radio`). BUSY має бути підключений; дозволяється ставити `-1` на RST/DIO1 (немає лінії), але без RST складніше відновлюватись після зависань, без DIO1 — робота тільки через опитування статусу. +- Роль пристрою визначається `CONFIG_LORA_ROLE_TX` / `CONFIG_LORA_ROLE_RX` (див. `components/common/Kconfig`). +- OLED: виберіть I2C порт/адресу/пін `SDA/SCL/RST` та розмір дисплея через `components/ui/Kconfig` (дефолт — SSD1306 128x64, addr `0x3C`). +- Джойстик: один ADC канал з резистивним дільником на кожну кнопку. Вкажіть канал та очікувані рівні АЦП (`CONFIG_JOYSTICK_ADC_LEVEL_*`) і допуск (`CONFIG_JOYSTICK_ADC_TOLERANCE`). + +## Екрани та керування (OLED + джойстик) +Джойстик: LEFT/RIGHT — перемикання екранів, UP/DOWN — змінити поточне значення, CENTER — підтвердити/оновити (або toggle на деяких екранах). + +### RX +- `FREQ`: робоча частота (крок 1 МГц) у межах вибраного band. CENTER просто повторно застосовує параметри. +- `BAND`: вибір діапазону (430, 868, 915 МГц; L‑band 1.525–1.660 ГГц; S‑band 1.9–2.1 ГГц; 2.4 ГГц ISM 2400–2483.5 МГц). FREQ клампиться всередині band. +- `BANDW`: смуга (BW) 125/250/500 кГц. +- `SPREAD`: LoRa SF 5…12. +- `CODERATE`: LoRa CR 4/5…4/8. +- `PAYLOAD`: відображає останній прийнятий пакет (ASCII до 31 символа), нижче завжди показані метрики SNR/RSSI/статус. +- `BOOT`: перезавантаження в ROM bootloader (USB CDC/JTAG) для прошивки. CENTER запускає перезавантаження; підключіть USB і запускайте флешер. + +### TX +- `FREQ`: робоча частота (крок 1 МГц) у межах вибраного band. +- `BAND`: діапазони як у RX (430/868/915/L/S/2.4G), FREQ клампиться всередині band. +- `BANDW`: смуга (BW) 125/250/500 кГц. +- `SPREAD`: LoRa SF 5…12. +- `CODERATE`: LoRa CR 4/5…4/8. +- `POWER`: вихідна потужність у dBm. Ліміт суб‑ГГц до +22 dBm, для HF (L/S/2.4 ГГц) до ~+13 dBm, мінімум −17 dBm. +- `PAYLOAD`: вибір фрази для передачі з готового списку `PING 1`…`PING 10` (UP/DOWN гортає). +- `PERIOD`: період автопередачі в мс (100…60000, крок 100). +- `TX`: enable/disable автопередачу (UP=ON, DOWN=OFF, CENTER=tgl). +- `BOOT`: перезавантаження в ROM bootloader (USB CDC/JTAG) для прошивки. CENTER запускає перезавантаження; підключіть USB і запускайте флешер. + +У кожному екрані нижні рядки відображають SNR/RSSI/стан IRQ з радіо. Застосування параметрів відбувається одразу після зміни (через `lora_radio_apply_params`). + +## USB JSON API +- Працює через USB Serial/JTAG (CDC ACM). Читайте/пишіть рядки UTF-8 із завершенням `\n`. +- Команди: + - `{"cmd":"get_status"}` — повертає JSON зі статусом/параметрами. + - `{"cmd":"set_params","params":{"band":"430","freq_mhz":433,"bw_khz":125,"sf":7,"cr":5,"tx_power_dbm":14,"period_ms":1000,"tx_enabled":true,"payload":"PING 1"}}` — застосувати параметри (RX ігнорує TX-поля). + - `{"cmd":"reboot_bootloader"}` — перезавантаження в ROM bootloader. +- Відповідь `status` для TX: `role`, `freq_mhz`, `bw_khz`, `sf`, `cr`, `band`, `tx_power_dbm`, `period_ms`, `tx_enabled`, `snr_db`, `rssi_dbm`, `last_status`. Для RX додається `payload` (останній прийнятий пакет). +- Готовий GUI-клієнт: `py_app/` (PyQt6), відправляє `set_params` одразу після зміни полів. + +збірка +IDF_TARGET=esp32s3 idf.py -C apps/tx -B build-tx -DSDKCONFIG_DEFAULTS=sdkconfig.defaults build diff --git a/Waveshare-ESP32-components b/Waveshare-ESP32-components new file mode 160000 index 0000000..fc44cc4 --- /dev/null +++ b/Waveshare-ESP32-components @@ -0,0 +1 @@ +Subproject commit fc44cc479877e435f515419c8d25b80810630e43 diff --git a/apps/rx/CMakeLists.txt b/apps/rx/CMakeLists.txt new file mode 100644 index 0000000..c92203a --- /dev/null +++ b/apps/rx/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +idf_build_set_property(MINIMAL_BUILD ON) + +set(EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_LIST_DIR}/../../components") +set(COMPONENTS main lora_radio common input ui) + +project(lora-rx) diff --git a/apps/rx/dependencies.lock b/apps/rx/dependencies.lock new file mode 100644 index 0000000..e233d8d --- /dev/null +++ b/apps/rx/dependencies.lock @@ -0,0 +1,10 @@ +dependencies: + idf: + source: + type: idf + version: 5.5.1 +direct_dependencies: +- idf +manifest_hash: 9db7a265ef57175d265e0d6eb7847107508a68a5847e4adbd1375406a7c73c51 +target: esp32s3 +version: 2.0.0 diff --git a/apps/rx/main/CMakeLists.txt b/apps/rx/main/CMakeLists.txt new file mode 100644 index 0000000..f0d0963 --- /dev/null +++ b/apps/rx/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "rx_main.c" + INCLUDE_DIRS "." + REQUIRES common lora_radio input ui usb_api +) diff --git a/apps/rx/main/rx_main.c b/apps/rx/main/rx_main.c new file mode 100644 index 0000000..293810b --- /dev/null +++ b/apps/rx/main/rx_main.c @@ -0,0 +1,448 @@ +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_system.h" +#include "sdkconfig.h" +#include "soc/rtc_cntl_reg.h" + +#include "common.h" +#include "input.h" +#include "lora_radio.h" +#include "ui.h" +#include "usb_api.h" +#include + +static const char *TAG = "lora_rx"; + +typedef enum { + FIELD_FREQ = 0, + FIELD_BW, + FIELD_SF, + FIELD_CR, + FIELD_COUNT +} field_t; + +typedef enum { + SCREEN_FREQ = 0, + SCREEN_BAND, + SCREEN_BW, + SCREEN_SF, + SCREEN_CR, + SCREEN_PAYLOAD, + SCREEN_BOOT, + SCREEN_COUNT +} screen_t; + +static lora_params_t s_params; +static field_t s_field = FIELD_FREQ; +static screen_t s_screen = SCREEN_FREQ; +static const char *s_screen_icon[SCREEN_COUNT] = {"ƒ", "BND", "BW", "SF", "CR", "PKT", "BL"}; +static const char *s_screen_label[SCREEN_COUNT] = {"FREQ", "BAND", "BANDW", "SPREAD", "CODERATE", "PAYLOAD", "BOOT"}; + +static const int s_bw_options[] = {125, 250, 500}; +typedef struct { + const char *name; + int min_cmhz; + int max_cmhz; + int default_cmhz; +} band_t; +static const band_t s_bands[] = { + {"430", 43000, 44000, 43300}, + {"868", 86300, 87000, 86800}, + {"915", 90200, 92800, 91500}, + {"L", 152500, 166000, 155000}, // 1.525-1.660 GHz + {"S", 190000, 210000, 200000}, // 1.9-2.1 GHz + {"2.4G", 240000, 248350, 244200}, // 2.4 GHz ISM (2400-2483.5 MHz) +}; +static int s_band_idx = 0; + +// --- JSON helpers for USB control ------------------------------------------------- + +static void sanitize_json_string(const char *in, char *out, size_t out_len) +{ + if (!out || out_len == 0) { + return; + } + size_t w = 0; + for (size_t i = 0; in && in[i] != '\0' && w + 1 < out_len; i++) { + char c = in[i]; + if (c == '\\' || c == '"' || (unsigned char)c < 0x20) { + out[w++] = ' '; + } else { + out[w++] = c; + } + } + out[w] = '\0'; +} + +static bool parse_int_field(const char *json, const char *key, int *out) +{ + if (!json || !key || !out) { + return false; + } + char pattern[32]; + snprintf(pattern, sizeof(pattern), "\"%s\"", key); + const char *p = strstr(json, pattern); + if (!p) return false; + p = strchr(p, ':'); + if (!p) return false; + p++; + int val = 0; + if (sscanf(p, " %d", &val) == 1) { + *out = val; + return true; + } + return false; +} + +static bool parse_string_field(const char *json, const char *key, char *out, size_t out_len) +{ + if (!json || !key || !out || out_len == 0) { + return false; + } + char pattern[32]; + snprintf(pattern, sizeof(pattern), "\"%s\"", key); + const char *p = strstr(json, pattern); + if (!p) return false; + p = strchr(p, ':'); + if (!p) return false; + const char *q = strchr(p, '"'); + if (!q) return false; + q++; // after quote + const char *end = strchr(q, '"'); + if (!end) return false; + size_t len = (size_t)(end - q); + if (len >= out_len) len = out_len - 1; + memcpy(out, q, len); + out[len] = '\0'; + return true; +} + +static int find_band_idx(const char *name) +{ + if (!name) return -1; + for (size_t i = 0; i < sizeof(s_bands)/sizeof(s_bands[0]); i++) { + if (strcasecmp(name, s_bands[i].name) == 0) { + return (int)i; + } + } + return -1; +} + +static int detect_band_from_freq(int cmhz) +{ + for (size_t i = 0; i < sizeof(s_bands) / sizeof(s_bands[0]); i++) { + if (cmhz >= s_bands[i].min_cmhz && cmhz <= s_bands[i].max_cmhz) { + return (int)i; + } + } + return 0; +} + +static void clamp_freq_to_band(void) +{ + const band_t *b = &s_bands[s_band_idx]; + if (s_params.freq_centi_mhz < b->min_cmhz) s_params.freq_centi_mhz = b->min_cmhz; + if (s_params.freq_centi_mhz > b->max_cmhz) s_params.freq_centi_mhz = b->max_cmhz; + // Нормалізуємо до цілих MHz + s_params.freq_centi_mhz = ((s_params.freq_centi_mhz + 50) / 100) * 100; +} + +static void usb_send_status(void) +{ + lora_metrics_t metrics = {0}; + char payload[48]; + char safe_payload[48]; + lora_radio_get_metrics(&metrics); + lora_radio_get_last_payload(payload, sizeof(payload)); + sanitize_json_string(payload, safe_payload, sizeof(safe_payload)); + char line[256]; + snprintf(line, sizeof(line), + "{\"resp\":\"status\",\"role\":\"rx\",\"freq_mhz\":%d,\"bw_khz\":%d,\"sf\":%d,\"cr\":%d," + "\"band\":\"%s\",\"snr_db\":%d,\"rssi_dbm\":%d,\"last_status\":%u,\"payload\":\"%s\"}\n", + s_params.freq_centi_mhz / 100, + s_params.bw_khz, + s_params.sf, + s_params.cr, + s_bands[s_band_idx].name, + metrics.snr_db, + metrics.rssi_dbm, + (unsigned)metrics.last_status, + safe_payload); + usb_api_send_line(line); +} + +static void usb_handle_set_params(const char *json) +{ + bool changed = false; + lora_params_t next = s_params; + + int band_idx = -1; + char band_name[16] = {0}; + if (parse_string_field(json, "band", band_name, sizeof(band_name))) { + band_idx = find_band_idx(band_name); + if (band_idx >= 0) { + s_band_idx = band_idx; + changed = true; + } + } + + int val = 0; + if (parse_int_field(json, "freq_mhz", &val)) { + next.freq_centi_mhz = val * 100; + changed = true; + } + if (parse_int_field(json, "bw_khz", &val)) { + next.bw_khz = val; + changed = true; + } + if (parse_int_field(json, "sf", &val)) { + next.sf = val; + changed = true; + } + if (parse_int_field(json, "cr", &val)) { + next.cr = val; + changed = true; + } + + if (!changed) { + return; + } + s_params = next; + clamp_freq_to_band(); + esp_err_t err = lora_radio_apply_params(&s_params); + if (err != ESP_OK) { + ESP_LOGE(TAG, "USB set_params failed: %s", esp_err_to_name(err)); + } else { + ESP_LOGI(TAG, "USB set_params applied"); + } +} + +static void usb_handle_line(const char *line) +{ + if (!line || line[0] == '\0') { + return; + } + ESP_LOGI(TAG, "USB RX: %s", line); + if (strstr(line, "set_params")) { + usb_handle_set_params(line); + return; + } + if (strstr(line, "get_status")) { + usb_send_status(); + return; + } + if (strstr(line, "reboot_bootloader")) { + ui_show_status("Bootloader", "Rebooting to USB", "Connect USB", NULL, NULL, NULL); + vTaskDelay(pdMS_TO_TICKS(200)); + REG_SET_BIT(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT); + esp_restart(); + } +} + +static void set_band(int idx) +{ + int max_idx = (int)(sizeof(s_bands) / sizeof(s_bands[0])) - 1; + if (idx < 0) idx = 0; + if (idx > max_idx) idx = max_idx; + s_band_idx = idx; + s_params.freq_centi_mhz = s_bands[s_band_idx].default_cmhz; + clamp_freq_to_band(); + esp_err_t err = lora_radio_apply_params(&s_params); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Apply params failed: %s", esp_err_to_name(err)); + } +} + +static void bump_field(int delta) +{ + switch (s_field) { + case FIELD_FREQ: + s_params.freq_centi_mhz += delta * 100; // крок 1 MHz + clamp_freq_to_band(); + break; + case FIELD_BW: { + int idx = 0; + for (size_t i = 0; i < sizeof(s_bw_options)/sizeof(s_bw_options[0]); i++) { + if (s_bw_options[i] == s_params.bw_khz) { + idx = i; + break; + } + } + idx += delta; + if (idx < 0) idx = 0; + if (idx >= (int)(sizeof(s_bw_options)/sizeof(s_bw_options[0]))) idx = (int)(sizeof(s_bw_options)/sizeof(s_bw_options[0])) - 1; + s_params.bw_khz = s_bw_options[idx]; + break; + } + case FIELD_SF: + s_params.sf += delta; + if (s_params.sf < 5) s_params.sf = 5; + if (s_params.sf > 12) s_params.sf = 12; + break; + case FIELD_CR: + s_params.cr += delta; + if (s_params.cr < 5) s_params.cr = 5; + if (s_params.cr > 8) s_params.cr = 8; + break; + default: + break; + } + esp_err_t err = lora_radio_apply_params(&s_params); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Apply params failed: %s", esp_err_to_name(err)); + } +} + +static void next_screen(int delta) +{ + int idx = (int)s_screen + delta; + if (idx < 0) idx = SCREEN_COUNT - 1; + if (idx >= SCREEN_COUNT) idx = 0; + s_screen = (screen_t)idx; + switch (s_screen) { + case SCREEN_FREQ: s_field = FIELD_FREQ; break; + case SCREEN_BAND: s_field = FIELD_FREQ; break; + case SCREEN_BW: s_field = FIELD_BW; break; + case SCREEN_SF: s_field = FIELD_SF; break; + case SCREEN_CR: s_field = FIELD_CR; break; + default: break; + } +} + +static void adjust_current(int delta) +{ + switch (s_screen) { + case SCREEN_FREQ: s_field = FIELD_FREQ; bump_field(delta); break; + case SCREEN_BAND: set_band(s_band_idx + delta); break; + case SCREEN_BW: s_field = FIELD_BW; bump_field(delta); break; + case SCREEN_SF: s_field = FIELD_SF; bump_field(delta); break; + case SCREEN_CR: s_field = FIELD_CR; bump_field(delta); break; + case SCREEN_PAYLOAD: + case SCREEN_BOOT: + default: + break; + } +} + +static void app_loop(void) +{ + lora_metrics_t metrics = {0}; + while (true) { + input_event_t evt = input_poll(); + switch (evt) { + case INPUT_LEFT: next_screen(-1); break; + case INPUT_RIGHT: next_screen(1); break; + case INPUT_UP: adjust_current(1); break; + case INPUT_DOWN: adjust_current(-1); break; + case INPUT_CENTER: + if (s_screen == SCREEN_BOOT) { + ui_show_status("Bootloader", "Rebooting to USB", "Connect USB", NULL, NULL, NULL); + vTaskDelay(pdMS_TO_TICKS(200)); + REG_SET_BIT(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT); + esp_restart(); + } else { + bump_field(0); + } + break; + default: break; + } + + usb_api_tick(); + lora_radio_tick_rx(); + lora_radio_get_metrics(&metrics); + char line1[32]; + char line2[32]; + char line3[32]; + char line4[32]; + char line5[32]; + char line6[32]; + char payload[48]; + memset(line1, 0, sizeof(line1)); + memset(line2, 0, sizeof(line2)); + memset(line3, 0, sizeof(line3)); + memset(line4, 0, sizeof(line4)); + memset(line5, 0, sizeof(line5)); + memset(line6, 0, sizeof(line6)); + + switch (s_screen) { + case SCREEN_FREQ: + snprintf(line1, sizeof(line1), "Freq: %d MHz", s_params.freq_centi_mhz / 100); + snprintf(line2, sizeof(line2), "UP/DN to change"); + break; + case SCREEN_BAND: { + const band_t *b = &s_bands[s_band_idx]; + snprintf(line1, sizeof(line1), "Band %s", b->name); + snprintf(line2, sizeof(line2), "%d-%d MHz", b->min_cmhz / 100, b->max_cmhz / 100); + snprintf(line3, sizeof(line3), "UP/DN to select"); + break; + } + case SCREEN_BW: + snprintf(line1, sizeof(line1), "BW: %d kHz", s_params.bw_khz); + snprintf(line2, sizeof(line2), "UP/DN to change"); + break; + case SCREEN_SF: + snprintf(line1, sizeof(line1), "SF: %d", s_params.sf); + snprintf(line2, sizeof(line2), "UP/DN to change"); + break; + case SCREEN_CR: + snprintf(line1, sizeof(line1), "CR: 4/%d", s_params.cr); + snprintf(line2, sizeof(line2), "UP/DN to change"); + break; + case SCREEN_PAYLOAD: + lora_radio_get_last_payload(payload, sizeof(payload)); + snprintf(line1, sizeof(line1), "Payload:"); + snprintf(line2, sizeof(line2), "%.31s", payload); + break; + case SCREEN_BOOT: + snprintf(line1, sizeof(line1), "USB Bootloader"); + snprintf(line2, sizeof(line2), "CENTER to reboot"); + snprintf(line3, sizeof(line3), "Plug USB first"); + break; + default: + break; + } + + // Метрики залишаємо у видимих рядках (5 рядків + заголовок) + snprintf(line4, sizeof(line4), "SNR %ddB", metrics.snr_db); + snprintf(line5, sizeof(line5), "RSSI %ddBm ST 0x%02X", metrics.rssi_dbm, metrics.last_status); + + char header[32]; + snprintf(header, sizeof(header), "%s %s", s_screen_icon[s_screen], s_screen_label[s_screen]); + ui_show_role(header); + ui_show_status(line1, line2, line3, line4, line5, line6); + vTaskDelay(pdMS_TO_TICKS(500)); + } +} + +void app_main(void) +{ + common_print_boot_info(); + input_init(); + ui_init(); + ui_show_role("RX"); + bool radio_ok = lora_radio_init(false); + ui_show_status(radio_ok ? "LR1121 OK" : "LR1121 FAIL", "Listening air", NULL, NULL, NULL, NULL); + ESP_LOGI(TAG, "LR1121 init: %s", radio_ok ? "OK" : "FAIL"); + s_params.freq_centi_mhz = CONFIG_LORA_FREQ_MHZ * 100; + s_params.bw_khz = CONFIG_LORA_BW_KHZ; + s_params.sf = CONFIG_LORA_SF; + s_params.cr = CONFIG_LORA_CR; + s_params.tx_power_dbm = 14; + s_params.preamble_syms = 8; + s_params.payload_len = 0; + s_params.crc_on = true; + s_params.iq_invert = false; + s_params.header_implicit = false; + s_band_idx = detect_band_from_freq(s_params.freq_centi_mhz); + clamp_freq_to_band(); + esp_err_t err = lora_radio_apply_params(&s_params); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Initial params failed: %s", esp_err_to_name(err)); + } + usb_api_init(usb_handle_line); + app_loop(); +} diff --git a/apps/rx/sdkconfig b/apps/rx/sdkconfig new file mode 100644 index 0000000..87e0f8a --- /dev/null +++ b/apps/rx/sdkconfig @@ -0,0 +1,1715 @@ +# +# Automatically generated file. DO NOT EDIT. +# Espressif IoT Development Framework (ESP-IDF) 5.5.1 Project Configuration +# +CONFIG_SOC_ADC_SUPPORTED=y +CONFIG_SOC_UART_SUPPORTED=y +CONFIG_SOC_PCNT_SUPPORTED=y +CONFIG_SOC_PHY_SUPPORTED=y +CONFIG_SOC_WIFI_SUPPORTED=y +CONFIG_SOC_TWAI_SUPPORTED=y +CONFIG_SOC_GDMA_SUPPORTED=y +CONFIG_SOC_UHCI_SUPPORTED=y +CONFIG_SOC_AHB_GDMA_SUPPORTED=y +CONFIG_SOC_GPTIMER_SUPPORTED=y +CONFIG_SOC_LCDCAM_SUPPORTED=y +CONFIG_SOC_LCDCAM_CAM_SUPPORTED=y +CONFIG_SOC_LCDCAM_I80_LCD_SUPPORTED=y +CONFIG_SOC_LCDCAM_RGB_LCD_SUPPORTED=y +CONFIG_SOC_MCPWM_SUPPORTED=y +CONFIG_SOC_DEDICATED_GPIO_SUPPORTED=y +CONFIG_SOC_CACHE_SUPPORT_WRAP=y +CONFIG_SOC_ULP_SUPPORTED=y +CONFIG_SOC_ULP_FSM_SUPPORTED=y +CONFIG_SOC_RISCV_COPROC_SUPPORTED=y +CONFIG_SOC_BT_SUPPORTED=y +CONFIG_SOC_USB_OTG_SUPPORTED=y +CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED=y +CONFIG_SOC_CCOMP_TIMER_SUPPORTED=y +CONFIG_SOC_ASYNC_MEMCPY_SUPPORTED=y +CONFIG_SOC_SUPPORTS_SECURE_DL_MODE=y +CONFIG_SOC_EFUSE_KEY_PURPOSE_FIELD=y +CONFIG_SOC_EFUSE_SUPPORTED=y +CONFIG_SOC_SDMMC_HOST_SUPPORTED=y +CONFIG_SOC_RTC_FAST_MEM_SUPPORTED=y +CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED=y +CONFIG_SOC_RTC_MEM_SUPPORTED=y +CONFIG_SOC_PSRAM_DMA_CAPABLE=y +CONFIG_SOC_XT_WDT_SUPPORTED=y +CONFIG_SOC_I2S_SUPPORTED=y +CONFIG_SOC_RMT_SUPPORTED=y +CONFIG_SOC_SDM_SUPPORTED=y +CONFIG_SOC_GPSPI_SUPPORTED=y +CONFIG_SOC_LEDC_SUPPORTED=y +CONFIG_SOC_I2C_SUPPORTED=y +CONFIG_SOC_SYSTIMER_SUPPORTED=y +CONFIG_SOC_SUPPORT_COEXISTENCE=y +CONFIG_SOC_TEMP_SENSOR_SUPPORTED=y +CONFIG_SOC_AES_SUPPORTED=y +CONFIG_SOC_MPI_SUPPORTED=y +CONFIG_SOC_SHA_SUPPORTED=y +CONFIG_SOC_HMAC_SUPPORTED=y +CONFIG_SOC_DIG_SIGN_SUPPORTED=y +CONFIG_SOC_FLASH_ENC_SUPPORTED=y +CONFIG_SOC_SECURE_BOOT_SUPPORTED=y +CONFIG_SOC_MEMPROT_SUPPORTED=y +CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y +CONFIG_SOC_BOD_SUPPORTED=y +CONFIG_SOC_CLK_TREE_SUPPORTED=y +CONFIG_SOC_MPU_SUPPORTED=y +CONFIG_SOC_WDT_SUPPORTED=y +CONFIG_SOC_SPI_FLASH_SUPPORTED=y +CONFIG_SOC_RNG_SUPPORTED=y +CONFIG_SOC_LIGHT_SLEEP_SUPPORTED=y +CONFIG_SOC_DEEP_SLEEP_SUPPORTED=y +CONFIG_SOC_LP_PERIPH_SHARE_INTERRUPT=y +CONFIG_SOC_PM_SUPPORTED=y +CONFIG_SOC_SIMD_INSTRUCTION_SUPPORTED=y +CONFIG_SOC_XTAL_SUPPORT_40M=y +CONFIG_SOC_APPCPU_HAS_CLOCK_GATING_BUG=y +CONFIG_SOC_ADC_RTC_CTRL_SUPPORTED=y +CONFIG_SOC_ADC_DIG_CTRL_SUPPORTED=y +CONFIG_SOC_ADC_ARBITER_SUPPORTED=y +CONFIG_SOC_ADC_DIG_IIR_FILTER_SUPPORTED=y +CONFIG_SOC_ADC_MONITOR_SUPPORTED=y +CONFIG_SOC_ADC_DMA_SUPPORTED=y +CONFIG_SOC_ADC_PERIPH_NUM=2 +CONFIG_SOC_ADC_MAX_CHANNEL_NUM=10 +CONFIG_SOC_ADC_ATTEN_NUM=4 +CONFIG_SOC_ADC_DIGI_CONTROLLER_NUM=2 +CONFIG_SOC_ADC_PATT_LEN_MAX=24 +CONFIG_SOC_ADC_DIGI_MIN_BITWIDTH=12 +CONFIG_SOC_ADC_DIGI_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_DIGI_RESULT_BYTES=4 +CONFIG_SOC_ADC_DIGI_DATA_BYTES_PER_CONV=4 +CONFIG_SOC_ADC_DIGI_IIR_FILTER_NUM=2 +CONFIG_SOC_ADC_DIGI_MONITOR_NUM=2 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH=83333 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW=611 +CONFIG_SOC_ADC_RTC_MIN_BITWIDTH=12 +CONFIG_SOC_ADC_RTC_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_CALIBRATION_V1_SUPPORTED=y +CONFIG_SOC_ADC_SELF_HW_CALI_SUPPORTED=y +CONFIG_SOC_ADC_SHARED_POWER=y +CONFIG_SOC_APB_BACKUP_DMA=y +CONFIG_SOC_BROWNOUT_RESET_SUPPORTED=y +CONFIG_SOC_CACHE_WRITEBACK_SUPPORTED=y +CONFIG_SOC_CACHE_FREEZE_SUPPORTED=y +CONFIG_SOC_CACHE_ACS_INVALID_STATE_ON_PANIC=y +CONFIG_SOC_CPU_CORES_NUM=2 +CONFIG_SOC_CPU_INTR_NUM=32 +CONFIG_SOC_CPU_HAS_FPU=y +CONFIG_SOC_HP_CPU_HAS_MULTIPLE_CORES=y +CONFIG_SOC_CPU_BREAKPOINTS_NUM=2 +CONFIG_SOC_CPU_WATCHPOINTS_NUM=2 +CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=0x40 +CONFIG_SOC_SIMD_PREFERRED_DATA_ALIGNMENT=16 +CONFIG_SOC_DS_SIGNATURE_MAX_BIT_LEN=4096 +CONFIG_SOC_DS_KEY_PARAM_MD_IV_LENGTH=16 +CONFIG_SOC_DS_KEY_CHECK_MAX_WAIT_US=1100 +CONFIG_SOC_AHB_GDMA_VERSION=1 +CONFIG_SOC_GDMA_NUM_GROUPS_MAX=1 +CONFIG_SOC_GDMA_PAIRS_PER_GROUP=5 +CONFIG_SOC_GDMA_PAIRS_PER_GROUP_MAX=5 +CONFIG_SOC_AHB_GDMA_SUPPORT_PSRAM=y +CONFIG_SOC_GPIO_PORT=1 +CONFIG_SOC_GPIO_PIN_COUNT=49 +CONFIG_SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER=y +CONFIG_SOC_GPIO_FILTER_CLK_SUPPORT_APB=y +CONFIG_SOC_GPIO_SUPPORT_RTC_INDEPENDENT=y +CONFIG_SOC_GPIO_SUPPORT_FORCE_HOLD=y +CONFIG_SOC_GPIO_VALID_GPIO_MASK=0x1FFFFFFFFFFFF +CONFIG_SOC_GPIO_IN_RANGE_MAX=48 +CONFIG_SOC_GPIO_OUT_RANGE_MAX=48 +CONFIG_SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK=0x0001FFFFFC000000 +CONFIG_SOC_GPIO_CLOCKOUT_BY_IO_MUX=y +CONFIG_SOC_GPIO_CLOCKOUT_CHANNEL_NUM=3 +CONFIG_SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP=y +CONFIG_SOC_DEDIC_GPIO_OUT_CHANNELS_NUM=8 +CONFIG_SOC_DEDIC_GPIO_IN_CHANNELS_NUM=8 +CONFIG_SOC_DEDIC_GPIO_OUT_AUTO_ENABLE=y +CONFIG_SOC_I2C_NUM=2 +CONFIG_SOC_HP_I2C_NUM=2 +CONFIG_SOC_I2C_FIFO_LEN=32 +CONFIG_SOC_I2C_CMD_REG_NUM=8 +CONFIG_SOC_I2C_SUPPORT_SLAVE=y +CONFIG_SOC_I2C_SUPPORT_HW_CLR_BUS=y +CONFIG_SOC_I2C_SUPPORT_XTAL=y +CONFIG_SOC_I2C_SUPPORT_RTC=y +CONFIG_SOC_I2C_SUPPORT_10BIT_ADDR=y +CONFIG_SOC_I2C_SLAVE_SUPPORT_BROADCAST=y +CONFIG_SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS=y +CONFIG_SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE=y +CONFIG_SOC_I2S_NUM=2 +CONFIG_SOC_I2S_HW_VERSION_2=y +CONFIG_SOC_I2S_SUPPORTS_XTAL=y +CONFIG_SOC_I2S_SUPPORTS_PLL_F160M=y +CONFIG_SOC_I2S_SUPPORTS_PCM=y +CONFIG_SOC_I2S_SUPPORTS_PDM=y +CONFIG_SOC_I2S_SUPPORTS_PDM_TX=y +CONFIG_SOC_I2S_SUPPORTS_PCM2PDM=y +CONFIG_SOC_I2S_SUPPORTS_PDM_RX=y +CONFIG_SOC_I2S_SUPPORTS_PDM2PCM=y +CONFIG_SOC_I2S_PDM_MAX_TX_LINES=2 +CONFIG_SOC_I2S_PDM_MAX_RX_LINES=4 +CONFIG_SOC_I2S_SUPPORTS_TDM=y +CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y +CONFIG_SOC_LEDC_SUPPORT_XTAL_CLOCK=y +CONFIG_SOC_LEDC_TIMER_NUM=4 +CONFIG_SOC_LEDC_CHANNEL_NUM=8 +CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=14 +CONFIG_SOC_LEDC_SUPPORT_FADE_STOP=y +CONFIG_SOC_MCPWM_GROUPS=2 +CONFIG_SOC_MCPWM_TIMERS_PER_GROUP=3 +CONFIG_SOC_MCPWM_OPERATORS_PER_GROUP=3 +CONFIG_SOC_MCPWM_COMPARATORS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_GENERATORS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_TRIGGERS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_GPIO_FAULTS_PER_GROUP=3 +CONFIG_SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP=y +CONFIG_SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER=3 +CONFIG_SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP=3 +CONFIG_SOC_MCPWM_SWSYNC_CAN_PROPAGATE=y +CONFIG_SOC_MMU_LINEAR_ADDRESS_REGION_NUM=1 +CONFIG_SOC_MMU_PERIPH_NUM=1 +CONFIG_SOC_MPU_MIN_REGION_SIZE=0x20000000 +CONFIG_SOC_MPU_REGIONS_MAX_NUM=8 +CONFIG_SOC_PCNT_GROUPS=1 +CONFIG_SOC_PCNT_UNITS_PER_GROUP=4 +CONFIG_SOC_PCNT_CHANNELS_PER_UNIT=2 +CONFIG_SOC_PCNT_THRES_POINT_PER_UNIT=2 +CONFIG_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 +CONFIG_SOC_LCD_I80_SUPPORTED=y +CONFIG_SOC_LCD_RGB_SUPPORTED=y +CONFIG_SOC_LCD_I80_BUSES=1 +CONFIG_SOC_LCD_RGB_PANELS=1 +CONFIG_SOC_LCD_I80_BUS_WIDTH=16 +CONFIG_SOC_LCD_RGB_DATA_WIDTH=16 +CONFIG_SOC_LCD_SUPPORT_RGB_YUV_CONV=y +CONFIG_SOC_LCDCAM_I80_NUM_BUSES=1 +CONFIG_SOC_LCDCAM_I80_BUS_WIDTH=16 +CONFIG_SOC_LCDCAM_RGB_NUM_PANELS=1 +CONFIG_SOC_LCDCAM_RGB_DATA_WIDTH=16 +CONFIG_SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH=128 +CONFIG_SOC_RTC_CNTL_CPU_PD_REG_FILE_NUM=549 +CONFIG_SOC_RTC_CNTL_TAGMEM_PD_DMA_BUS_WIDTH=128 +CONFIG_SOC_RTCIO_PIN_COUNT=22 +CONFIG_SOC_RTCIO_INPUT_OUTPUT_SUPPORTED=y +CONFIG_SOC_RTCIO_HOLD_SUPPORTED=y +CONFIG_SOC_RTCIO_WAKE_SUPPORTED=y +CONFIG_SOC_LP_IO_CLOCK_IS_INDEPENDENT=y +CONFIG_SOC_SDM_GROUPS=1 +CONFIG_SOC_SDM_CHANNELS_PER_GROUP=8 +CONFIG_SOC_SDM_CLK_SUPPORT_APB=y +CONFIG_SOC_SPI_PERIPH_NUM=3 +CONFIG_SOC_SPI_MAX_CS_NUM=6 +CONFIG_SOC_SPI_MAXIMUM_BUFFER_SIZE=64 +CONFIG_SOC_SPI_SUPPORT_DDRCLK=y +CONFIG_SOC_SPI_SLAVE_SUPPORT_SEG_TRANS=y +CONFIG_SOC_SPI_SUPPORT_CD_SIG=y +CONFIG_SOC_SPI_SUPPORT_CONTINUOUS_TRANS=y +CONFIG_SOC_SPI_SUPPORT_SLAVE_HD_VER2=y +CONFIG_SOC_SPI_SUPPORT_CLK_APB=y +CONFIG_SOC_SPI_SUPPORT_CLK_XTAL=y +CONFIG_SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT=y +CONFIG_SOC_MEMSPI_IS_INDEPENDENT=y +CONFIG_SOC_SPI_MAX_PRE_DIVIDER=16 +CONFIG_SOC_SPI_SUPPORT_OCT=y +CONFIG_SOC_SPI_SCT_SUPPORTED=y +CONFIG_SOC_SPI_SCT_REG_NUM=14 +CONFIG_SOC_SPI_SCT_BUFFER_NUM_MAX=y +CONFIG_SOC_SPI_SCT_CONF_BITLEN_MAX=0x3FFFA +CONFIG_SOC_MEMSPI_SRC_FREQ_120M_SUPPORTED=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 +CONFIG_SOC_SPIRAM_XIP_SUPPORTED=y +CONFIG_SOC_SYSTIMER_COUNTER_NUM=2 +CONFIG_SOC_SYSTIMER_ALARM_NUM=3 +CONFIG_SOC_SYSTIMER_BIT_WIDTH_LO=32 +CONFIG_SOC_SYSTIMER_BIT_WIDTH_HI=20 +CONFIG_SOC_SYSTIMER_FIXED_DIVIDER=y +CONFIG_SOC_SYSTIMER_INT_LEVEL=y +CONFIG_SOC_SYSTIMER_ALARM_MISS_COMPENSATE=y +CONFIG_SOC_TIMER_GROUPS=2 +CONFIG_SOC_TIMER_GROUP_TIMERS_PER_GROUP=2 +CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=54 +CONFIG_SOC_TIMER_GROUP_SUPPORT_XTAL=y +CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y +CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=4 +CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32 +CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16 +CONFIG_SOC_TOUCH_SENSOR_VERSION=2 +CONFIG_SOC_TOUCH_SENSOR_NUM=15 +CONFIG_SOC_TOUCH_MIN_CHAN_ID=1 +CONFIG_SOC_TOUCH_MAX_CHAN_ID=14 +CONFIG_SOC_TOUCH_SUPPORT_BENCHMARK=y +CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y +CONFIG_SOC_TOUCH_SUPPORT_WATERPROOF=y +CONFIG_SOC_TOUCH_SUPPORT_PROX_SENSING=y +CONFIG_SOC_TOUCH_SUPPORT_DENOISE_CHAN=y +CONFIG_SOC_TOUCH_PROXIMITY_CHANNEL_NUM=3 +CONFIG_SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED=y +CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1 +CONFIG_SOC_TWAI_CONTROLLER_NUM=1 +CONFIG_SOC_TWAI_MASK_FILTER_NUM=1 +CONFIG_SOC_TWAI_CLK_SUPPORT_APB=y +CONFIG_SOC_TWAI_BRP_MIN=2 +CONFIG_SOC_TWAI_BRP_MAX=16384 +CONFIG_SOC_TWAI_SUPPORTS_RX_STATUS=y +CONFIG_SOC_UART_NUM=3 +CONFIG_SOC_UART_HP_NUM=3 +CONFIG_SOC_UART_FIFO_LEN=128 +CONFIG_SOC_UART_BITRATE_MAX=5000000 +CONFIG_SOC_UART_SUPPORT_FSM_TX_WAIT_SEND=y +CONFIG_SOC_UART_SUPPORT_WAKEUP_INT=y +CONFIG_SOC_UART_SUPPORT_APB_CLK=y +CONFIG_SOC_UART_SUPPORT_RTC_CLK=y +CONFIG_SOC_UART_SUPPORT_XTAL_CLK=y +CONFIG_SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE=y +CONFIG_SOC_UHCI_NUM=1 +CONFIG_SOC_USB_OTG_PERIPH_NUM=1 +CONFIG_SOC_SHA_DMA_MAX_BUFFER_SIZE=3968 +CONFIG_SOC_SHA_SUPPORT_DMA=y +CONFIG_SOC_SHA_SUPPORT_RESUME=y +CONFIG_SOC_SHA_GDMA=y +CONFIG_SOC_SHA_SUPPORT_SHA1=y +CONFIG_SOC_SHA_SUPPORT_SHA224=y +CONFIG_SOC_SHA_SUPPORT_SHA256=y +CONFIG_SOC_SHA_SUPPORT_SHA384=y +CONFIG_SOC_SHA_SUPPORT_SHA512=y +CONFIG_SOC_SHA_SUPPORT_SHA512_224=y +CONFIG_SOC_SHA_SUPPORT_SHA512_256=y +CONFIG_SOC_SHA_SUPPORT_SHA512_T=y +CONFIG_SOC_MPI_MEM_BLOCKS_NUM=4 +CONFIG_SOC_MPI_OPERATIONS_NUM=3 +CONFIG_SOC_RSA_MAX_BIT_LEN=4096 +CONFIG_SOC_AES_SUPPORT_DMA=y +CONFIG_SOC_AES_GDMA=y +CONFIG_SOC_AES_SUPPORT_AES_128=y +CONFIG_SOC_AES_SUPPORT_AES_256=y +CONFIG_SOC_PM_SUPPORT_EXT0_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_EXT1_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_EXT_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_WIFI_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_BT_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_CPU_PD=y +CONFIG_SOC_PM_SUPPORT_TAGMEM_PD=y +CONFIG_SOC_PM_SUPPORT_RTC_PERIPH_PD=y +CONFIG_SOC_PM_SUPPORT_RC_FAST_PD=y +CONFIG_SOC_PM_SUPPORT_VDDSDIO_PD=y +CONFIG_SOC_PM_SUPPORT_MAC_BB_PD=y +CONFIG_SOC_PM_SUPPORT_MODEM_PD=y +CONFIG_SOC_CONFIGURABLE_VDDSDIO_SUPPORTED=y +CONFIG_SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY=y +CONFIG_SOC_PM_CPU_RETENTION_BY_RTCCNTL=y +CONFIG_SOC_PM_MODEM_RETENTION_BY_BACKUPDMA=y +CONFIG_SOC_PM_MODEM_PD_BY_SW=y +CONFIG_SOC_CLK_RC_FAST_D256_SUPPORTED=y +CONFIG_SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256=y +CONFIG_SOC_CLK_RC_FAST_SUPPORT_CALIBRATION=y +CONFIG_SOC_CLK_XTAL32K_SUPPORTED=y +CONFIG_SOC_CLK_LP_FAST_SUPPORT_XTAL_D2=y +CONFIG_SOC_EFUSE_DIS_DOWNLOAD_ICACHE=y +CONFIG_SOC_EFUSE_DIS_DOWNLOAD_DCACHE=y +CONFIG_SOC_EFUSE_HARD_DIS_JTAG=y +CONFIG_SOC_EFUSE_DIS_USB_JTAG=y +CONFIG_SOC_EFUSE_SOFT_DIS_JTAG=y +CONFIG_SOC_EFUSE_DIS_DIRECT_BOOT=y +CONFIG_SOC_EFUSE_DIS_ICACHE=y +CONFIG_SOC_EFUSE_BLOCK9_KEY_PURPOSE_QUIRK=y +CONFIG_SOC_SECURE_BOOT_V2_RSA=y +CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS=3 +CONFIG_SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS=y +CONFIG_SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY=y +CONFIG_SOC_FLASH_ENCRYPTED_XTS_AES_BLOCK_MAX=64 +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES=y +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_OPTIONS=y +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_128=y +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_256=y +CONFIG_SOC_MEMPROT_CPU_PREFETCH_PAD_SIZE=16 +CONFIG_SOC_MEMPROT_MEM_ALIGN_SIZE=256 +CONFIG_SOC_PHY_DIG_REGS_MEM_SIZE=21 +CONFIG_SOC_MAC_BB_PD_MEM_SIZE=192 +CONFIG_SOC_WIFI_LIGHT_SLEEP_CLK_WIDTH=12 +CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE=y +CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND=y +CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_RESUME=y +CONFIG_SOC_SPI_MEM_SUPPORT_SW_SUSPEND=y +CONFIG_SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE=y +CONFIG_SOC_SPI_MEM_SUPPORT_TIMING_TUNING=y +CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y +CONFIG_SOC_SPI_MEM_SUPPORT_WRAP=y +CONFIG_SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY=y +CONFIG_SOC_MEMSPI_CORE_CLK_SHARED_WITH_PSRAM=y +CONFIG_SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP=y +CONFIG_SOC_COEX_HW_PTI=y +CONFIG_SOC_EXTERNAL_COEX_LEADER_TX_LINE=y +CONFIG_SOC_SDMMC_USE_GPIO_MATRIX=y +CONFIG_SOC_SDMMC_NUM_SLOTS=2 +CONFIG_SOC_SDMMC_SUPPORT_XTAL_CLOCK=y +CONFIG_SOC_SDMMC_DELAY_PHASE_NUM=4 +CONFIG_SOC_TEMPERATURE_SENSOR_SUPPORT_FAST_RC=y +CONFIG_SOC_WIFI_HW_TSF=y +CONFIG_SOC_WIFI_FTM_SUPPORT=y +CONFIG_SOC_WIFI_GCMP_SUPPORT=y +CONFIG_SOC_WIFI_WAPI_SUPPORT=y +CONFIG_SOC_WIFI_CSI_SUPPORT=y +CONFIG_SOC_WIFI_MESH_SUPPORT=y +CONFIG_SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW=y +CONFIG_SOC_WIFI_PHY_NEEDS_USB_WORKAROUND=y +CONFIG_SOC_BLE_SUPPORTED=y +CONFIG_SOC_BLE_MESH_SUPPORTED=y +CONFIG_SOC_BLE_50_SUPPORTED=y +CONFIG_SOC_BLE_DEVICE_PRIVACY_SUPPORTED=y +CONFIG_SOC_BLUFI_SUPPORTED=y +CONFIG_SOC_ULP_HAS_ADC=y +CONFIG_SOC_PHY_COMBO_MODULE=y +CONFIG_SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV=y +CONFIG_SOC_LCDCAM_CAM_PERIPH_NUM=1 +CONFIG_SOC_LCDCAM_CAM_DATA_WIDTH_MAX=16 +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TOOLCHAIN="gcc" +CONFIG_IDF_TOOLCHAIN_GCC=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET_ARCH="xtensa" +CONFIG_IDF_TARGET="esp32s3" +CONFIG_IDF_INIT_VERSION="5.5.1" +CONFIG_IDF_TARGET_ESP32S3=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009 + +# +# Build type +# +CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y +# CONFIG_APP_BUILD_TYPE_RAM is not set +CONFIG_APP_BUILD_GENERATE_BINARIES=y +CONFIG_APP_BUILD_BOOTLOADER=y +CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y +# CONFIG_APP_REPRODUCIBLE_BUILD is not set +# CONFIG_APP_NO_BLOBS is not set +# end of Build type + +# +# Bootloader config +# + +# +# Bootloader manager +# +CONFIG_BOOTLOADER_COMPILE_TIME_DATE=y +CONFIG_BOOTLOADER_PROJECT_VER=1 +# end of Bootloader manager + +# +# Application Rollback +# +# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set +# end of Application Rollback + +# +# Recovery Bootloader and Rollback +# +# end of Recovery Bootloader and Rollback + +CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x0 +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set + +# +# Log +# +CONFIG_BOOTLOADER_LOG_VERSION_1=y +CONFIG_BOOTLOADER_LOG_VERSION=1 +# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set +CONFIG_BOOTLOADER_LOG_LEVEL=3 + +# +# Format +# +# CONFIG_BOOTLOADER_LOG_COLORS is not set +CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y +# end of Format + +# +# Settings +# +CONFIG_BOOTLOADER_LOG_MODE_TEXT_EN=y +CONFIG_BOOTLOADER_LOG_MODE_TEXT=y +# end of Settings +# end of Log + +# +# Serial Flash Configurations +# +# CONFIG_BOOTLOADER_FLASH_DC_AWARE is not set +CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=y +# end of Serial Flash Configurations + +CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y +# CONFIG_BOOTLOADER_FACTORY_RESET is not set +# CONFIG_BOOTLOADER_APP_TEST is not set +CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y +CONFIG_BOOTLOADER_WDT_ENABLE=y +# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set +CONFIG_BOOTLOADER_WDT_TIME_MS=9000 +# CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set +CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0 +# CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set +# end of Bootloader config + +# +# Security features +# +CONFIG_SECURE_BOOT_V2_RSA_SUPPORTED=y +CONFIG_SECURE_BOOT_V2_PREFERRED=y +# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set +# CONFIG_SECURE_BOOT is not set +# CONFIG_SECURE_FLASH_ENC_ENABLED is not set +CONFIG_SECURE_ROM_DL_MODE_ENABLED=y +# end of Security features + +# +# Application manager +# +CONFIG_APP_COMPILE_TIME_DATE=y +# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set +# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set +# CONFIG_APP_PROJECT_VER_FROM_CONFIG is not set +CONFIG_APP_RETRIEVE_LEN_ELF_SHA=9 +# end of Application manager + +CONFIG_ESP_ROM_HAS_CRC_LE=y +CONFIG_ESP_ROM_HAS_CRC_BE=y +CONFIG_ESP_ROM_HAS_MZ_CRC32=y +CONFIG_ESP_ROM_HAS_JPEG_DECODE=y +CONFIG_ESP_ROM_UART_CLK_IS_XTAL=y +CONFIG_ESP_ROM_HAS_RETARGETABLE_LOCKING=y +CONFIG_ESP_ROM_USB_OTG_NUM=3 +CONFIG_ESP_ROM_USB_SERIAL_DEVICE_NUM=4 +CONFIG_ESP_ROM_HAS_ERASE_0_REGION_BUG=y +CONFIG_ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV=y +CONFIG_ESP_ROM_GET_CLK_FREQ=y +CONFIG_ESP_ROM_HAS_HAL_WDT=y +CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y +CONFIG_ESP_ROM_HAS_LAYOUT_TABLE=y +CONFIG_ESP_ROM_HAS_SPI_FLASH=y +CONFIG_ESP_ROM_HAS_SPI_FLASH_MMAP=y +CONFIG_ESP_ROM_HAS_ETS_PRINTF_BUG=y +CONFIG_ESP_ROM_HAS_NEWLIB=y +CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y +CONFIG_ESP_ROM_HAS_NEWLIB_32BIT_TIME=y +CONFIG_ESP_ROM_NEEDS_SET_CACHE_MMU_SIZE=y +CONFIG_ESP_ROM_RAM_APP_NEEDS_MMU_INIT=y +CONFIG_ESP_ROM_HAS_FLASH_COUNT_PAGES_BUG=y +CONFIG_ESP_ROM_HAS_CACHE_SUSPEND_WAITI_BUG=y +CONFIG_ESP_ROM_HAS_CACHE_WRITEBACK_BUG=y +CONFIG_ESP_ROM_HAS_SW_FLOAT=y +CONFIG_ESP_ROM_HAS_VERSION=y +CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y +CONFIG_ESP_ROM_HAS_OUTPUT_PUTC_FUNC=y +CONFIG_ESP_ROM_CONSOLE_OUTPUT_SECONDARY=y + +# +# Boot ROM Behavior +# +CONFIG_BOOT_ROM_LOG_ALWAYS_ON=y +# CONFIG_BOOT_ROM_LOG_ALWAYS_OFF is not set +# CONFIG_BOOT_ROM_LOG_ON_GPIO_HIGH is not set +# CONFIG_BOOT_ROM_LOG_ON_GPIO_LOW is not set +# end of Boot ROM Behavior + +# +# 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_FLASHMODE_QIO is not set +# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE="dio" +# CONFIG_ESPTOOLPY_FLASHFREQ_120M is not set +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +# CONFIG_ESPTOOLPY_FLASHFREQ_40M is not set +# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE="2MB" +# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set +CONFIG_ESPTOOLPY_BEFORE_RESET=y +# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +# CONFIG_ESPTOOLPY_AFTER_NORESET is not set +CONFIG_ESPTOOLPY_AFTER="hard_reset" +CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 +# end of Serial flasher config + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_SINGLE_APP=y +# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set +# CONFIG_PARTITION_TABLE_TWO_OTA is not set +# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set +# CONFIG_PARTITION_TABLE_CUSTOM is not set +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# Compiler options +# +CONFIG_COMPILER_OPTIMIZATION_DEBUG=y +# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set +# CONFIG_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_COMPILER_OPTIMIZATION_NONE is not set +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set +CONFIG_COMPILER_ASSERT_NDEBUG_EVALUATE=y +CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set +CONFIG_COMPILER_HIDE_PATHS_MACROS=y +# CONFIG_COMPILER_CXX_EXCEPTIONS is not set +# CONFIG_COMPILER_CXX_RTTI is not set +CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y +# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set +# CONFIG_COMPILER_NO_MERGE_CONSTANTS is not set +# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set +CONFIG_COMPILER_DISABLE_DEFAULT_ERRORS=y +# CONFIG_COMPILER_DISABLE_GCC12_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC13_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC14_WARNINGS is not set +# CONFIG_COMPILER_DUMP_RTL_FILES is not set +CONFIG_COMPILER_RT_LIB_GCCLIB=y +CONFIG_COMPILER_RT_LIB_NAME="gcc" +CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=y +# CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE is not set +# CONFIG_COMPILER_STATIC_ANALYZER is not set +# end of Compiler options + +# +# Component config +# + +# +# !!! MINIMAL_BUILD is enabled !!! +# + +# +# Only common components and those transitively required by the main component are listed +# + +# +# If a component configuration is missing, please add it to the main component's requirements +# + +# +# Driver Configurations +# + +# +# Legacy TWAI Driver Configurations +# +# CONFIG_TWAI_SKIP_LEGACY_CONFLICT_CHECK is not set +CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y +# end of Legacy TWAI Driver Configurations + +# +# Legacy ADC Driver Configuration +# +# CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_ADC_SKIP_LEGACY_CONFLICT_CHECK is not set + +# +# Legacy ADC Calibration Configuration +# +# CONFIG_ADC_CALI_SUPPRESS_DEPRECATE_WARN is not set +# end of Legacy ADC Calibration Configuration +# end of Legacy ADC Driver Configuration + +# +# Legacy MCPWM Driver Configurations +# +# CONFIG_MCPWM_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_MCPWM_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy MCPWM Driver Configurations + +# +# Legacy Timer Group Driver Configurations +# +# CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_GPTIMER_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Timer Group Driver Configurations + +# +# Legacy RMT Driver Configurations +# +# CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_RMT_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy RMT Driver Configurations + +# +# Legacy I2S Driver Configurations +# +# CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy I2S Driver Configurations + +# +# Legacy I2C Driver Configurations +# +# CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy I2C Driver Configurations + +# +# Legacy PCNT Driver Configurations +# +# CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_PCNT_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy PCNT Driver Configurations + +# +# Legacy SDM Driver Configurations +# +# CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_SDM_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy SDM Driver Configurations + +# +# Legacy Temperature Sensor Driver Configurations +# +# CONFIG_TEMP_SENSOR_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_TEMP_SENSOR_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Temperature Sensor Driver Configurations + +# +# Legacy Touch Sensor Driver Configurations +# +# CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_TOUCH_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Touch Sensor Driver Configurations +# end of Driver Configurations + +# +# eFuse Bit Manager +# +# CONFIG_EFUSE_CUSTOM_TABLE is not set +# CONFIG_EFUSE_VIRTUAL is not set +CONFIG_EFUSE_MAX_BLK_LEN=256 +# end of eFuse Bit Manager + +# +# ADC and ADC Calibration +# +# CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM is not set +# CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE is not set +# CONFIG_ADC_CONTINUOUS_FORCE_USE_ADC2_ON_C3_S3 is not set +# CONFIG_ADC_ENABLE_DEBUG_LOG is not set +# end of ADC and ADC Calibration + +# +# Common ESP-related +# +CONFIG_ESP_ERR_TO_NAME_LOOKUP=y +# end of Common ESP-related + +# +# ESP-Driver:GPIO Configurations +# +# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:GPIO Configurations + +# +# ESP-Driver:GPTimer Configurations +# +CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y +# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set +# CONFIG_GPTIMER_ISR_CACHE_SAFE is not set +CONFIG_GPTIMER_OBJ_CACHE_SAFE=y +# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:GPTimer Configurations + +# +# ESP-Driver:I2C Configurations +# +# CONFIG_I2C_ISR_IRAM_SAFE is not set +# CONFIG_I2C_ENABLE_DEBUG_LOG is not set +# CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2 is not set +CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y +# end of ESP-Driver:I2C Configurations + +# +# ESP-Driver:I2S Configurations +# +# CONFIG_I2S_ISR_IRAM_SAFE is not set +# CONFIG_I2S_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:I2S Configurations + +# +# ESP-Driver:LEDC Configurations +# +# CONFIG_LEDC_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:LEDC Configurations + +# +# ESP-Driver:MCPWM Configurations +# +CONFIG_MCPWM_ISR_HANDLER_IN_IRAM=y +# CONFIG_MCPWM_ISR_CACHE_SAFE is not set +# CONFIG_MCPWM_CTRL_FUNC_IN_IRAM is not set +CONFIG_MCPWM_OBJ_CACHE_SAFE=y +# CONFIG_MCPWM_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:MCPWM Configurations + +# +# ESP-Driver:PCNT Configurations +# +# CONFIG_PCNT_CTRL_FUNC_IN_IRAM is not set +# CONFIG_PCNT_ISR_IRAM_SAFE is not set +# CONFIG_PCNT_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:PCNT Configurations + +# +# ESP-Driver:RMT Configurations +# +CONFIG_RMT_ENCODER_FUNC_IN_IRAM=y +CONFIG_RMT_TX_ISR_HANDLER_IN_IRAM=y +CONFIG_RMT_RX_ISR_HANDLER_IN_IRAM=y +# CONFIG_RMT_RECV_FUNC_IN_IRAM is not set +# CONFIG_RMT_TX_ISR_CACHE_SAFE is not set +# CONFIG_RMT_RX_ISR_CACHE_SAFE is not set +CONFIG_RMT_OBJ_CACHE_SAFE=y +# CONFIG_RMT_ENABLE_DEBUG_LOG is not set +# CONFIG_RMT_ISR_IRAM_SAFE is not set +# end of ESP-Driver:RMT Configurations + +# +# ESP-Driver:Sigma Delta Modulator Configurations +# +# CONFIG_SDM_CTRL_FUNC_IN_IRAM is not set +# CONFIG_SDM_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:Sigma Delta Modulator Configurations + +# +# ESP-Driver:SPI Configurations +# +# CONFIG_SPI_MASTER_IN_IRAM is not set +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +# CONFIG_SPI_SLAVE_IN_IRAM is not set +CONFIG_SPI_SLAVE_ISR_IN_IRAM=y +# end of ESP-Driver:SPI Configurations + +# +# ESP-Driver:Temperature Sensor Configurations +# +# CONFIG_TEMP_SENSOR_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:Temperature Sensor Configurations + +# +# ESP-Driver:TWAI Configurations +# +# CONFIG_TWAI_ISR_IN_IRAM is not set +# CONFIG_TWAI_ISR_CACHE_SAFE is not set +# CONFIG_TWAI_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:TWAI Configurations + +# +# ESP-Driver:UART Configurations +# +# CONFIG_UART_ISR_IN_IRAM is not set +# end of ESP-Driver:UART Configurations + +# +# ESP-Driver:UHCI Configurations +# +# CONFIG_UHCI_ISR_HANDLER_IN_IRAM is not set +# CONFIG_UHCI_ISR_CACHE_SAFE is not set +# CONFIG_UHCI_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:UHCI Configurations + +# +# ESP-Driver:USB Serial/JTAG Configuration +# +CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=y +# end of ESP-Driver:USB Serial/JTAG Configuration + +# +# Hardware Settings +# + +# +# Chip revision +# +CONFIG_ESP32S3_REV_MIN_0=y +# CONFIG_ESP32S3_REV_MIN_1 is not set +# CONFIG_ESP32S3_REV_MIN_2 is not set +CONFIG_ESP32S3_REV_MIN_FULL=0 +CONFIG_ESP_REV_MIN_FULL=0 + +# +# Maximum Supported ESP32-S3 Revision (Rev v0.99) +# +CONFIG_ESP32S3_REV_MAX_FULL=99 +CONFIG_ESP_REV_MAX_FULL=99 +CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL=0 +CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL=199 + +# +# Maximum Supported ESP32-S3 eFuse Block Revision (eFuse Block Rev v1.99) +# +# end of Chip revision + +# +# 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_ESP32S3_UNIVERSAL_MAC_ADDRESSES_TWO is not set +CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES_FOUR=y +CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES=4 +# CONFIG_ESP_MAC_USE_CUSTOM_MAC_AS_BASE_MAC is not set +# end of MAC Config + +# +# Sleep Config +# +# CONFIG_ESP_SLEEP_POWER_DOWN_FLASH is not set +CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND=y +CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU=y +CONFIG_ESP_SLEEP_RTC_BUS_ISO_WORKAROUND=y +CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND=y +CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY=2000 +# CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION is not set +# CONFIG_ESP_SLEEP_DEBUG is not set +CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS=y +# end of Sleep Config + +# +# RTC Clock Config +# +CONFIG_RTC_CLK_SRC_INT_RC=y +# CONFIG_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_RTC_CLK_SRC_INT_8MD256 is not set +CONFIG_RTC_CLK_CAL_CYCLES=1024 +# end of RTC Clock Config + +# +# Peripheral Control +# +CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=y +CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=y +# end of Peripheral Control + +# +# GDMA Configurations +# +CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y +CONFIG_GDMA_ISR_HANDLER_IN_IRAM=y +CONFIG_GDMA_OBJ_DRAM_SAFE=y +# CONFIG_GDMA_ENABLE_DEBUG_LOG is not set +# CONFIG_GDMA_ISR_IRAM_SAFE is not set +# end of GDMA Configurations + +# +# Main XTAL Config +# +CONFIG_XTAL_FREQ_40=y +CONFIG_XTAL_FREQ=40 +# end of Main XTAL Config + +# +# Power Supplier +# + +# +# Brownout Detector +# +CONFIG_ESP_BROWNOUT_DET=y +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7=y +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_1 is not set +CONFIG_ESP_BROWNOUT_DET_LVL=7 +CONFIG_ESP_BROWNOUT_USE_INTR=y +# end of Brownout Detector +# end of Power Supplier + +CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y +CONFIG_ESP_INTR_IN_IRAM=y +# end of Hardware Settings + +# +# ESP-MM: Memory Management Configurations +# +# CONFIG_ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS is not set +# end of ESP-MM: Memory Management Configurations + +# +# Partition API Configuration +# +# end of Partition API Configuration + +# +# Power Management +# +CONFIG_PM_SLEEP_FUNC_IN_IRAM=y +# CONFIG_PM_ENABLE is not set +CONFIG_PM_SLP_IRAM_OPT=y +CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y +CONFIG_PM_RESTORE_CACHE_TAGMEM_AFTER_LIGHT_SLEEP=y +# end of Power Management + +# +# ESP Ringbuf +# +# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set +# end of ESP Ringbuf + +# +# ESP-ROM +# +CONFIG_ESP_ROM_PRINT_IN_IRAM=y +# end of ESP-ROM + +# +# ESP Security Specific +# +# end of ESP Security Specific + +# +# ESP System Settings +# +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160 + +# +# Cache config +# +CONFIG_ESP32S3_INSTRUCTION_CACHE_16KB=y +# CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB is not set +CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE=0x4000 +# CONFIG_ESP32S3_INSTRUCTION_CACHE_4WAYS is not set +CONFIG_ESP32S3_INSTRUCTION_CACHE_8WAYS=y +CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS=8 +# CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_16B is not set +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32 +# CONFIG_ESP32S3_DATA_CACHE_16KB is not set +CONFIG_ESP32S3_DATA_CACHE_32KB=y +# CONFIG_ESP32S3_DATA_CACHE_64KB is not set +CONFIG_ESP32S3_DATA_CACHE_SIZE=0x8000 +# CONFIG_ESP32S3_DATA_CACHE_4WAYS is not set +CONFIG_ESP32S3_DATA_CACHE_8WAYS=y +CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8 +# CONFIG_ESP32S3_DATA_CACHE_LINE_16B is not set +CONFIG_ESP32S3_DATA_CACHE_LINE_32B=y +# CONFIG_ESP32S3_DATA_CACHE_LINE_64B is not set +CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=32 +# end of Cache config + +# +# Memory +# +# CONFIG_ESP32S3_RTCDATA_IN_FAST_MEM is not set +# CONFIG_ESP32S3_USE_FIXED_STATIC_RAM_SIZE is not set +# end of Memory + +# +# Trace memory +# +# CONFIG_ESP32S3_TRAX is not set +CONFIG_ESP32S3_TRACEMEM_RESERVE_DRAM=0x0 +# end of Trace memory + +CONFIG_ESP_SYSTEM_IN_IRAM=y +# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set +CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y +# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set +CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS=0 +CONFIG_ESP_SYSTEM_RTC_FAST_MEM_AS_HEAP_DEPCHECK=y +CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=y + +# +# Memory protection +# +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=y +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y +# end of Memory protection + +CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584 +CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y +# CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set +# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set +CONFIG_ESP_MAIN_TASK_AFFINITY=0x0 +CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 +# CONFIG_ESP_CONSOLE_UART_DEFAULT is not set +# CONFIG_ESP_CONSOLE_USB_CDC is not set +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y +# CONFIG_ESP_CONSOLE_UART_CUSTOM is not set +# CONFIG_ESP_CONSOLE_NONE is not set +CONFIG_ESP_CONSOLE_SECONDARY_NONE=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED=y +CONFIG_ESP_CONSOLE_UART_NUM=-1 +CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=4 +CONFIG_ESP_INT_WDT=y +CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 +CONFIG_ESP_INT_WDT_CHECK_CPU1=y +CONFIG_ESP_TASK_WDT_EN=y +CONFIG_ESP_TASK_WDT_INIT=y +# CONFIG_ESP_TASK_WDT_PANIC is not set +CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP_PANIC_HANDLER_IRAM is not set +# CONFIG_ESP_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP_DEBUG_OCDAWARE=y +CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4=y +CONFIG_ESP_SYSTEM_BBPLL_RECALIB=y +# end of ESP System Settings + +# +# IPC (Inter-Processor Call) +# +CONFIG_ESP_IPC_ENABLE=y +CONFIG_ESP_IPC_TASK_STACK_SIZE=1280 +CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y +CONFIG_ESP_IPC_ISR_ENABLE=y +# end of IPC (Inter-Processor Call) + +# +# ESP Timer (High Resolution Timer) +# +CONFIG_ESP_TIMER_IN_IRAM=y +# CONFIG_ESP_TIMER_PROFILING is not set +CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y +CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y +CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 +CONFIG_ESP_TIMER_INTERRUPT_LEVEL=1 +# CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL is not set +CONFIG_ESP_TIMER_TASK_AFFINITY=0x0 +CONFIG_ESP_TIMER_TASK_AFFINITY_CPU0=y +CONFIG_ESP_TIMER_ISR_AFFINITY_CPU0=y +# CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD is not set +CONFIG_ESP_TIMER_IMPL_SYSTIMER=y +# end of ESP Timer (High Resolution Timer) + +# +# FreeRTOS +# + +# +# Kernel +# +# CONFIG_FREERTOS_SMP is not set +# CONFIG_FREERTOS_UNICORE is not set +CONFIG_FREERTOS_HZ=100 +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y +CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 +# CONFIG_FREERTOS_USE_IDLE_HOOK is not set +# CONFIG_FREERTOS_USE_TICK_HOOK is not set +CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 +# CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY is not set +CONFIG_FREERTOS_USE_TIMERS=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_NAME="Tmr Svc" +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0 is not set +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU1 is not set +CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_CORE_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 +CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 +CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 +# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set +# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set +# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set +# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set +# end of Kernel + +# +# Port +# +CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y +# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set +CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y +# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set +# CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set +CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y +CONFIG_FREERTOS_ISR_STACKSIZE=1536 +CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y +# CONFIG_FREERTOS_FPU_IN_ISR is not set +CONFIG_FREERTOS_TICK_SUPPORT_SYSTIMER=y +CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y +# CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set +CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y +# CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set +# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set +# end of Port + +# +# Extra +# +# end of Extra + +CONFIG_FREERTOS_PORT=y +CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y +CONFIG_FREERTOS_DEBUG_OCDAWARE=y +CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y +CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y +CONFIG_FREERTOS_NUMBER_OF_CORES=2 +CONFIG_FREERTOS_IN_IRAM=y +# end of FreeRTOS + +# +# Hardware Abstraction Layer (HAL) and Low Level (LL) +# +CONFIG_HAL_ASSERTION_EQUALS_SYSTEM=y +# CONFIG_HAL_ASSERTION_DISABLE is not set +# CONFIG_HAL_ASSERTION_SILENT is not set +# CONFIG_HAL_ASSERTION_ENABLE is not set +CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2 +CONFIG_HAL_WDT_USE_ROM_IMPL=y +# end of Hardware Abstraction Layer (HAL) and Low Level (LL) + +# +# Heap memory debugging +# +CONFIG_HEAP_POISONING_DISABLED=y +# CONFIG_HEAP_POISONING_LIGHT is not set +# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set +CONFIG_HEAP_TRACING_OFF=y +# CONFIG_HEAP_TRACING_STANDALONE is not set +# CONFIG_HEAP_TRACING_TOHOST is not set +# CONFIG_HEAP_USE_HOOKS is not set +# CONFIG_HEAP_TASK_TRACKING is not set +# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set +# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set +# end of Heap memory debugging + +# +# Log +# +CONFIG_LOG_VERSION_1=y +# CONFIG_LOG_VERSION_2 is not set +CONFIG_LOG_VERSION=1 + +# +# Log Level +# +# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set +# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set +# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set +# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y +# CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set +# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set +CONFIG_LOG_MAXIMUM_LEVEL=3 + +# +# Level Settings +# +# CONFIG_LOG_MASTER_LEVEL is not set +CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y +# CONFIG_LOG_TAG_LEVEL_IMPL_NONE is not set +# CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST is not set +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST=y +# CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY is not set +CONFIG_LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP=y +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31 +# end of Level Settings +# end of Log Level + +# +# Format +# +# CONFIG_LOG_COLORS is not set +CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y +# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set +# end of Format + +# +# Settings +# +CONFIG_LOG_MODE_TEXT_EN=y +CONFIG_LOG_MODE_TEXT=y +# end of Settings + +CONFIG_LOG_IN_IRAM=y +# end of Log + +# +# mbedTLS +# +CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y +# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set +# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set +CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +# CONFIG_MBEDTLS_DYNAMIC_BUFFER is not set +# CONFIG_MBEDTLS_DEBUG is not set + +# +# mbedTLS v3.x related +# +# CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 is not set +# CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH is not set +# CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set +# CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set +CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y +# CONFIG_MBEDTLS_SSL_KEYING_MATERIAL_EXPORT is not set +CONFIG_MBEDTLS_PKCS7_C=y +# end of mbedTLS v3.x related + +# +# Certificate Bundle +# +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set +# CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEPRECATED_LIST is not set +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS=200 +# end of Certificate Bundle + +# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set +# CONFIG_MBEDTLS_CMAC_C is not set +CONFIG_MBEDTLS_HARDWARE_AES=y +CONFIG_MBEDTLS_AES_USE_INTERRUPT=y +CONFIG_MBEDTLS_AES_INTERRUPT_LEVEL=0 +CONFIG_MBEDTLS_GCM_SUPPORT_NON_AES_CIPHER=y +CONFIG_MBEDTLS_HARDWARE_MPI=y +# CONFIG_MBEDTLS_LARGE_KEY_SOFTWARE_MPI is not set +CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y +CONFIG_MBEDTLS_MPI_INTERRUPT_LEVEL=0 +CONFIG_MBEDTLS_HARDWARE_SHA=y +CONFIG_MBEDTLS_ROM_MD5=y +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY is not set +CONFIG_MBEDTLS_HAVE_TIME=y +# CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set +# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set +CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y +CONFIG_MBEDTLS_SHA1_C=y +CONFIG_MBEDTLS_SHA512_C=y +# CONFIG_MBEDTLS_SHA3_C is not set +CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y +# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set +# CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set +# CONFIG_MBEDTLS_TLS_DISABLED is not set +CONFIG_MBEDTLS_TLS_SERVER=y +CONFIG_MBEDTLS_TLS_CLIENT=y +CONFIG_MBEDTLS_TLS_ENABLED=y + +# +# TLS Key Exchange Methods +# +# CONFIG_MBEDTLS_PSK_MODES is not set +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y +# end of TLS Key Exchange Methods + +CONFIG_MBEDTLS_SSL_RENEGOTIATION=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +# CONFIG_MBEDTLS_SSL_PROTO_GMTSSL1_1 is not set +# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set +CONFIG_MBEDTLS_SSL_ALPN=y +CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y +CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y + +# +# Symmetric Ciphers +# +CONFIG_MBEDTLS_AES_C=y +# CONFIG_MBEDTLS_CAMELLIA_C is not set +# CONFIG_MBEDTLS_DES_C is not set +# CONFIG_MBEDTLS_BLOWFISH_C is not set +# CONFIG_MBEDTLS_XTEA_C is not set +CONFIG_MBEDTLS_CCM_C=y +CONFIG_MBEDTLS_GCM_C=y +# CONFIG_MBEDTLS_NIST_KW_C is not set +# end of Symmetric Ciphers + +# CONFIG_MBEDTLS_RIPEMD160_C is not set + +# +# Certificates +# +CONFIG_MBEDTLS_PEM_PARSE_C=y +CONFIG_MBEDTLS_PEM_WRITE_C=y +CONFIG_MBEDTLS_X509_CRL_PARSE_C=y +CONFIG_MBEDTLS_X509_CSR_PARSE_C=y +# end of Certificates + +CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_PK_PARSE_EC_EXTENDED=y +CONFIG_MBEDTLS_PK_PARSE_EC_COMPRESSED=y +# CONFIG_MBEDTLS_DHM_C is not set +CONFIG_MBEDTLS_ECDH_C=y +CONFIG_MBEDTLS_ECDSA_C=y +# CONFIG_MBEDTLS_ECJPAKE_C is not set +CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y +CONFIG_MBEDTLS_ECP_NIST_OPTIM=y +# CONFIG_MBEDTLS_ECP_FIXED_POINT_OPTIM is not set +# CONFIG_MBEDTLS_POLY1305_C is not set +# CONFIG_MBEDTLS_CHACHA20_C is not set +# CONFIG_MBEDTLS_HKDF_C is not set +# CONFIG_MBEDTLS_THREADING_C is not set +CONFIG_MBEDTLS_ERROR_STRINGS=y +# CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION is not set +# end of mbedTLS + +# +# LibC +# +CONFIG_LIBC_NEWLIB=y +CONFIG_LIBC_MISC_IN_IRAM=y +CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=y +# CONFIG_LIBC_NEWLIB_NANO_FORMAT is not set +CONFIG_LIBC_TIME_SYSCALL_USE_RTC_HRT=y +# CONFIG_LIBC_TIME_SYSCALL_USE_RTC is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_HRT is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_NONE is not set +# end of LibC + +# +# PThreads +# +CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_PTHREAD_STACK_MIN=768 +CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY=y +# CONFIG_PTHREAD_DEFAULT_CORE_0 is not set +# CONFIG_PTHREAD_DEFAULT_CORE_1 is not set +CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" +# end of PThreads + +# +# MMU Config +# +CONFIG_MMU_PAGE_SIZE_64KB=y +CONFIG_MMU_PAGE_MODE="64KB" +CONFIG_MMU_PAGE_SIZE=0x10000 +# end of MMU Config + +# +# Main Flash configuration +# + +# +# SPI Flash behavior when brownout +# +CONFIG_SPI_FLASH_BROWNOUT_RESET_XMC=y +CONFIG_SPI_FLASH_BROWNOUT_RESET=y +# end of SPI Flash behavior when brownout + +# +# Optional and Experimental Features (READ DOCS FIRST) +# + +# +# Features here require specific hardware (READ DOCS FIRST!) +# +# CONFIG_SPI_FLASH_HPM_ENA is not set +CONFIG_SPI_FLASH_HPM_AUTO=y +# CONFIG_SPI_FLASH_HPM_DIS is not set +CONFIG_SPI_FLASH_HPM_ON=y +CONFIG_SPI_FLASH_HPM_DC_AUTO=y +# CONFIG_SPI_FLASH_HPM_DC_DISABLE is not set +# CONFIG_SPI_FLASH_AUTO_SUSPEND is not set +CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50 +# CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set +# CONFIG_SPI_FLASH_FORCE_ENABLE_C6_H2_SUSPEND is not set +CONFIG_SPI_FLASH_PLACE_FUNCTIONS_IN_IRAM=y +# end of Optional and Experimental Features (READ DOCS FIRST) +# end of Main Flash configuration + +# +# SPI Flash driver +# +# CONFIG_SPI_FLASH_VERIFY_WRITE is not set +# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y +# CONFIG_SPI_FLASH_ROM_IMPL is not set +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set +# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set +CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y +CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 +CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 +CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192 +# CONFIG_SPI_FLASH_SIZE_OVERRIDE is not set +# CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED is not set +# CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST is not set + +# +# Auto-detect flash chips +# +CONFIG_SPI_FLASH_VENDOR_XMC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_GD_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_ISSI_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_MXIC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_WINBOND_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_BOYA_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_TH_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_TH_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP=y +# end of Auto-detect flash chips + +CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=y +# end of SPI Flash driver + +# +# LoRa common options +# +# CONFIG_LORA_ROLE_TX is not set +CONFIG_LORA_ROLE_RX=y +# end of LoRa common options + +# +# Joystick input +# +CONFIG_JOYSTICK_ADC_CHANNEL=5 +CONFIG_JOYSTICK_ADC_LEVEL_LEFT=2630 +CONFIG_JOYSTICK_ADC_LEVEL_UP=1230 +CONFIG_JOYSTICK_ADC_LEVEL_PRESS=0 +CONFIG_JOYSTICK_ADC_LEVEL_DOWN=1970 +CONFIG_JOYSTICK_ADC_LEVEL_RIGHT=680 +CONFIG_JOYSTICK_ADC_TOLERANCE=150 +# end of Joystick input + +# +# LoRa radio (LR1121) +# +CONFIG_LORA_SPI_HOST=2 +CONFIG_LORA_PIN_CS=12 +CONFIG_LORA_PIN_MOSI=10 +CONFIG_LORA_PIN_MISO=9 +CONFIG_LORA_PIN_SCK=11 +CONFIG_LORA_PIN_RST=5 +CONFIG_LORA_PIN_BUSY=13 +CONFIG_LORA_PIN_DIO1=4 +CONFIG_LORA_FREQ_MHZ=433 +CONFIG_LORA_BW_KHZ=125 +CONFIG_LORA_SF=7 +CONFIG_LORA_CR=5 +# end of LoRa radio (LR1121) + +# +# Display +# +CONFIG_DISPLAY_DRIVER="SSD1306 I2C OLED" +CONFIG_DISPLAY_I2C_PORT=0 +CONFIG_DISPLAY_I2C_ADDR=60 +CONFIG_DISPLAY_PIN_SDA=7 +CONFIG_DISPLAY_PIN_RST=-1 +CONFIG_DISPLAY_PIN_SCL=8 +CONFIG_DISPLAY_WIDTH=128 +CONFIG_DISPLAY_HEIGHT=64 +# end of Display +# end of Component config + +# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set + +# Deprecated options for backward compatibility +# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set +# CONFIG_NO_BLOBS is not set +# CONFIG_APP_ROLLBACK_ENABLE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set +CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y +# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set +CONFIG_LOG_BOOTLOADER_LEVEL=3 +# CONFIG_FLASH_ENCRYPTION_ENABLED is not set +# CONFIG_FLASHMODE_QIO is not set +# CONFIG_FLASHMODE_QOUT is not set +CONFIG_FLASHMODE_DIO=y +# CONFIG_FLASHMODE_DOUT is not set +CONFIG_MONITOR_BAUD=115200 +CONFIG_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y +# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set +# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set +CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y +# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set +CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_CXX_EXCEPTIONS is not set +CONFIG_STACK_CHECK_NONE=y +# CONFIG_STACK_CHECK_NORM is not set +# CONFIG_STACK_CHECK_STRONG is not set +# CONFIG_STACK_CHECK_ALL is not set +# CONFIG_WARN_WRITE_STRINGS is not set +# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set +# CONFIG_MCPWM_ISR_IRAM_SAFE is not set +# CONFIG_ESP_SYSTEM_PD_FLASH is not set +CONFIG_ESP32S3_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y +# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_ESP32S3_RTC_CLK_SRC_INT_8MD256 is not set +CONFIG_ESP32S3_RTC_CLK_CAL_CYCLES=1024 +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y +CONFIG_BROWNOUT_DET=y +CONFIG_ESP32S3_BROWNOUT_DET=y +CONFIG_BROWNOUT_DET_LVL_SEL_7=y +CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_7=y +# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_1 is not set +CONFIG_BROWNOUT_DET_LVL=7 +CONFIG_ESP32S3_BROWNOUT_DET_LVL=7 +CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y +CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU=y +CONFIG_PM_POWER_DOWN_TAGMEM_IN_LIGHT_SLEEP=y +# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_80 is not set +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_160=y +# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240 is not set +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=160 +CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_MAIN_TASK_STACK_SIZE=3584 +# CONFIG_CONSOLE_UART_DEFAULT is not set +# CONFIG_CONSOLE_UART_CUSTOM is not set +# CONFIG_CONSOLE_UART_NONE is not set +# CONFIG_ESP_CONSOLE_UART_NONE is not set +CONFIG_CONSOLE_UART_NUM=-1 +CONFIG_INT_WDT=y +CONFIG_INT_WDT_TIMEOUT_MS=300 +CONFIG_INT_WDT_CHECK_CPU1=y +CONFIG_TASK_WDT=y +CONFIG_ESP_TASK_WDT=y +# CONFIG_TASK_WDT_PANIC is not set +CONFIG_TASK_WDT_TIMEOUT_S=5 +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP32S3_DEBUG_OCDAWARE=y +CONFIG_IPC_TASK_STACK_SIZE=1280 +CONFIG_TIMER_TASK_STACK_SIZE=3584 +CONFIG_TIMER_TASK_PRIORITY=1 +CONFIG_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_TIMER_QUEUE_LENGTH=10 +# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set +# CONFIG_HAL_ASSERTION_SILIENT is not set +# CONFIG_NEWLIB_NANO_FORMAT is not set +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y +CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_SYSTIMER=y +CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_FRC1=y +# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_SYSTIMER is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_FRC1 is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_NONE is not set +CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_ESP32_PTHREAD_STACK_MIN=768 +CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set +CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set +# End of deprecated options diff --git a/apps/rx/sdkconfig.defaults b/apps/rx/sdkconfig.defaults new file mode 100644 index 0000000..3c3439c --- /dev/null +++ b/apps/rx/sdkconfig.defaults @@ -0,0 +1,43 @@ +# Роль за замовчуванням +CONFIG_LORA_ROLE_RX=y + + +# Піни RA01 (налаштуйте під свою плату) +CONFIG_LORA_SPI_HOST=2 +CONFIG_LORA_PIN_CS=12 +CONFIG_LORA_PIN_RST=-1 +CONFIG_LORA_PIN_MOSI=10 +CONFIG_LORA_PIN_MISO=9 +CONFIG_LORA_PIN_SCK=11 +CONFIG_LORA_PIN_BUSY=13 +CONFIG_LORA_PIN_DIO1=-1 + +# Радіо +CONFIG_LORA_FREQ_MHZ=433 +CONFIG_LORA_BW_KHZ=125 +CONFIG_LORA_SF=7 +CONFIG_LORA_CR=5 + +# Консоль через USB Serial/JTAG (CDC), щоб не тримати BOOT +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y +CONFIG_ESP_CONSOLE_UART_NONE=y +# CONFIG_ESP_CONSOLE_UART_DEFAULT is not set +# CONFIG_ESP_CONSOLE_USB_CDC is not set + +# Джойстик +CONFIG_JOYSTICK_ADC_CHANNEL=5 +CONFIG_JOYSTICK_ADC_LEVEL_LEFT=600 +CONFIG_JOYSTICK_ADC_LEVEL_UP=1200 +CONFIG_JOYSTICK_ADC_LEVEL_PRESS=1900 +CONFIG_JOYSTICK_ADC_LEVEL_DOWN=2600 +CONFIG_JOYSTICK_ADC_LEVEL_RIGHT=3300 +CONFIG_JOYSTICK_ADC_TOLERANCE=150 + +# Дисплей +CONFIG_DISPLAY_I2C_PORT=0 +CONFIG_DISPLAY_I2C_ADDR=60 +CONFIG_DISPLAY_PIN_SDA=7 +CONFIG_DISPLAY_PIN_SCL=8 +CONFIG_DISPLAY_PIN_RST=-1 +CONFIG_DISPLAY_WIDTH=128 +CONFIG_DISPLAY_HEIGHT=64 diff --git a/apps/rx/sdkconfig.old b/apps/rx/sdkconfig.old new file mode 100644 index 0000000..aae2a5a --- /dev/null +++ b/apps/rx/sdkconfig.old @@ -0,0 +1,1548 @@ +# +# Automatically generated file. DO NOT EDIT. +# Espressif IoT Development Framework (ESP-IDF) 5.5.1 Project Configuration +# +CONFIG_SOC_CAPS_ECO_VER_MAX=301 +CONFIG_SOC_ADC_SUPPORTED=y +CONFIG_SOC_DAC_SUPPORTED=y +CONFIG_SOC_UART_SUPPORTED=y +CONFIG_SOC_MCPWM_SUPPORTED=y +CONFIG_SOC_GPTIMER_SUPPORTED=y +CONFIG_SOC_SDMMC_HOST_SUPPORTED=y +CONFIG_SOC_BT_SUPPORTED=y +CONFIG_SOC_PCNT_SUPPORTED=y +CONFIG_SOC_PHY_SUPPORTED=y +CONFIG_SOC_WIFI_SUPPORTED=y +CONFIG_SOC_SDIO_SLAVE_SUPPORTED=y +CONFIG_SOC_TWAI_SUPPORTED=y +CONFIG_SOC_EFUSE_SUPPORTED=y +CONFIG_SOC_EMAC_SUPPORTED=y +CONFIG_SOC_ULP_SUPPORTED=y +CONFIG_SOC_CCOMP_TIMER_SUPPORTED=y +CONFIG_SOC_RTC_FAST_MEM_SUPPORTED=y +CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED=y +CONFIG_SOC_RTC_MEM_SUPPORTED=y +CONFIG_SOC_I2S_SUPPORTED=y +CONFIG_SOC_RMT_SUPPORTED=y +CONFIG_SOC_SDM_SUPPORTED=y +CONFIG_SOC_GPSPI_SUPPORTED=y +CONFIG_SOC_LEDC_SUPPORTED=y +CONFIG_SOC_I2C_SUPPORTED=y +CONFIG_SOC_SUPPORT_COEXISTENCE=y +CONFIG_SOC_AES_SUPPORTED=y +CONFIG_SOC_MPI_SUPPORTED=y +CONFIG_SOC_SHA_SUPPORTED=y +CONFIG_SOC_FLASH_ENC_SUPPORTED=y +CONFIG_SOC_SECURE_BOOT_SUPPORTED=y +CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y +CONFIG_SOC_BOD_SUPPORTED=y +CONFIG_SOC_ULP_FSM_SUPPORTED=y +CONFIG_SOC_CLK_TREE_SUPPORTED=y +CONFIG_SOC_MPU_SUPPORTED=y +CONFIG_SOC_WDT_SUPPORTED=y +CONFIG_SOC_SPI_FLASH_SUPPORTED=y +CONFIG_SOC_RNG_SUPPORTED=y +CONFIG_SOC_LIGHT_SLEEP_SUPPORTED=y +CONFIG_SOC_DEEP_SLEEP_SUPPORTED=y +CONFIG_SOC_LP_PERIPH_SHARE_INTERRUPT=y +CONFIG_SOC_PM_SUPPORTED=y +CONFIG_SOC_DPORT_WORKAROUND_DIS_INTERRUPT_LVL=5 +CONFIG_SOC_XTAL_SUPPORT_26M=y +CONFIG_SOC_XTAL_SUPPORT_40M=y +CONFIG_SOC_XTAL_SUPPORT_AUTO_DETECT=y +CONFIG_SOC_ADC_RTC_CTRL_SUPPORTED=y +CONFIG_SOC_ADC_DIG_CTRL_SUPPORTED=y +CONFIG_SOC_ADC_DMA_SUPPORTED=y +CONFIG_SOC_ADC_PERIPH_NUM=2 +CONFIG_SOC_ADC_MAX_CHANNEL_NUM=10 +CONFIG_SOC_ADC_ATTEN_NUM=4 +CONFIG_SOC_ADC_DIGI_CONTROLLER_NUM=2 +CONFIG_SOC_ADC_PATT_LEN_MAX=16 +CONFIG_SOC_ADC_DIGI_MIN_BITWIDTH=9 +CONFIG_SOC_ADC_DIGI_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_DIGI_RESULT_BYTES=2 +CONFIG_SOC_ADC_DIGI_DATA_BYTES_PER_CONV=4 +CONFIG_SOC_ADC_DIGI_MONITOR_NUM=0 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH=2 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW=20 +CONFIG_SOC_ADC_RTC_MIN_BITWIDTH=9 +CONFIG_SOC_ADC_RTC_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_SHARED_POWER=y +CONFIG_SOC_BROWNOUT_RESET_SUPPORTED=y +CONFIG_SOC_SHARED_IDCACHE_SUPPORTED=y +CONFIG_SOC_IDCACHE_PER_CORE=y +CONFIG_SOC_CPU_CORES_NUM=2 +CONFIG_SOC_CPU_INTR_NUM=32 +CONFIG_SOC_CPU_HAS_FPU=y +CONFIG_SOC_HP_CPU_HAS_MULTIPLE_CORES=y +CONFIG_SOC_CPU_BREAKPOINTS_NUM=2 +CONFIG_SOC_CPU_WATCHPOINTS_NUM=2 +CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=0x40 +CONFIG_SOC_DAC_CHAN_NUM=2 +CONFIG_SOC_DAC_RESOLUTION=8 +CONFIG_SOC_DAC_DMA_16BIT_ALIGN=y +CONFIG_SOC_GPIO_PORT=1 +CONFIG_SOC_GPIO_PIN_COUNT=40 +CONFIG_SOC_GPIO_VALID_GPIO_MASK=0xFFFFFFFFFF +CONFIG_SOC_GPIO_IN_RANGE_MAX=39 +CONFIG_SOC_GPIO_OUT_RANGE_MAX=33 +CONFIG_SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK=0xEF0FEA +CONFIG_SOC_GPIO_CLOCKOUT_BY_IO_MUX=y +CONFIG_SOC_GPIO_CLOCKOUT_CHANNEL_NUM=3 +CONFIG_SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP=y +CONFIG_SOC_I2C_NUM=2 +CONFIG_SOC_HP_I2C_NUM=2 +CONFIG_SOC_I2C_FIFO_LEN=32 +CONFIG_SOC_I2C_CMD_REG_NUM=16 +CONFIG_SOC_I2C_SUPPORT_SLAVE=y +CONFIG_SOC_I2C_SUPPORT_APB=y +CONFIG_SOC_I2C_SUPPORT_10BIT_ADDR=y +CONFIG_SOC_I2C_STOP_INDEPENDENT=y +CONFIG_SOC_I2S_NUM=2 +CONFIG_SOC_I2S_HW_VERSION_1=y +CONFIG_SOC_I2S_SUPPORTS_APLL=y +CONFIG_SOC_I2S_SUPPORTS_PLL_F160M=y +CONFIG_SOC_I2S_SUPPORTS_PDM=y +CONFIG_SOC_I2S_SUPPORTS_PDM_TX=y +CONFIG_SOC_I2S_SUPPORTS_PCM2PDM=y +CONFIG_SOC_I2S_SUPPORTS_PDM_RX=y +CONFIG_SOC_I2S_SUPPORTS_PDM2PCM=y +CONFIG_SOC_I2S_PDM_MAX_TX_LINES=1 +CONFIG_SOC_I2S_PDM_MAX_RX_LINES=1 +CONFIG_SOC_I2S_SUPPORTS_ADC_DAC=y +CONFIG_SOC_I2S_SUPPORTS_ADC=y +CONFIG_SOC_I2S_SUPPORTS_DAC=y +CONFIG_SOC_I2S_SUPPORTS_LCD_CAMERA=y +CONFIG_SOC_I2S_MAX_DATA_WIDTH=24 +CONFIG_SOC_I2S_TRANS_SIZE_ALIGN_WORD=y +CONFIG_SOC_I2S_LCD_I80_VARIANT=y +CONFIG_SOC_LCD_I80_SUPPORTED=y +CONFIG_SOC_LCD_I80_BUSES=2 +CONFIG_SOC_LCD_I80_BUS_WIDTH=24 +CONFIG_SOC_LEDC_HAS_TIMER_SPECIFIC_MUX=y +CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y +CONFIG_SOC_LEDC_SUPPORT_REF_TICK=y +CONFIG_SOC_LEDC_SUPPORT_HS_MODE=y +CONFIG_SOC_LEDC_TIMER_NUM=4 +CONFIG_SOC_LEDC_CHANNEL_NUM=8 +CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=20 +CONFIG_SOC_MCPWM_GROUPS=2 +CONFIG_SOC_MCPWM_TIMERS_PER_GROUP=3 +CONFIG_SOC_MCPWM_OPERATORS_PER_GROUP=3 +CONFIG_SOC_MCPWM_COMPARATORS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_GENERATORS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_TRIGGERS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_GPIO_FAULTS_PER_GROUP=3 +CONFIG_SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP=y +CONFIG_SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER=3 +CONFIG_SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP=3 +CONFIG_SOC_MMU_PERIPH_NUM=2 +CONFIG_SOC_MMU_LINEAR_ADDRESS_REGION_NUM=3 +CONFIG_SOC_MPU_MIN_REGION_SIZE=0x20000000 +CONFIG_SOC_MPU_REGIONS_MAX_NUM=8 +CONFIG_SOC_PCNT_GROUPS=1 +CONFIG_SOC_PCNT_UNITS_PER_GROUP=8 +CONFIG_SOC_PCNT_CHANNELS_PER_UNIT=2 +CONFIG_SOC_PCNT_THRES_POINT_PER_UNIT=2 +CONFIG_SOC_RMT_GROUPS=1 +CONFIG_SOC_RMT_TX_CANDIDATES_PER_GROUP=8 +CONFIG_SOC_RMT_RX_CANDIDATES_PER_GROUP=8 +CONFIG_SOC_RMT_CHANNELS_PER_GROUP=8 +CONFIG_SOC_RMT_MEM_WORDS_PER_CHANNEL=64 +CONFIG_SOC_RMT_SUPPORT_REF_TICK=y +CONFIG_SOC_RMT_SUPPORT_APB=y +CONFIG_SOC_RMT_CHANNEL_CLK_INDEPENDENT=y +CONFIG_SOC_RTCIO_PIN_COUNT=18 +CONFIG_SOC_RTCIO_INPUT_OUTPUT_SUPPORTED=y +CONFIG_SOC_RTCIO_HOLD_SUPPORTED=y +CONFIG_SOC_RTCIO_WAKE_SUPPORTED=y +CONFIG_SOC_SDM_GROUPS=1 +CONFIG_SOC_SDM_CHANNELS_PER_GROUP=8 +CONFIG_SOC_SDM_CLK_SUPPORT_APB=y +CONFIG_SOC_SPI_HD_BOTH_INOUT_SUPPORTED=y +CONFIG_SOC_SPI_AS_CS_SUPPORTED=y +CONFIG_SOC_SPI_PERIPH_NUM=3 +CONFIG_SOC_SPI_DMA_CHAN_NUM=2 +CONFIG_SOC_SPI_MAX_CS_NUM=3 +CONFIG_SOC_SPI_SUPPORT_CLK_APB=y +CONFIG_SOC_SPI_MAXIMUM_BUFFER_SIZE=64 +CONFIG_SOC_SPI_MAX_PRE_DIVIDER=8192 +CONFIG_SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_26M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_20M_SUPPORTED=y +CONFIG_SOC_TIMER_GROUPS=2 +CONFIG_SOC_TIMER_GROUP_TIMERS_PER_GROUP=2 +CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=64 +CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=4 +CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y +CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32 +CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16 +CONFIG_SOC_TOUCH_SENSOR_VERSION=1 +CONFIG_SOC_TOUCH_SENSOR_NUM=10 +CONFIG_SOC_TOUCH_MIN_CHAN_ID=0 +CONFIG_SOC_TOUCH_MAX_CHAN_ID=9 +CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y +CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1 +CONFIG_SOC_TWAI_CONTROLLER_NUM=1 +CONFIG_SOC_TWAI_MASK_FILTER_NUM=1 +CONFIG_SOC_TWAI_BRP_MIN=2 +CONFIG_SOC_TWAI_CLK_SUPPORT_APB=y +CONFIG_SOC_TWAI_SUPPORT_MULTI_ADDRESS_LAYOUT=y +CONFIG_SOC_UART_NUM=3 +CONFIG_SOC_UART_HP_NUM=3 +CONFIG_SOC_UART_SUPPORT_APB_CLK=y +CONFIG_SOC_UART_SUPPORT_REF_TICK=y +CONFIG_SOC_UART_FIFO_LEN=128 +CONFIG_SOC_UART_BITRATE_MAX=5000000 +CONFIG_SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE=y +CONFIG_SOC_SPIRAM_SUPPORTED=y +CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y +CONFIG_SOC_SHA_SUPPORT_PARALLEL_ENG=y +CONFIG_SOC_SHA_ENDIANNESS_BE=y +CONFIG_SOC_SHA_SUPPORT_SHA1=y +CONFIG_SOC_SHA_SUPPORT_SHA256=y +CONFIG_SOC_SHA_SUPPORT_SHA384=y +CONFIG_SOC_SHA_SUPPORT_SHA512=y +CONFIG_SOC_MPI_MEM_BLOCKS_NUM=4 +CONFIG_SOC_MPI_OPERATIONS_NUM=1 +CONFIG_SOC_RSA_MAX_BIT_LEN=4096 +CONFIG_SOC_AES_SUPPORT_AES_128=y +CONFIG_SOC_AES_SUPPORT_AES_192=y +CONFIG_SOC_AES_SUPPORT_AES_256=y +CONFIG_SOC_SECURE_BOOT_V1=y +CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS=1 +CONFIG_SOC_FLASH_ENCRYPTED_XTS_AES_BLOCK_MAX=32 +CONFIG_SOC_PHY_DIG_REGS_MEM_SIZE=21 +CONFIG_SOC_PM_SUPPORT_EXT0_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_EXT1_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_EXT_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_RTC_PERIPH_PD=y +CONFIG_SOC_PM_SUPPORT_RTC_FAST_MEM_PD=y +CONFIG_SOC_PM_SUPPORT_RTC_SLOW_MEM_PD=y +CONFIG_SOC_PM_SUPPORT_RC_FAST_PD=y +CONFIG_SOC_PM_SUPPORT_VDDSDIO_PD=y +CONFIG_SOC_PM_SUPPORT_MODEM_PD=y +CONFIG_SOC_CONFIGURABLE_VDDSDIO_SUPPORTED=y +CONFIG_SOC_PM_MODEM_PD_BY_SW=y +CONFIG_SOC_CLK_APLL_SUPPORTED=y +CONFIG_SOC_CLK_RC_FAST_D256_SUPPORTED=y +CONFIG_SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256=y +CONFIG_SOC_CLK_RC_FAST_SUPPORT_CALIBRATION=y +CONFIG_SOC_CLK_XTAL32K_SUPPORTED=y +CONFIG_SOC_CLK_LP_FAST_SUPPORT_XTAL_D4=y +CONFIG_SOC_SDMMC_USE_IOMUX=y +CONFIG_SOC_SDMMC_NUM_SLOTS=2 +CONFIG_SOC_WIFI_WAPI_SUPPORT=y +CONFIG_SOC_WIFI_CSI_SUPPORT=y +CONFIG_SOC_WIFI_MESH_SUPPORT=y +CONFIG_SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW=y +CONFIG_SOC_WIFI_NAN_SUPPORT=y +CONFIG_SOC_BLE_SUPPORTED=y +CONFIG_SOC_BLE_MESH_SUPPORTED=y +CONFIG_SOC_BT_CLASSIC_SUPPORTED=y +CONFIG_SOC_BLUFI_SUPPORTED=y +CONFIG_SOC_BT_H2C_ENC_KEY_CTRL_ENH_VSC_SUPPORTED=y +CONFIG_SOC_BLE_MULTI_CONN_OPTIMIZATION=y +CONFIG_SOC_ULP_HAS_ADC=y +CONFIG_SOC_PHY_COMBO_MODULE=y +CONFIG_SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK=y +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TOOLCHAIN="gcc" +CONFIG_IDF_TOOLCHAIN_GCC=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET_ARCH="xtensa" +CONFIG_IDF_TARGET="esp32" +CONFIG_IDF_INIT_VERSION="5.5.1" +CONFIG_IDF_TARGET_ESP32=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 + +# +# Build type +# +CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y +# CONFIG_APP_BUILD_TYPE_RAM is not set +CONFIG_APP_BUILD_GENERATE_BINARIES=y +CONFIG_APP_BUILD_BOOTLOADER=y +CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y +# CONFIG_APP_REPRODUCIBLE_BUILD is not set +# CONFIG_APP_NO_BLOBS is not set +# CONFIG_APP_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set +# CONFIG_APP_COMPATIBLE_PRE_V3_1_BOOTLOADERS is not set +# end of Build type + +# +# Bootloader config +# + +# +# Bootloader manager +# +CONFIG_BOOTLOADER_COMPILE_TIME_DATE=y +CONFIG_BOOTLOADER_PROJECT_VER=1 +# end of Bootloader manager + +# +# Application Rollback +# +# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set +# end of Application Rollback + +# +# Recovery Bootloader and Rollback +# +# end of Recovery Bootloader and Rollback + +CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x1000 +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set + +# +# Log +# +CONFIG_BOOTLOADER_LOG_VERSION_1=y +CONFIG_BOOTLOADER_LOG_VERSION=1 +# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set +CONFIG_BOOTLOADER_LOG_LEVEL=3 + +# +# Format +# +# CONFIG_BOOTLOADER_LOG_COLORS is not set +CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y +# end of Format + +# +# Settings +# +CONFIG_BOOTLOADER_LOG_MODE_TEXT_EN=y +CONFIG_BOOTLOADER_LOG_MODE_TEXT=y +# end of Settings +# end of Log + +# +# Serial Flash Configurations +# +# CONFIG_BOOTLOADER_FLASH_DC_AWARE is not set +CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=y +# end of Serial Flash Configurations + +# CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_8V is not set +CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y +# CONFIG_BOOTLOADER_FACTORY_RESET is not set +# CONFIG_BOOTLOADER_APP_TEST is not set +CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y +CONFIG_BOOTLOADER_WDT_ENABLE=y +# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set +CONFIG_BOOTLOADER_WDT_TIME_MS=9000 +# CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set +CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0 +# CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set +# end of Bootloader config + +# +# Security features +# +CONFIG_SECURE_BOOT_V1_SUPPORTED=y +# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set +# CONFIG_SECURE_BOOT is not set +# CONFIG_SECURE_FLASH_ENC_ENABLED is not set +# end of Security features + +# +# Application manager +# +CONFIG_APP_COMPILE_TIME_DATE=y +# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set +# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set +# CONFIG_APP_PROJECT_VER_FROM_CONFIG is not set +CONFIG_APP_RETRIEVE_LEN_ELF_SHA=9 +# end of Application manager + +CONFIG_ESP_ROM_HAS_CRC_LE=y +CONFIG_ESP_ROM_HAS_CRC_BE=y +CONFIG_ESP_ROM_HAS_MZ_CRC32=y +CONFIG_ESP_ROM_HAS_JPEG_DECODE=y +CONFIG_ESP_ROM_HAS_UART_BUF_SWITCH=y +CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y +CONFIG_ESP_ROM_HAS_NEWLIB=y +CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y +CONFIG_ESP_ROM_HAS_NEWLIB_32BIT_TIME=y +CONFIG_ESP_ROM_HAS_SW_FLOAT=y +CONFIG_ESP_ROM_USB_OTG_NUM=-1 +CONFIG_ESP_ROM_USB_SERIAL_DEVICE_NUM=-1 +CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y +CONFIG_ESP_ROM_HAS_OUTPUT_PUTC_FUNC=y + +# +# Serial flasher config +# +# CONFIG_ESPTOOLPY_NO_STUB is not set +# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set +# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE="dio" +# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set +CONFIG_ESPTOOLPY_FLASHFREQ_40M=y +# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set +# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set +CONFIG_ESPTOOLPY_FLASHFREQ="40m" +# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE="2MB" +# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set +CONFIG_ESPTOOLPY_BEFORE_RESET=y +# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +# CONFIG_ESPTOOLPY_AFTER_NORESET is not set +CONFIG_ESPTOOLPY_AFTER="hard_reset" +CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 +# end of Serial flasher config + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_SINGLE_APP=y +# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set +# CONFIG_PARTITION_TABLE_TWO_OTA is not set +# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set +# CONFIG_PARTITION_TABLE_CUSTOM is not set +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# Compiler options +# +CONFIG_COMPILER_OPTIMIZATION_DEBUG=y +# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set +# CONFIG_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_COMPILER_OPTIMIZATION_NONE is not set +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set +CONFIG_COMPILER_ASSERT_NDEBUG_EVALUATE=y +CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set +CONFIG_COMPILER_HIDE_PATHS_MACROS=y +# CONFIG_COMPILER_CXX_EXCEPTIONS is not set +# CONFIG_COMPILER_CXX_RTTI is not set +CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y +# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set +# CONFIG_COMPILER_NO_MERGE_CONSTANTS is not set +# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set +CONFIG_COMPILER_DISABLE_DEFAULT_ERRORS=y +# CONFIG_COMPILER_DISABLE_GCC12_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC13_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC14_WARNINGS is not set +# CONFIG_COMPILER_DUMP_RTL_FILES is not set +CONFIG_COMPILER_RT_LIB_GCCLIB=y +CONFIG_COMPILER_RT_LIB_NAME="gcc" +CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=y +# CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE is not set +# CONFIG_COMPILER_STATIC_ANALYZER is not set +# end of Compiler options + +# +# Component config +# + +# +# !!! MINIMAL_BUILD is enabled !!! +# + +# +# Only common components and those transitively required by the main component are listed +# + +# +# If a component configuration is missing, please add it to the main component's requirements +# + +# +# Driver Configurations +# + +# +# Legacy TWAI Driver Configurations +# +# CONFIG_TWAI_SKIP_LEGACY_CONFLICT_CHECK is not set +CONFIG_TWAI_ERRATA_FIX_BUS_OFF_REC=y +CONFIG_TWAI_ERRATA_FIX_TX_INTR_LOST=y +CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID=y +CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT=y +CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y +# end of Legacy TWAI Driver Configurations + +# +# Legacy ADC Driver Configuration +# +CONFIG_ADC_DISABLE_DAC=y +# CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_ADC_SKIP_LEGACY_CONFLICT_CHECK is not set + +# +# Legacy ADC Calibration Configuration +# +CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y +CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y +CONFIG_ADC_CAL_LUT_ENABLE=y +# CONFIG_ADC_CALI_SUPPRESS_DEPRECATE_WARN is not set +# end of Legacy ADC Calibration Configuration +# end of Legacy ADC Driver Configuration + +# +# Legacy DAC Driver Configurations +# +# CONFIG_DAC_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_DAC_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy DAC Driver Configurations + +# +# Legacy MCPWM Driver Configurations +# +# CONFIG_MCPWM_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_MCPWM_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy MCPWM Driver Configurations + +# +# Legacy Timer Group Driver Configurations +# +# CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_GPTIMER_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Timer Group Driver Configurations + +# +# Legacy RMT Driver Configurations +# +# CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_RMT_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy RMT Driver Configurations + +# +# Legacy I2S Driver Configurations +# +# CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy I2S Driver Configurations + +# +# Legacy I2C Driver Configurations +# +# CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy I2C Driver Configurations + +# +# Legacy PCNT Driver Configurations +# +# CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_PCNT_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy PCNT Driver Configurations + +# +# Legacy SDM Driver Configurations +# +# CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_SDM_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy SDM Driver Configurations + +# +# Legacy Touch Sensor Driver Configurations +# +# CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_TOUCH_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Touch Sensor Driver Configurations +# end of Driver Configurations + +# +# eFuse Bit Manager +# +# CONFIG_EFUSE_CUSTOM_TABLE is not set +# CONFIG_EFUSE_VIRTUAL is not set +# CONFIG_EFUSE_CODE_SCHEME_COMPAT_NONE is not set +CONFIG_EFUSE_CODE_SCHEME_COMPAT_3_4=y +# CONFIG_EFUSE_CODE_SCHEME_COMPAT_REPEAT is not set +CONFIG_EFUSE_MAX_BLK_LEN=192 +# end of eFuse Bit Manager + +# +# ADC and ADC Calibration +# +# CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM is not set +# CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE is not set + +# +# ADC Calibration Configurations +# +CONFIG_ADC_CALI_EFUSE_TP_ENABLE=y +CONFIG_ADC_CALI_EFUSE_VREF_ENABLE=y +CONFIG_ADC_CALI_LUT_ENABLE=y +# end of ADC Calibration Configurations + +CONFIG_ADC_DISABLE_DAC_OUTPUT=y +# CONFIG_ADC_ENABLE_DEBUG_LOG is not set +# end of ADC and ADC Calibration + +# +# Common ESP-related +# +CONFIG_ESP_ERR_TO_NAME_LOOKUP=y +# end of Common ESP-related + +# +# ESP-Driver:DAC Configurations +# +# CONFIG_DAC_CTRL_FUNC_IN_IRAM is not set +# CONFIG_DAC_ISR_IRAM_SAFE is not set +# CONFIG_DAC_ENABLE_DEBUG_LOG is not set +CONFIG_DAC_DMA_AUTO_16BIT_ALIGN=y +# end of ESP-Driver:DAC Configurations + +# +# ESP-Driver:GPIO Configurations +# +# CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL is not set +# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:GPIO Configurations + +# +# ESP-Driver:GPTimer Configurations +# +CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y +# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set +# CONFIG_GPTIMER_ISR_CACHE_SAFE is not set +CONFIG_GPTIMER_OBJ_CACHE_SAFE=y +# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:GPTimer Configurations + +# +# ESP-Driver:I2C Configurations +# +# CONFIG_I2C_ISR_IRAM_SAFE is not set +# CONFIG_I2C_ENABLE_DEBUG_LOG is not set +# CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2 is not set +CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y +# end of ESP-Driver:I2C Configurations + +# +# ESP-Driver:I2S Configurations +# +# CONFIG_I2S_ISR_IRAM_SAFE is not set +# CONFIG_I2S_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:I2S Configurations + +# +# ESP-Driver:LEDC Configurations +# +# CONFIG_LEDC_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:LEDC Configurations + +# +# ESP-Driver:MCPWM Configurations +# +CONFIG_MCPWM_ISR_HANDLER_IN_IRAM=y +# CONFIG_MCPWM_ISR_CACHE_SAFE is not set +# CONFIG_MCPWM_CTRL_FUNC_IN_IRAM is not set +CONFIG_MCPWM_OBJ_CACHE_SAFE=y +# CONFIG_MCPWM_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:MCPWM Configurations + +# +# ESP-Driver:PCNT Configurations +# +# CONFIG_PCNT_CTRL_FUNC_IN_IRAM is not set +# CONFIG_PCNT_ISR_IRAM_SAFE is not set +# CONFIG_PCNT_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:PCNT Configurations + +# +# ESP-Driver:RMT Configurations +# +CONFIG_RMT_ENCODER_FUNC_IN_IRAM=y +CONFIG_RMT_TX_ISR_HANDLER_IN_IRAM=y +CONFIG_RMT_RX_ISR_HANDLER_IN_IRAM=y +# CONFIG_RMT_RECV_FUNC_IN_IRAM is not set +# CONFIG_RMT_TX_ISR_CACHE_SAFE is not set +# CONFIG_RMT_RX_ISR_CACHE_SAFE is not set +CONFIG_RMT_OBJ_CACHE_SAFE=y +# CONFIG_RMT_ENABLE_DEBUG_LOG is not set +# CONFIG_RMT_ISR_IRAM_SAFE is not set +# end of ESP-Driver:RMT Configurations + +# +# ESP-Driver:Sigma Delta Modulator Configurations +# +# CONFIG_SDM_CTRL_FUNC_IN_IRAM is not set +# CONFIG_SDM_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:Sigma Delta Modulator Configurations + +# +# ESP-Driver:SPI Configurations +# +# CONFIG_SPI_MASTER_IN_IRAM is not set +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +# CONFIG_SPI_SLAVE_IN_IRAM is not set +CONFIG_SPI_SLAVE_ISR_IN_IRAM=y +# end of ESP-Driver:SPI Configurations + +# +# ESP-Driver:TWAI Configurations +# +# CONFIG_TWAI_ISR_IN_IRAM is not set +# CONFIG_TWAI_ISR_CACHE_SAFE is not set +# CONFIG_TWAI_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:TWAI Configurations + +# +# ESP-Driver:UART Configurations +# +# CONFIG_UART_ISR_IN_IRAM is not set +# end of ESP-Driver:UART Configurations + +# +# ESP-Driver:UHCI Configurations +# +# CONFIG_UHCI_ISR_HANDLER_IN_IRAM is not set +# CONFIG_UHCI_ISR_CACHE_SAFE is not set +# CONFIG_UHCI_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:UHCI Configurations + +# +# Hardware Settings +# + +# +# Chip revision +# +CONFIG_ESP32_REV_MIN_0=y +# CONFIG_ESP32_REV_MIN_1 is not set +# CONFIG_ESP32_REV_MIN_1_1 is not set +# CONFIG_ESP32_REV_MIN_2 is not set +# CONFIG_ESP32_REV_MIN_3 is not set +# CONFIG_ESP32_REV_MIN_3_1 is not set +CONFIG_ESP32_REV_MIN=0 +CONFIG_ESP32_REV_MIN_FULL=0 +CONFIG_ESP_REV_MIN_FULL=0 + +# +# Maximum Supported ESP32 Revision (Rev v3.99) +# +CONFIG_ESP32_REV_MAX_FULL=399 +CONFIG_ESP_REV_MAX_FULL=399 +CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL=0 +CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL=99 + +# +# Maximum Supported ESP32 eFuse Block Revision (eFuse Block Rev v0.99) +# +# end of Chip revision + +# +# 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 is not set +# CONFIG_ESP_MAC_USE_CUSTOM_MAC_AS_BASE_MAC is not set +# end of MAC Config + +# +# Sleep Config +# +# CONFIG_ESP_SLEEP_POWER_DOWN_FLASH is not set +CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND=y +# CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU is not set +CONFIG_ESP_SLEEP_RTC_BUS_ISO_WORKAROUND=y +# CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND is not set +CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY=2000 +# CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION is not set +# CONFIG_ESP_SLEEP_DEBUG is not set +CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS=y +# end of Sleep Config + +# +# RTC Clock Config +# +CONFIG_RTC_CLK_SRC_INT_RC=y +# CONFIG_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_RTC_CLK_SRC_INT_8MD256 is not set +CONFIG_RTC_CLK_CAL_CYCLES=1024 +# end of RTC Clock Config + +# +# Peripheral Control +# +CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=y +CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=y +# end of Peripheral Control + +# +# Main XTAL Config +# +# CONFIG_XTAL_FREQ_26 is not set +# CONFIG_XTAL_FREQ_32 is not set +CONFIG_XTAL_FREQ_40=y +# CONFIG_XTAL_FREQ_AUTO is not set +CONFIG_XTAL_FREQ=40 +# end of Main XTAL Config + +# +# Power Supplier +# + +# +# Brownout Detector +# +CONFIG_ESP_BROWNOUT_DET=y +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_0=y +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7 is not set +CONFIG_ESP_BROWNOUT_DET_LVL=0 +CONFIG_ESP_BROWNOUT_USE_INTR=y +# end of Brownout Detector +# end of Power Supplier + +CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y +CONFIG_ESP_INTR_IN_IRAM=y +# end of Hardware Settings + +# +# ESP-MM: Memory Management Configurations +# +# end of ESP-MM: Memory Management Configurations + +# +# Partition API Configuration +# +# end of Partition API Configuration + +# +# Power Management +# +CONFIG_PM_SLEEP_FUNC_IN_IRAM=y +# CONFIG_PM_ENABLE is not set +CONFIG_PM_SLP_IRAM_OPT=y +# end of Power Management + +# +# ESP Ringbuf +# +# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set +# end of ESP Ringbuf + +# +# ESP-ROM +# +CONFIG_ESP_ROM_PRINT_IN_IRAM=y +# end of ESP-ROM + +# +# ESP Security Specific +# +# end of ESP Security Specific + +# +# ESP System Settings +# +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160 + +# +# Memory +# +# CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE is not set + +# +# Non-backward compatible options +# +# CONFIG_ESP_SYSTEM_ESP32_SRAM1_REGION_AS_IRAM is not set +# end of Non-backward compatible options +# end of Memory + +# +# Trace memory +# +# CONFIG_ESP32_TRAX is not set +CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x0 +# end of Trace memory + +CONFIG_ESP_SYSTEM_IN_IRAM=y +# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set +CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y +# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set +CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS=0 + +# +# Memory protection +# +# end of Memory protection + +CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584 +CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y +# CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set +# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set +CONFIG_ESP_MAIN_TASK_AFFINITY=0x0 +CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 +CONFIG_ESP_CONSOLE_UART_DEFAULT=y +# CONFIG_ESP_CONSOLE_UART_CUSTOM is not set +# CONFIG_ESP_CONSOLE_NONE is not set +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=0 +CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 +CONFIG_ESP_INT_WDT=y +CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 +CONFIG_ESP_INT_WDT_CHECK_CPU1=y +CONFIG_ESP_TASK_WDT_EN=y +CONFIG_ESP_TASK_WDT_INIT=y +# CONFIG_ESP_TASK_WDT_PANIC is not set +CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP_PANIC_HANDLER_IRAM is not set +# CONFIG_ESP_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP_DEBUG_OCDAWARE=y +# CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 is not set +CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4=y +# CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE is not set +# end of ESP System Settings + +# +# IPC (Inter-Processor Call) +# +CONFIG_ESP_IPC_ENABLE=y +CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 +CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y +CONFIG_ESP_IPC_ISR_ENABLE=y +# end of IPC (Inter-Processor Call) + +# +# ESP Timer (High Resolution Timer) +# +CONFIG_ESP_TIMER_IN_IRAM=y +# CONFIG_ESP_TIMER_PROFILING is not set +CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y +CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y +CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 +CONFIG_ESP_TIMER_INTERRUPT_LEVEL=1 +# CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL is not set +CONFIG_ESP_TIMER_TASK_AFFINITY=0x0 +CONFIG_ESP_TIMER_TASK_AFFINITY_CPU0=y +CONFIG_ESP_TIMER_ISR_AFFINITY_CPU0=y +# CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD is not set +CONFIG_ESP_TIMER_IMPL_TG0_LAC=y +# end of ESP Timer (High Resolution Timer) + +# +# FreeRTOS +# + +# +# Kernel +# +# CONFIG_FREERTOS_SMP is not set +# CONFIG_FREERTOS_UNICORE is not set +CONFIG_FREERTOS_HZ=100 +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y +CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 +# CONFIG_FREERTOS_USE_IDLE_HOOK is not set +# CONFIG_FREERTOS_USE_TICK_HOOK is not set +CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 +# CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY is not set +CONFIG_FREERTOS_USE_TIMERS=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_NAME="Tmr Svc" +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0 is not set +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU1 is not set +CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_CORE_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 +CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 +CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 +# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set +# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set +# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set +# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set +# end of Kernel + +# +# Port +# +CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y +# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set +CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y +# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set +# CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set +CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y +CONFIG_FREERTOS_ISR_STACKSIZE=1536 +CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y +# CONFIG_FREERTOS_FPU_IN_ISR is not set +CONFIG_FREERTOS_TICK_SUPPORT_CORETIMER=y +CONFIG_FREERTOS_CORETIMER_0=y +# CONFIG_FREERTOS_CORETIMER_1 is not set +CONFIG_FREERTOS_SYSTICK_USES_CCOUNT=y +# CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set +# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set +# end of Port + +# +# Extra +# +# end of Extra + +CONFIG_FREERTOS_PORT=y +CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y +CONFIG_FREERTOS_DEBUG_OCDAWARE=y +CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y +CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y +CONFIG_FREERTOS_NUMBER_OF_CORES=2 +CONFIG_FREERTOS_IN_IRAM=y +# end of FreeRTOS + +# +# Hardware Abstraction Layer (HAL) and Low Level (LL) +# +CONFIG_HAL_ASSERTION_EQUALS_SYSTEM=y +# CONFIG_HAL_ASSERTION_DISABLE is not set +# CONFIG_HAL_ASSERTION_SILENT is not set +# CONFIG_HAL_ASSERTION_ENABLE is not set +CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2 +# end of Hardware Abstraction Layer (HAL) and Low Level (LL) + +# +# Heap memory debugging +# +CONFIG_HEAP_POISONING_DISABLED=y +# CONFIG_HEAP_POISONING_LIGHT is not set +# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set +CONFIG_HEAP_TRACING_OFF=y +# CONFIG_HEAP_TRACING_STANDALONE is not set +# CONFIG_HEAP_TRACING_TOHOST is not set +# CONFIG_HEAP_USE_HOOKS is not set +# CONFIG_HEAP_TASK_TRACKING is not set +# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set +# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set +# end of Heap memory debugging + +# +# Log +# +CONFIG_LOG_VERSION_1=y +# CONFIG_LOG_VERSION_2 is not set +CONFIG_LOG_VERSION=1 + +# +# Log Level +# +# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set +# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set +# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set +# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y +# CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set +# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set +CONFIG_LOG_MAXIMUM_LEVEL=3 + +# +# Level Settings +# +# CONFIG_LOG_MASTER_LEVEL is not set +CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y +# CONFIG_LOG_TAG_LEVEL_IMPL_NONE is not set +# CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST is not set +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST=y +# CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY is not set +CONFIG_LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP=y +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31 +# end of Level Settings +# end of Log Level + +# +# Format +# +# CONFIG_LOG_COLORS is not set +CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y +# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set +# end of Format + +# +# Settings +# +CONFIG_LOG_MODE_TEXT_EN=y +CONFIG_LOG_MODE_TEXT=y +# end of Settings + +CONFIG_LOG_IN_IRAM=y +# end of Log + +# +# mbedTLS +# +CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y +# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set +# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set +CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +# CONFIG_MBEDTLS_DYNAMIC_BUFFER is not set +# CONFIG_MBEDTLS_DEBUG is not set + +# +# mbedTLS v3.x related +# +# CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 is not set +# CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH is not set +# CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set +# CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set +CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y +# CONFIG_MBEDTLS_SSL_KEYING_MATERIAL_EXPORT is not set +CONFIG_MBEDTLS_PKCS7_C=y +# end of mbedTLS v3.x related + +# +# Certificate Bundle +# +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set +# CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEPRECATED_LIST is not set +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS=200 +# end of Certificate Bundle + +# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set +# CONFIG_MBEDTLS_CMAC_C is not set +CONFIG_MBEDTLS_HARDWARE_AES=y +CONFIG_MBEDTLS_GCM_SUPPORT_NON_AES_CIPHER=y +CONFIG_MBEDTLS_HARDWARE_MPI=y +# CONFIG_MBEDTLS_LARGE_KEY_SOFTWARE_MPI is not set +CONFIG_MBEDTLS_HARDWARE_SHA=y +CONFIG_MBEDTLS_ROM_MD5=y +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY is not set +CONFIG_MBEDTLS_HAVE_TIME=y +# CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set +# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set +CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y +CONFIG_MBEDTLS_SHA1_C=y +CONFIG_MBEDTLS_SHA512_C=y +# CONFIG_MBEDTLS_SHA3_C is not set +CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y +# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set +# CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set +# CONFIG_MBEDTLS_TLS_DISABLED is not set +CONFIG_MBEDTLS_TLS_SERVER=y +CONFIG_MBEDTLS_TLS_CLIENT=y +CONFIG_MBEDTLS_TLS_ENABLED=y + +# +# TLS Key Exchange Methods +# +# CONFIG_MBEDTLS_PSK_MODES is not set +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y +# end of TLS Key Exchange Methods + +CONFIG_MBEDTLS_SSL_RENEGOTIATION=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +# CONFIG_MBEDTLS_SSL_PROTO_GMTSSL1_1 is not set +# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set +CONFIG_MBEDTLS_SSL_ALPN=y +CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y +CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y + +# +# Symmetric Ciphers +# +CONFIG_MBEDTLS_AES_C=y +# CONFIG_MBEDTLS_CAMELLIA_C is not set +# CONFIG_MBEDTLS_DES_C is not set +# CONFIG_MBEDTLS_BLOWFISH_C is not set +# CONFIG_MBEDTLS_XTEA_C is not set +CONFIG_MBEDTLS_CCM_C=y +CONFIG_MBEDTLS_GCM_C=y +# CONFIG_MBEDTLS_NIST_KW_C is not set +# end of Symmetric Ciphers + +# CONFIG_MBEDTLS_RIPEMD160_C is not set + +# +# Certificates +# +CONFIG_MBEDTLS_PEM_PARSE_C=y +CONFIG_MBEDTLS_PEM_WRITE_C=y +CONFIG_MBEDTLS_X509_CRL_PARSE_C=y +CONFIG_MBEDTLS_X509_CSR_PARSE_C=y +# end of Certificates + +CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_PK_PARSE_EC_EXTENDED=y +CONFIG_MBEDTLS_PK_PARSE_EC_COMPRESSED=y +# CONFIG_MBEDTLS_DHM_C is not set +CONFIG_MBEDTLS_ECDH_C=y +CONFIG_MBEDTLS_ECDSA_C=y +# CONFIG_MBEDTLS_ECJPAKE_C is not set +CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y +CONFIG_MBEDTLS_ECP_NIST_OPTIM=y +# CONFIG_MBEDTLS_ECP_FIXED_POINT_OPTIM is not set +# CONFIG_MBEDTLS_POLY1305_C is not set +# CONFIG_MBEDTLS_CHACHA20_C is not set +# CONFIG_MBEDTLS_HKDF_C is not set +# CONFIG_MBEDTLS_THREADING_C is not set +CONFIG_MBEDTLS_ERROR_STRINGS=y +# CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION is not set +# end of mbedTLS + +# +# LibC +# +CONFIG_LIBC_NEWLIB=y +CONFIG_LIBC_MISC_IN_IRAM=y +CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=y +# CONFIG_LIBC_NEWLIB_NANO_FORMAT is not set +CONFIG_LIBC_TIME_SYSCALL_USE_RTC_HRT=y +# CONFIG_LIBC_TIME_SYSCALL_USE_RTC is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_HRT is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_NONE is not set +# end of LibC + +# +# PThreads +# +CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_PTHREAD_STACK_MIN=768 +CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY=y +# CONFIG_PTHREAD_DEFAULT_CORE_0 is not set +# CONFIG_PTHREAD_DEFAULT_CORE_1 is not set +CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" +# end of PThreads + +# +# MMU Config +# +CONFIG_MMU_PAGE_SIZE_64KB=y +CONFIG_MMU_PAGE_MODE="64KB" +CONFIG_MMU_PAGE_SIZE=0x10000 +# end of MMU Config + +# +# Main Flash configuration +# + +# +# SPI Flash behavior when brownout +# +CONFIG_SPI_FLASH_BROWNOUT_RESET_XMC=y +CONFIG_SPI_FLASH_BROWNOUT_RESET=y +# end of SPI Flash behavior when brownout + +# +# Optional and Experimental Features (READ DOCS FIRST) +# + +# +# Features here require specific hardware (READ DOCS FIRST!) +# +CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50 +# CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set +# CONFIG_SPI_FLASH_FORCE_ENABLE_C6_H2_SUSPEND is not set +CONFIG_SPI_FLASH_PLACE_FUNCTIONS_IN_IRAM=y +# end of Optional and Experimental Features (READ DOCS FIRST) +# end of Main Flash configuration + +# +# SPI Flash driver +# +# CONFIG_SPI_FLASH_VERIFY_WRITE is not set +# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set +# CONFIG_SPI_FLASH_SHARE_SPI1_BUS is not set +# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set +CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y +CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 +CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 +CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192 +# CONFIG_SPI_FLASH_SIZE_OVERRIDE is not set +# CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED is not set +# CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST is not set + +# +# Auto-detect flash chips +# +CONFIG_SPI_FLASH_VENDOR_XMC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_GD_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_ISSI_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_MXIC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_WINBOND_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP=y +# CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP is not set +# CONFIG_SPI_FLASH_SUPPORT_TH_CHIP is not set +# end of Auto-detect flash chips + +CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=y +# end of SPI Flash driver + +# +# LoRa common options +# +# CONFIG_LORA_ROLE_TX is not set +CONFIG_LORA_ROLE_RX=y +# end of LoRa common options + +# +# Joystick input +# +CONFIG_JOYSTICK_ADC_CHANNEL=6 +CONFIG_JOYSTICK_ADC_LEVEL_LEFT=600 +CONFIG_JOYSTICK_ADC_LEVEL_UP=1200 +CONFIG_JOYSTICK_ADC_LEVEL_PRESS=1900 +CONFIG_JOYSTICK_ADC_LEVEL_DOWN=2600 +CONFIG_JOYSTICK_ADC_LEVEL_RIGHT=3300 +CONFIG_JOYSTICK_ADC_TOLERANCE=150 +# end of Joystick input + +# +# LoRa radio (LR1121) +# +CONFIG_LORA_SPI_HOST=2 +CONFIG_LORA_PIN_CS=12 +CONFIG_LORA_PIN_MOSI=10 +CONFIG_LORA_PIN_MISO=9 +CONFIG_LORA_PIN_SCK=11 +CONFIG_LORA_PIN_RST=-1 +CONFIG_LORA_PIN_BUSY=13 +CONFIG_LORA_PIN_DIO1=-1 +CONFIG_LORA_FREQ_MHZ=868 +CONFIG_LORA_BW_KHZ=125 +CONFIG_LORA_SF=7 +CONFIG_LORA_CR=5 +# end of LoRa radio (LR1121) + +# +# Display +# +CONFIG_DISPLAY_DRIVER="ST7735" +CONFIG_DISPLAY_I2C_PORT=0 +CONFIG_DISPLAY_I2C_ADDR=60 +CONFIG_DISPLAY_PIN_SDA=7 +CONFIG_DISPLAY_PIN_RST=-1 +CONFIG_DISPLAY_PIN_SCL=8 +CONFIG_DISPLAY_WIDTH=128 +CONFIG_DISPLAY_HEIGHT=160 +# end of Display +# end of Component config + +# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set + +# Deprecated options for backward compatibility +# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set +# CONFIG_NO_BLOBS is not set +# CONFIG_ESP32_NO_BLOBS is not set +# CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set +# CONFIG_ESP32_COMPATIBLE_PRE_V3_1_BOOTLOADERS is not set +# CONFIG_APP_ROLLBACK_ENABLE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set +CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y +# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set +CONFIG_LOG_BOOTLOADER_LEVEL=3 +# CONFIG_FLASH_ENCRYPTION_ENABLED is not set +# CONFIG_FLASHMODE_QIO is not set +# CONFIG_FLASHMODE_QOUT is not set +CONFIG_FLASHMODE_DIO=y +# CONFIG_FLASHMODE_DOUT is not set +CONFIG_MONITOR_BAUD=115200 +CONFIG_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y +# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set +# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set +CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y +# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set +CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_CXX_EXCEPTIONS is not set +CONFIG_STACK_CHECK_NONE=y +# CONFIG_STACK_CHECK_NORM is not set +# CONFIG_STACK_CHECK_STRONG is not set +# CONFIG_STACK_CHECK_ALL is not set +# CONFIG_WARN_WRITE_STRINGS is not set +CONFIG_ADC2_DISABLE_DAC=y +# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set +# CONFIG_MCPWM_ISR_IRAM_SAFE is not set +# CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set +CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y +CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 +# CONFIG_ESP_SYSTEM_PD_FLASH is not set +CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y +CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y +# CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL is not set +# CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC is not set +# CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256 is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set +CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y +# CONFIG_ESP32_XTAL_FREQ_26 is not set +CONFIG_ESP32_XTAL_FREQ_40=y +# CONFIG_ESP32_XTAL_FREQ_AUTO is not set +CONFIG_ESP32_XTAL_FREQ=40 +CONFIG_BROWNOUT_DET=y +CONFIG_ESP32_BROWNOUT_DET=y +CONFIG_BROWNOUT_DET_LVL_SEL_0=y +CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_0=y +# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7 is not set +CONFIG_BROWNOUT_DET_LVL=0 +CONFIG_ESP32_BROWNOUT_DET_LVL=0 +CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y +# CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set +CONFIG_ESP32_DEFAULT_CPU_FREQ_160=y +# CONFIG_ESP32_DEFAULT_CPU_FREQ_240 is not set +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=160 +CONFIG_TRACEMEM_RESERVE_DRAM=0x0 +# CONFIG_ESP32_PANIC_PRINT_HALT is not set +CONFIG_ESP32_PANIC_PRINT_REBOOT=y +# CONFIG_ESP32_PANIC_SILENT_REBOOT is not set +CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_MAIN_TASK_STACK_SIZE=3584 +CONFIG_CONSOLE_UART_DEFAULT=y +# CONFIG_CONSOLE_UART_CUSTOM is not set +# CONFIG_CONSOLE_UART_NONE is not set +# CONFIG_ESP_CONSOLE_UART_NONE is not set +CONFIG_CONSOLE_UART=y +CONFIG_CONSOLE_UART_NUM=0 +CONFIG_CONSOLE_UART_BAUDRATE=115200 +CONFIG_INT_WDT=y +CONFIG_INT_WDT_TIMEOUT_MS=300 +CONFIG_INT_WDT_CHECK_CPU1=y +CONFIG_TASK_WDT=y +CONFIG_ESP_TASK_WDT=y +# CONFIG_TASK_WDT_PANIC is not set +CONFIG_TASK_WDT_TIMEOUT_S=5 +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP32_DEBUG_OCDAWARE=y +# CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set +CONFIG_IPC_TASK_STACK_SIZE=1024 +CONFIG_TIMER_TASK_STACK_SIZE=3584 +CONFIG_TIMER_TASK_PRIORITY=1 +CONFIG_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_TIMER_QUEUE_LENGTH=10 +# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set +# CONFIG_HAL_ASSERTION_SILIENT is not set +# CONFIG_NEWLIB_NANO_FORMAT is not set +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y +CONFIG_ESP32_TIME_SYSCALL_USE_RTC_HRT=y +CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y +# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_HRT is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set +CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_ESP32_PTHREAD_STACK_MIN=768 +CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set +CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set +# End of deprecated options diff --git a/apps/tx/CMakeLists.txt b/apps/tx/CMakeLists.txt new file mode 100644 index 0000000..a20c8dc --- /dev/null +++ b/apps/tx/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +idf_build_set_property(MINIMAL_BUILD ON) + +set(EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_LIST_DIR}/../../components") +set(COMPONENTS main lora_radio common input ui) + +project(lora-tx) diff --git a/apps/tx/dependencies.lock b/apps/tx/dependencies.lock new file mode 100644 index 0000000..e233d8d --- /dev/null +++ b/apps/tx/dependencies.lock @@ -0,0 +1,10 @@ +dependencies: + idf: + source: + type: idf + version: 5.5.1 +direct_dependencies: +- idf +manifest_hash: 9db7a265ef57175d265e0d6eb7847107508a68a5847e4adbd1375406a7c73c51 +target: esp32s3 +version: 2.0.0 diff --git a/apps/tx/main/CMakeLists.txt b/apps/tx/main/CMakeLists.txt new file mode 100644 index 0000000..06e6b92 --- /dev/null +++ b/apps/tx/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "tx_main.c" + INCLUDE_DIRS "." + REQUIRES common lora_radio input ui usb_api +) diff --git a/apps/tx/main/tx_main.c b/apps/tx/main/tx_main.c new file mode 100644 index 0000000..14e7720 --- /dev/null +++ b/apps/tx/main/tx_main.c @@ -0,0 +1,581 @@ +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_system.h" +#include "sdkconfig.h" +#include "soc/rtc_cntl_reg.h" + +#include "common.h" +#include "input.h" +#include "lora_radio.h" +#include "ui.h" +#include "usb_api.h" +#include +#include + +static const char *TAG = "lora_tx"; + +typedef enum { + FIELD_FREQ = 0, + FIELD_BW, + FIELD_SF, + FIELD_CR, + FIELD_TX_POWER, + FIELD_PERIOD, + FIELD_COUNT +} field_t; + +typedef enum { + SCREEN_FREQ = 0, + SCREEN_BAND, + SCREEN_BW, + SCREEN_SF, + SCREEN_CR, + SCREEN_POWER, + SCREEN_PAYLOAD, + SCREEN_PERIOD, + SCREEN_TX_ENABLE, + SCREEN_BOOT, + SCREEN_COUNT +} screen_t; + +static lora_params_t s_params; +static field_t s_field = FIELD_FREQ; +static screen_t s_screen = SCREEN_FREQ; +static const char *s_screen_icon[SCREEN_COUNT] = {"ƒ", "BND", "BW", "SF", "CR", "P", "PKT", "PRD", "ON", "BL"}; +static const char *s_screen_label[SCREEN_COUNT] = {"FREQ", "BAND", "BANDW", "SPREAD", "CODERATE", "POWER", "PAYLOAD", "PERIOD", "TX", "BOOT"}; + +static const int s_bw_options[] = {125, 250, 500}; +static const char *s_payload_options[] = { + "PING 1", "PING 2", "PING 3", "PING 4", "PING 5", + "PING 6", "PING 7", "PING 8", "PING 9", "PING 10" +}; +static int s_payload_idx = 0; +static char s_payload[32] = "PING 1"; +static int s_period_ms = 1000; +static int64_t s_last_tx_us = 0; +static bool s_tx_enabled = true; +typedef struct { + const char *name; + int min_cmhz; + int max_cmhz; + int default_cmhz; +} band_t; +static const band_t s_bands[] = { + {"430", 43000, 44000, 43300}, + {"868", 86300, 87000, 86800}, + {"915", 90200, 92800, 91500}, + {"L", 152500, 166000, 155000}, // 1.525-1.660 GHz + {"S", 190000, 210000, 200000}, // 1.9-2.1 GHz + {"2.4G", 240000, 248350, 244200}, // 2.4 GHz ISM (2400-2483.5 MHz) +}; +static int s_band_idx = 0; + +// --- JSON helpers for USB control ------------------------------------------------- + +static void sanitize_json_string(const char *in, char *out, size_t out_len) +{ + if (!out || out_len == 0) { + return; + } + size_t w = 0; + for (size_t i = 0; in && in[i] != '\0' && w + 1 < out_len; i++) { + char c = in[i]; + if (c == '\\' || c == '\"' || (unsigned char)c < 0x20) { + out[w++] = ' '; + } else { + out[w++] = c; + } + } + out[w] = '\0'; +} + +static bool parse_int_field(const char *json, const char *key, int *out) +{ + if (!json || !key || !out) { + return false; + } + char pattern[32]; + snprintf(pattern, sizeof(pattern), "\"%s\"", key); + const char *p = strstr(json, pattern); + if (!p) return false; + p = strchr(p, ':'); + if (!p) return false; + p++; + int val = 0; + if (sscanf(p, " %d", &val) == 1) { + *out = val; + return true; + } + return false; +} + +static bool parse_string_field(const char *json, const char *key, char *out, size_t out_len) +{ + if (!json || !key || !out || out_len == 0) { + return false; + } + char pattern[32]; + snprintf(pattern, sizeof(pattern), "\"%s\"", key); + const char *p = strstr(json, pattern); + if (!p) return false; + p = strchr(p, ':'); + if (!p) return false; + const char *q = strchr(p, '\"'); + if (!q) return false; + q++; // after quote + const char *end = strchr(q, '\"'); + if (!end) return false; + size_t len = (size_t)(end - q); + if (len >= out_len) len = out_len - 1; + memcpy(out, q, len); + out[len] = '\0'; + return true; +} + +static bool parse_bool_field(const char *json, const char *key, bool *out) +{ + if (!json || !key || !out) { + return false; + } + char pattern[32]; + snprintf(pattern, sizeof(pattern), "\"%s\"", key); + const char *p = strstr(json, pattern); + if (!p) return false; + p = strchr(p, ':'); + if (!p) return false; + p++; + if (strncmp(p, " true", 5) == 0 || strncmp(p, "true", 4) == 0 || strncmp(p, "1", 1) == 0) { + *out = true; + return true; + } + if (strncmp(p, " false", 6) == 0 || strncmp(p, "false", 5) == 0 || strncmp(p, "0", 1) == 0) { + *out = false; + return true; + } + return false; +} + +static int find_band_idx(const char *name) +{ + if (!name) return -1; + for (size_t i = 0; i < sizeof(s_bands)/sizeof(s_bands[0]); i++) { + if (strcasecmp(name, s_bands[i].name) == 0) { + return (int)i; + } + } + return -1; +} + +static void apply_payload_selection(void) +{ + if (s_payload_idx < 0) { + s_payload_idx = 0; + } + int max_idx = (int)(sizeof(s_payload_options) / sizeof(s_payload_options[0])) - 1; + if (s_payload_idx > max_idx) { + s_payload_idx = max_idx; + } + strlcpy(s_payload, s_payload_options[s_payload_idx], sizeof(s_payload)); +} + +static void change_payload(int delta) +{ + s_payload_idx += delta; + apply_payload_selection(); +} + +static int detect_band_from_freq(int cmhz) +{ + for (size_t i = 0; i < sizeof(s_bands) / sizeof(s_bands[0]); i++) { + if (cmhz >= s_bands[i].min_cmhz && cmhz <= s_bands[i].max_cmhz) { + return (int)i; + } + } + return 0; +} + +static void clamp_freq_to_band(void) +{ + const band_t *b = &s_bands[s_band_idx]; + if (s_params.freq_centi_mhz < b->min_cmhz) s_params.freq_centi_mhz = b->min_cmhz; + if (s_params.freq_centi_mhz > b->max_cmhz) s_params.freq_centi_mhz = b->max_cmhz; + s_params.freq_centi_mhz = ((s_params.freq_centi_mhz + 50) / 100) * 100; +} + +static void usb_send_status(void) +{ + lora_metrics_t metrics = {0}; + lora_radio_get_metrics(&metrics); + char line[256]; + snprintf(line, sizeof(line), + "{\"resp\":\"status\",\"role\":\"tx\",\"freq_mhz\":%d,\"bw_khz\":%d,\"sf\":%d,\"cr\":%d," + "\"band\":\"%s\",\"tx_power_dbm\":%d,\"period_ms\":%d,\"tx_enabled\":%s," + "\"snr_db\":%d,\"rssi_dbm\":%d,\"last_status\":%u}\n", + s_params.freq_centi_mhz / 100, + s_params.bw_khz, + s_params.sf, + s_params.cr, + s_bands[s_band_idx].name, + s_params.tx_power_dbm, + s_period_ms, + s_tx_enabled ? "true" : "false", + metrics.snr_db, + metrics.rssi_dbm, + (unsigned)metrics.last_status); + usb_api_send_line(line); +} + +static void usb_handle_set_params(const char *json) +{ + bool radio_changed = false; + lora_params_t next = s_params; + + int band_idx = -1; + char band_name[16] = {0}; + if (parse_string_field(json, "band", band_name, sizeof(band_name))) { + band_idx = find_band_idx(band_name); + if (band_idx >= 0) { + s_band_idx = band_idx; + radio_changed = true; + } + } + + int val = 0; + if (parse_int_field(json, "freq_mhz", &val)) { + next.freq_centi_mhz = val * 100; + radio_changed = true; + } + if (parse_int_field(json, "bw_khz", &val)) { + next.bw_khz = val; + radio_changed = true; + } + if (parse_int_field(json, "sf", &val)) { + next.sf = val; + radio_changed = true; + } + if (parse_int_field(json, "cr", &val)) { + next.cr = val; + radio_changed = true; + } + if (parse_int_field(json, "tx_power_dbm", &val)) { + next.tx_power_dbm = val; + radio_changed = true; + } + + bool ctrl_changed = false; + if (parse_int_field(json, "period_ms", &val)) { + s_period_ms = val; + if (s_period_ms < 100) s_period_ms = 100; + if (s_period_ms > 60000) s_period_ms = 60000; + ctrl_changed = true; + } + bool bval = false; + if (parse_bool_field(json, "tx_enabled", &bval)) { + s_tx_enabled = bval; + ctrl_changed = true; + } + char payload[64] = {0}; + if (parse_string_field(json, "payload", payload, sizeof(payload))) { + sanitize_json_string(payload, payload, sizeof(payload)); + strlcpy(s_payload, payload, sizeof(s_payload)); + ctrl_changed = true; + } + + if (radio_changed) { + s_params = next; + clamp_freq_to_band(); + esp_err_t err = lora_radio_apply_params(&s_params); + if (err != ESP_OK) { + ESP_LOGE(TAG, "USB set_params failed: %s", esp_err_to_name(err)); + } else { + ESP_LOGI(TAG, "USB set_params applied"); + } + } else if (!ctrl_changed) { + // nothing to do + return; + } +} + +static void usb_handle_line(const char *line) +{ + if (!line || line[0] == '\0') { + return; + } + ESP_LOGI(TAG, "USB RX: %s", line); + if (strstr(line, "set_params")) { + usb_handle_set_params(line); + return; + } + if (strstr(line, "get_status")) { + usb_send_status(); + return; + } + if (strstr(line, "reboot_bootloader")) { + ui_show_status("Bootloader", "Rebooting to USB", "Connect USB", NULL, NULL, NULL); + vTaskDelay(pdMS_TO_TICKS(200)); + REG_SET_BIT(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT); + esp_restart(); + } +} + +static void set_band(int idx) +{ + int max_idx = (int)(sizeof(s_bands) / sizeof(s_bands[0])) - 1; + if (idx < 0) idx = 0; + if (idx > max_idx) idx = max_idx; + s_band_idx = idx; + s_params.freq_centi_mhz = s_bands[s_band_idx].default_cmhz; + clamp_freq_to_band(); + esp_err_t err = lora_radio_apply_params(&s_params); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Apply params failed: %s", esp_err_to_name(err)); + } +} + +static void bump_field(int delta) +{ + switch (s_field) { + case FIELD_FREQ: + s_params.freq_centi_mhz += delta * 100; // крок 1 MHz + clamp_freq_to_band(); + break; + case FIELD_BW: { + int idx = 0; + for (size_t i = 0; i < sizeof(s_bw_options)/sizeof(s_bw_options[0]); i++) { + if (s_bw_options[i] == s_params.bw_khz) { + idx = i; + break; + } + } + idx += delta; + if (idx < 0) idx = 0; + if (idx >= (int)(sizeof(s_bw_options)/sizeof(s_bw_options[0]))) idx = (int)(sizeof(s_bw_options)/sizeof(s_bw_options[0])) - 1; + s_params.bw_khz = s_bw_options[idx]; + break; + } + case FIELD_SF: + s_params.sf += delta; + if (s_params.sf < 5) s_params.sf = 5; + if (s_params.sf > 12) s_params.sf = 12; + break; + case FIELD_CR: + s_params.cr += delta; + if (s_params.cr < 5) s_params.cr = 5; + if (s_params.cr > 8) s_params.cr = 8; + break; + case FIELD_TX_POWER: + s_params.tx_power_dbm += delta; + break; + case FIELD_PERIOD: + s_period_ms += delta * 100; + if (s_period_ms < 100) s_period_ms = 100; + if (s_period_ms > 60000) s_period_ms = 60000; + break; + default: + break; + } + esp_err_t err = lora_radio_apply_params(&s_params); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Apply params failed: %s", esp_err_to_name(err)); + } +} + +static void next_screen(int delta) +{ + int idx = (int)s_screen + delta; + if (idx < 0) idx = SCREEN_COUNT - 1; + if (idx >= SCREEN_COUNT) idx = 0; + s_screen = (screen_t)idx; + switch (s_screen) { + case SCREEN_FREQ: s_field = FIELD_FREQ; break; + case SCREEN_BAND: s_field = FIELD_FREQ; break; + case SCREEN_BW: s_field = FIELD_BW; break; + case SCREEN_SF: s_field = FIELD_SF; break; + case SCREEN_CR: s_field = FIELD_CR; break; + case SCREEN_POWER: s_field = FIELD_TX_POWER; break; + case SCREEN_PERIOD: s_field = FIELD_PERIOD; break; + case SCREEN_BOOT: s_field = FIELD_FREQ; break; + default: break; + } +} + +static void adjust_current(int delta) +{ + switch (s_screen) { + case SCREEN_FREQ: s_field = FIELD_FREQ; bump_field(delta); break; + case SCREEN_BAND: set_band(s_band_idx + delta); break; + case SCREEN_BW: s_field = FIELD_BW; bump_field(delta); break; + case SCREEN_SF: s_field = FIELD_SF; bump_field(delta); break; + case SCREEN_CR: s_field = FIELD_CR; bump_field(delta); break; + case SCREEN_POWER: s_field = FIELD_TX_POWER; bump_field(delta); break; + case SCREEN_PERIOD: s_field = FIELD_PERIOD; bump_field(delta); break; + case SCREEN_PAYLOAD: change_payload(delta); break; + case SCREEN_BOOT: break; + default: + break; + } +} + +static void app_loop(void) +{ + lora_metrics_t metrics = {0}; + while (true) { + input_event_t evt = input_poll(); + switch (evt) { + case INPUT_LEFT: next_screen(-1); break; + case INPUT_RIGHT: next_screen(1); break; + case INPUT_UP: + if (s_screen == SCREEN_TX_ENABLE) { + s_tx_enabled = true; + } else { + adjust_current(1); + } + break; + case INPUT_DOWN: + if (s_screen == SCREEN_TX_ENABLE) { + s_tx_enabled = false; + } else { + adjust_current(-1); + } + break; + case INPUT_CENTER: + if (s_screen == SCREEN_TX_ENABLE) { + s_tx_enabled = !s_tx_enabled; + } else if (s_screen == SCREEN_BOOT) { + ui_show_status("Bootloader", "Rebooting to USB", "Connect USB", NULL, NULL, NULL); + vTaskDelay(pdMS_TO_TICKS(200)); + REG_SET_BIT(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT); + esp_restart(); + } else { + bump_field(0); + } + break; + default: break; + } + + usb_api_tick(); + lora_radio_tick_tx(); + lora_radio_get_metrics(&metrics); + // Періодична передача + int64_t now = esp_timer_get_time(); + if (s_tx_enabled && now - s_last_tx_us >= (int64_t)s_period_ms * 1000) { + size_t len = strnlen(s_payload, sizeof(s_payload)); + if (len > 0) { + esp_err_t tx_err = lora_radio_send((const uint8_t *)s_payload, len); + if (tx_err == ESP_OK) { + s_last_tx_us = now; + ESP_LOGI(TAG, "TX \"%s\" (%u bytes)", s_payload, (unsigned)len); + } else { + ESP_LOGW(TAG, "TX failed: %s", esp_err_to_name(tx_err)); + s_last_tx_us = now; // уникнути спаму + } + } + } + char line1[32]; + char line2[32]; + char line3[32]; + char line4[32]; + char line5[32]; + char line6[32]; + memset(line1, 0, sizeof(line1)); + memset(line2, 0, sizeof(line2)); + memset(line3, 0, sizeof(line3)); + memset(line4, 0, sizeof(line4)); + memset(line5, 0, sizeof(line5)); + memset(line6, 0, sizeof(line6)); + + switch (s_screen) { + case SCREEN_FREQ: + snprintf(line1, sizeof(line1), "Freq: %d MHz", s_params.freq_centi_mhz / 100); + snprintf(line2, sizeof(line2), "UP/DN to change"); + break; + case SCREEN_BAND: { + const band_t *b = &s_bands[s_band_idx]; + snprintf(line1, sizeof(line1), "Band %s", b->name); + snprintf(line2, sizeof(line2), "%d-%d MHz", b->min_cmhz / 100, b->max_cmhz / 100); + snprintf(line3, sizeof(line3), "UP/DN to select"); + break; + } + case SCREEN_BW: + snprintf(line1, sizeof(line1), "BW: %d kHz", s_params.bw_khz); + snprintf(line2, sizeof(line2), "UP/DN to change"); + break; + case SCREEN_SF: + snprintf(line1, sizeof(line1), "SF: %d", s_params.sf); + snprintf(line2, sizeof(line2), "UP/DN to change"); + break; + case SCREEN_CR: + snprintf(line1, sizeof(line1), "CR: 4/%d", s_params.cr); + snprintf(line2, sizeof(line2), "UP/DN to change"); + break; + case SCREEN_POWER: + snprintf(line1, sizeof(line1), "PWR: %d dBm", s_params.tx_power_dbm); + snprintf(line2, sizeof(line2), "UP/DN to change"); + break; + case SCREEN_PAYLOAD: + snprintf(line1, sizeof(line1), "Payload:"); + snprintf(line2, sizeof(line2), "%.31s", s_payload); + snprintf(line3, sizeof(line3), "UP/DN pick PING"); + break; + case SCREEN_PERIOD: + snprintf(line1, sizeof(line1), "Period: %d ms", s_period_ms); + snprintf(line2, sizeof(line2), "UP/DN to change"); + break; + case SCREEN_TX_ENABLE: + snprintf(line1, sizeof(line1), "TX: %s", s_tx_enabled ? "ON" : "OFF"); + snprintf(line2, sizeof(line2), "UP=on DN=off CTR=tgl"); + break; + case SCREEN_BOOT: + snprintf(line1, sizeof(line1), "USB Bootloader"); + snprintf(line2, sizeof(line2), "CENTER to reboot"); + snprintf(line3, sizeof(line3), "Plug USB first"); + break; + default: + break; + } + + snprintf(line4, sizeof(line4), "SNR %ddB", metrics.snr_db); + snprintf(line5, sizeof(line5), "RSSI %ddBm ST 0x%02X", metrics.rssi_dbm, metrics.last_status); + + char header[32]; + snprintf(header, sizeof(header), "%s %s", s_screen_icon[s_screen], s_screen_label[s_screen]); + ui_show_role(header); + ui_show_status(line1, line2, line3, line4, line5, line6); + vTaskDelay(pdMS_TO_TICKS(500)); + } +} + +void app_main(void) +{ + common_print_boot_info(); + input_init(); + ui_init(); + ui_show_role("TX"); + bool radio_ok = lora_radio_init(true); + ui_show_status(radio_ok ? "LR1121 OK" : "LR1121 FAIL", "Ready", NULL, NULL, NULL, NULL); + ESP_LOGI(TAG, "LR1121 init: %s", radio_ok ? "OK" : "FAIL"); + s_params.freq_centi_mhz = CONFIG_LORA_FREQ_MHZ * 100; + s_params.bw_khz = CONFIG_LORA_BW_KHZ; + s_params.sf = CONFIG_LORA_SF; + s_params.cr = CONFIG_LORA_CR; + s_params.tx_power_dbm = 14; + s_params.preamble_syms = 8; + s_params.payload_len = 0; + s_params.crc_on = true; + s_params.iq_invert = false; + s_params.header_implicit = false; + apply_payload_selection(); + s_band_idx = detect_band_from_freq(s_params.freq_centi_mhz); + clamp_freq_to_band(); + esp_err_t err = lora_radio_apply_params(&s_params); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Initial params failed: %s", esp_err_to_name(err)); + } + usb_api_init(usb_handle_line); + app_loop(); +} diff --git a/apps/tx/sdkconfig b/apps/tx/sdkconfig new file mode 100644 index 0000000..c9d7f04 --- /dev/null +++ b/apps/tx/sdkconfig @@ -0,0 +1,1716 @@ +# +# Automatically generated file. DO NOT EDIT. +# Espressif IoT Development Framework (ESP-IDF) 5.5.1 Project Configuration +# +CONFIG_SOC_ADC_SUPPORTED=y +CONFIG_SOC_UART_SUPPORTED=y +CONFIG_SOC_PCNT_SUPPORTED=y +CONFIG_SOC_PHY_SUPPORTED=y +CONFIG_SOC_WIFI_SUPPORTED=y +CONFIG_SOC_TWAI_SUPPORTED=y +CONFIG_SOC_GDMA_SUPPORTED=y +CONFIG_SOC_UHCI_SUPPORTED=y +CONFIG_SOC_AHB_GDMA_SUPPORTED=y +CONFIG_SOC_GPTIMER_SUPPORTED=y +CONFIG_SOC_LCDCAM_SUPPORTED=y +CONFIG_SOC_LCDCAM_CAM_SUPPORTED=y +CONFIG_SOC_LCDCAM_I80_LCD_SUPPORTED=y +CONFIG_SOC_LCDCAM_RGB_LCD_SUPPORTED=y +CONFIG_SOC_MCPWM_SUPPORTED=y +CONFIG_SOC_DEDICATED_GPIO_SUPPORTED=y +CONFIG_SOC_CACHE_SUPPORT_WRAP=y +CONFIG_SOC_ULP_SUPPORTED=y +CONFIG_SOC_ULP_FSM_SUPPORTED=y +CONFIG_SOC_RISCV_COPROC_SUPPORTED=y +CONFIG_SOC_BT_SUPPORTED=y +CONFIG_SOC_USB_OTG_SUPPORTED=y +CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED=y +CONFIG_SOC_CCOMP_TIMER_SUPPORTED=y +CONFIG_SOC_ASYNC_MEMCPY_SUPPORTED=y +CONFIG_SOC_SUPPORTS_SECURE_DL_MODE=y +CONFIG_SOC_EFUSE_KEY_PURPOSE_FIELD=y +CONFIG_SOC_EFUSE_SUPPORTED=y +CONFIG_SOC_SDMMC_HOST_SUPPORTED=y +CONFIG_SOC_RTC_FAST_MEM_SUPPORTED=y +CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED=y +CONFIG_SOC_RTC_MEM_SUPPORTED=y +CONFIG_SOC_PSRAM_DMA_CAPABLE=y +CONFIG_SOC_XT_WDT_SUPPORTED=y +CONFIG_SOC_I2S_SUPPORTED=y +CONFIG_SOC_RMT_SUPPORTED=y +CONFIG_SOC_SDM_SUPPORTED=y +CONFIG_SOC_GPSPI_SUPPORTED=y +CONFIG_SOC_LEDC_SUPPORTED=y +CONFIG_SOC_I2C_SUPPORTED=y +CONFIG_SOC_SYSTIMER_SUPPORTED=y +CONFIG_SOC_SUPPORT_COEXISTENCE=y +CONFIG_SOC_TEMP_SENSOR_SUPPORTED=y +CONFIG_SOC_AES_SUPPORTED=y +CONFIG_SOC_MPI_SUPPORTED=y +CONFIG_SOC_SHA_SUPPORTED=y +CONFIG_SOC_HMAC_SUPPORTED=y +CONFIG_SOC_DIG_SIGN_SUPPORTED=y +CONFIG_SOC_FLASH_ENC_SUPPORTED=y +CONFIG_SOC_SECURE_BOOT_SUPPORTED=y +CONFIG_SOC_MEMPROT_SUPPORTED=y +CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y +CONFIG_SOC_BOD_SUPPORTED=y +CONFIG_SOC_CLK_TREE_SUPPORTED=y +CONFIG_SOC_MPU_SUPPORTED=y +CONFIG_SOC_WDT_SUPPORTED=y +CONFIG_SOC_SPI_FLASH_SUPPORTED=y +CONFIG_SOC_RNG_SUPPORTED=y +CONFIG_SOC_LIGHT_SLEEP_SUPPORTED=y +CONFIG_SOC_DEEP_SLEEP_SUPPORTED=y +CONFIG_SOC_LP_PERIPH_SHARE_INTERRUPT=y +CONFIG_SOC_PM_SUPPORTED=y +CONFIG_SOC_SIMD_INSTRUCTION_SUPPORTED=y +CONFIG_SOC_XTAL_SUPPORT_40M=y +CONFIG_SOC_APPCPU_HAS_CLOCK_GATING_BUG=y +CONFIG_SOC_ADC_RTC_CTRL_SUPPORTED=y +CONFIG_SOC_ADC_DIG_CTRL_SUPPORTED=y +CONFIG_SOC_ADC_ARBITER_SUPPORTED=y +CONFIG_SOC_ADC_DIG_IIR_FILTER_SUPPORTED=y +CONFIG_SOC_ADC_MONITOR_SUPPORTED=y +CONFIG_SOC_ADC_DMA_SUPPORTED=y +CONFIG_SOC_ADC_PERIPH_NUM=2 +CONFIG_SOC_ADC_MAX_CHANNEL_NUM=10 +CONFIG_SOC_ADC_ATTEN_NUM=4 +CONFIG_SOC_ADC_DIGI_CONTROLLER_NUM=2 +CONFIG_SOC_ADC_PATT_LEN_MAX=24 +CONFIG_SOC_ADC_DIGI_MIN_BITWIDTH=12 +CONFIG_SOC_ADC_DIGI_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_DIGI_RESULT_BYTES=4 +CONFIG_SOC_ADC_DIGI_DATA_BYTES_PER_CONV=4 +CONFIG_SOC_ADC_DIGI_IIR_FILTER_NUM=2 +CONFIG_SOC_ADC_DIGI_MONITOR_NUM=2 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH=83333 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW=611 +CONFIG_SOC_ADC_RTC_MIN_BITWIDTH=12 +CONFIG_SOC_ADC_RTC_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_CALIBRATION_V1_SUPPORTED=y +CONFIG_SOC_ADC_SELF_HW_CALI_SUPPORTED=y +CONFIG_SOC_ADC_SHARED_POWER=y +CONFIG_SOC_APB_BACKUP_DMA=y +CONFIG_SOC_BROWNOUT_RESET_SUPPORTED=y +CONFIG_SOC_CACHE_WRITEBACK_SUPPORTED=y +CONFIG_SOC_CACHE_FREEZE_SUPPORTED=y +CONFIG_SOC_CACHE_ACS_INVALID_STATE_ON_PANIC=y +CONFIG_SOC_CPU_CORES_NUM=2 +CONFIG_SOC_CPU_INTR_NUM=32 +CONFIG_SOC_CPU_HAS_FPU=y +CONFIG_SOC_HP_CPU_HAS_MULTIPLE_CORES=y +CONFIG_SOC_CPU_BREAKPOINTS_NUM=2 +CONFIG_SOC_CPU_WATCHPOINTS_NUM=2 +CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=0x40 +CONFIG_SOC_SIMD_PREFERRED_DATA_ALIGNMENT=16 +CONFIG_SOC_DS_SIGNATURE_MAX_BIT_LEN=4096 +CONFIG_SOC_DS_KEY_PARAM_MD_IV_LENGTH=16 +CONFIG_SOC_DS_KEY_CHECK_MAX_WAIT_US=1100 +CONFIG_SOC_AHB_GDMA_VERSION=1 +CONFIG_SOC_GDMA_NUM_GROUPS_MAX=1 +CONFIG_SOC_GDMA_PAIRS_PER_GROUP=5 +CONFIG_SOC_GDMA_PAIRS_PER_GROUP_MAX=5 +CONFIG_SOC_AHB_GDMA_SUPPORT_PSRAM=y +CONFIG_SOC_GPIO_PORT=1 +CONFIG_SOC_GPIO_PIN_COUNT=49 +CONFIG_SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER=y +CONFIG_SOC_GPIO_FILTER_CLK_SUPPORT_APB=y +CONFIG_SOC_GPIO_SUPPORT_RTC_INDEPENDENT=y +CONFIG_SOC_GPIO_SUPPORT_FORCE_HOLD=y +CONFIG_SOC_GPIO_VALID_GPIO_MASK=0x1FFFFFFFFFFFF +CONFIG_SOC_GPIO_IN_RANGE_MAX=48 +CONFIG_SOC_GPIO_OUT_RANGE_MAX=48 +CONFIG_SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK=0x0001FFFFFC000000 +CONFIG_SOC_GPIO_CLOCKOUT_BY_IO_MUX=y +CONFIG_SOC_GPIO_CLOCKOUT_CHANNEL_NUM=3 +CONFIG_SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP=y +CONFIG_SOC_DEDIC_GPIO_OUT_CHANNELS_NUM=8 +CONFIG_SOC_DEDIC_GPIO_IN_CHANNELS_NUM=8 +CONFIG_SOC_DEDIC_GPIO_OUT_AUTO_ENABLE=y +CONFIG_SOC_I2C_NUM=2 +CONFIG_SOC_HP_I2C_NUM=2 +CONFIG_SOC_I2C_FIFO_LEN=32 +CONFIG_SOC_I2C_CMD_REG_NUM=8 +CONFIG_SOC_I2C_SUPPORT_SLAVE=y +CONFIG_SOC_I2C_SUPPORT_HW_CLR_BUS=y +CONFIG_SOC_I2C_SUPPORT_XTAL=y +CONFIG_SOC_I2C_SUPPORT_RTC=y +CONFIG_SOC_I2C_SUPPORT_10BIT_ADDR=y +CONFIG_SOC_I2C_SLAVE_SUPPORT_BROADCAST=y +CONFIG_SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS=y +CONFIG_SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE=y +CONFIG_SOC_I2S_NUM=2 +CONFIG_SOC_I2S_HW_VERSION_2=y +CONFIG_SOC_I2S_SUPPORTS_XTAL=y +CONFIG_SOC_I2S_SUPPORTS_PLL_F160M=y +CONFIG_SOC_I2S_SUPPORTS_PCM=y +CONFIG_SOC_I2S_SUPPORTS_PDM=y +CONFIG_SOC_I2S_SUPPORTS_PDM_TX=y +CONFIG_SOC_I2S_SUPPORTS_PCM2PDM=y +CONFIG_SOC_I2S_SUPPORTS_PDM_RX=y +CONFIG_SOC_I2S_SUPPORTS_PDM2PCM=y +CONFIG_SOC_I2S_PDM_MAX_TX_LINES=2 +CONFIG_SOC_I2S_PDM_MAX_RX_LINES=4 +CONFIG_SOC_I2S_SUPPORTS_TDM=y +CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y +CONFIG_SOC_LEDC_SUPPORT_XTAL_CLOCK=y +CONFIG_SOC_LEDC_TIMER_NUM=4 +CONFIG_SOC_LEDC_CHANNEL_NUM=8 +CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=14 +CONFIG_SOC_LEDC_SUPPORT_FADE_STOP=y +CONFIG_SOC_MCPWM_GROUPS=2 +CONFIG_SOC_MCPWM_TIMERS_PER_GROUP=3 +CONFIG_SOC_MCPWM_OPERATORS_PER_GROUP=3 +CONFIG_SOC_MCPWM_COMPARATORS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_GENERATORS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_TRIGGERS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_GPIO_FAULTS_PER_GROUP=3 +CONFIG_SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP=y +CONFIG_SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER=3 +CONFIG_SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP=3 +CONFIG_SOC_MCPWM_SWSYNC_CAN_PROPAGATE=y +CONFIG_SOC_MMU_LINEAR_ADDRESS_REGION_NUM=1 +CONFIG_SOC_MMU_PERIPH_NUM=1 +CONFIG_SOC_MPU_MIN_REGION_SIZE=0x20000000 +CONFIG_SOC_MPU_REGIONS_MAX_NUM=8 +CONFIG_SOC_PCNT_GROUPS=1 +CONFIG_SOC_PCNT_UNITS_PER_GROUP=4 +CONFIG_SOC_PCNT_CHANNELS_PER_UNIT=2 +CONFIG_SOC_PCNT_THRES_POINT_PER_UNIT=2 +CONFIG_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 +CONFIG_SOC_LCD_I80_SUPPORTED=y +CONFIG_SOC_LCD_RGB_SUPPORTED=y +CONFIG_SOC_LCD_I80_BUSES=1 +CONFIG_SOC_LCD_RGB_PANELS=1 +CONFIG_SOC_LCD_I80_BUS_WIDTH=16 +CONFIG_SOC_LCD_RGB_DATA_WIDTH=16 +CONFIG_SOC_LCD_SUPPORT_RGB_YUV_CONV=y +CONFIG_SOC_LCDCAM_I80_NUM_BUSES=1 +CONFIG_SOC_LCDCAM_I80_BUS_WIDTH=16 +CONFIG_SOC_LCDCAM_RGB_NUM_PANELS=1 +CONFIG_SOC_LCDCAM_RGB_DATA_WIDTH=16 +CONFIG_SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH=128 +CONFIG_SOC_RTC_CNTL_CPU_PD_REG_FILE_NUM=549 +CONFIG_SOC_RTC_CNTL_TAGMEM_PD_DMA_BUS_WIDTH=128 +CONFIG_SOC_RTCIO_PIN_COUNT=22 +CONFIG_SOC_RTCIO_INPUT_OUTPUT_SUPPORTED=y +CONFIG_SOC_RTCIO_HOLD_SUPPORTED=y +CONFIG_SOC_RTCIO_WAKE_SUPPORTED=y +CONFIG_SOC_LP_IO_CLOCK_IS_INDEPENDENT=y +CONFIG_SOC_SDM_GROUPS=1 +CONFIG_SOC_SDM_CHANNELS_PER_GROUP=8 +CONFIG_SOC_SDM_CLK_SUPPORT_APB=y +CONFIG_SOC_SPI_PERIPH_NUM=3 +CONFIG_SOC_SPI_MAX_CS_NUM=6 +CONFIG_SOC_SPI_MAXIMUM_BUFFER_SIZE=64 +CONFIG_SOC_SPI_SUPPORT_DDRCLK=y +CONFIG_SOC_SPI_SLAVE_SUPPORT_SEG_TRANS=y +CONFIG_SOC_SPI_SUPPORT_CD_SIG=y +CONFIG_SOC_SPI_SUPPORT_CONTINUOUS_TRANS=y +CONFIG_SOC_SPI_SUPPORT_SLAVE_HD_VER2=y +CONFIG_SOC_SPI_SUPPORT_CLK_APB=y +CONFIG_SOC_SPI_SUPPORT_CLK_XTAL=y +CONFIG_SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT=y +CONFIG_SOC_MEMSPI_IS_INDEPENDENT=y +CONFIG_SOC_SPI_MAX_PRE_DIVIDER=16 +CONFIG_SOC_SPI_SUPPORT_OCT=y +CONFIG_SOC_SPI_SCT_SUPPORTED=y +CONFIG_SOC_SPI_SCT_REG_NUM=14 +CONFIG_SOC_SPI_SCT_BUFFER_NUM_MAX=y +CONFIG_SOC_SPI_SCT_CONF_BITLEN_MAX=0x3FFFA +CONFIG_SOC_MEMSPI_SRC_FREQ_120M_SUPPORTED=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 +CONFIG_SOC_SPIRAM_XIP_SUPPORTED=y +CONFIG_SOC_SYSTIMER_COUNTER_NUM=2 +CONFIG_SOC_SYSTIMER_ALARM_NUM=3 +CONFIG_SOC_SYSTIMER_BIT_WIDTH_LO=32 +CONFIG_SOC_SYSTIMER_BIT_WIDTH_HI=20 +CONFIG_SOC_SYSTIMER_FIXED_DIVIDER=y +CONFIG_SOC_SYSTIMER_INT_LEVEL=y +CONFIG_SOC_SYSTIMER_ALARM_MISS_COMPENSATE=y +CONFIG_SOC_TIMER_GROUPS=2 +CONFIG_SOC_TIMER_GROUP_TIMERS_PER_GROUP=2 +CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=54 +CONFIG_SOC_TIMER_GROUP_SUPPORT_XTAL=y +CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y +CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=4 +CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32 +CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16 +CONFIG_SOC_TOUCH_SENSOR_VERSION=2 +CONFIG_SOC_TOUCH_SENSOR_NUM=15 +CONFIG_SOC_TOUCH_MIN_CHAN_ID=1 +CONFIG_SOC_TOUCH_MAX_CHAN_ID=14 +CONFIG_SOC_TOUCH_SUPPORT_BENCHMARK=y +CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y +CONFIG_SOC_TOUCH_SUPPORT_WATERPROOF=y +CONFIG_SOC_TOUCH_SUPPORT_PROX_SENSING=y +CONFIG_SOC_TOUCH_SUPPORT_DENOISE_CHAN=y +CONFIG_SOC_TOUCH_PROXIMITY_CHANNEL_NUM=3 +CONFIG_SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED=y +CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1 +CONFIG_SOC_TWAI_CONTROLLER_NUM=1 +CONFIG_SOC_TWAI_MASK_FILTER_NUM=1 +CONFIG_SOC_TWAI_CLK_SUPPORT_APB=y +CONFIG_SOC_TWAI_BRP_MIN=2 +CONFIG_SOC_TWAI_BRP_MAX=16384 +CONFIG_SOC_TWAI_SUPPORTS_RX_STATUS=y +CONFIG_SOC_UART_NUM=3 +CONFIG_SOC_UART_HP_NUM=3 +CONFIG_SOC_UART_FIFO_LEN=128 +CONFIG_SOC_UART_BITRATE_MAX=5000000 +CONFIG_SOC_UART_SUPPORT_FSM_TX_WAIT_SEND=y +CONFIG_SOC_UART_SUPPORT_WAKEUP_INT=y +CONFIG_SOC_UART_SUPPORT_APB_CLK=y +CONFIG_SOC_UART_SUPPORT_RTC_CLK=y +CONFIG_SOC_UART_SUPPORT_XTAL_CLK=y +CONFIG_SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE=y +CONFIG_SOC_UHCI_NUM=1 +CONFIG_SOC_USB_OTG_PERIPH_NUM=1 +CONFIG_SOC_SHA_DMA_MAX_BUFFER_SIZE=3968 +CONFIG_SOC_SHA_SUPPORT_DMA=y +CONFIG_SOC_SHA_SUPPORT_RESUME=y +CONFIG_SOC_SHA_GDMA=y +CONFIG_SOC_SHA_SUPPORT_SHA1=y +CONFIG_SOC_SHA_SUPPORT_SHA224=y +CONFIG_SOC_SHA_SUPPORT_SHA256=y +CONFIG_SOC_SHA_SUPPORT_SHA384=y +CONFIG_SOC_SHA_SUPPORT_SHA512=y +CONFIG_SOC_SHA_SUPPORT_SHA512_224=y +CONFIG_SOC_SHA_SUPPORT_SHA512_256=y +CONFIG_SOC_SHA_SUPPORT_SHA512_T=y +CONFIG_SOC_MPI_MEM_BLOCKS_NUM=4 +CONFIG_SOC_MPI_OPERATIONS_NUM=3 +CONFIG_SOC_RSA_MAX_BIT_LEN=4096 +CONFIG_SOC_AES_SUPPORT_DMA=y +CONFIG_SOC_AES_GDMA=y +CONFIG_SOC_AES_SUPPORT_AES_128=y +CONFIG_SOC_AES_SUPPORT_AES_256=y +CONFIG_SOC_PM_SUPPORT_EXT0_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_EXT1_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_EXT_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_WIFI_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_BT_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_CPU_PD=y +CONFIG_SOC_PM_SUPPORT_TAGMEM_PD=y +CONFIG_SOC_PM_SUPPORT_RTC_PERIPH_PD=y +CONFIG_SOC_PM_SUPPORT_RC_FAST_PD=y +CONFIG_SOC_PM_SUPPORT_VDDSDIO_PD=y +CONFIG_SOC_PM_SUPPORT_MAC_BB_PD=y +CONFIG_SOC_PM_SUPPORT_MODEM_PD=y +CONFIG_SOC_CONFIGURABLE_VDDSDIO_SUPPORTED=y +CONFIG_SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY=y +CONFIG_SOC_PM_CPU_RETENTION_BY_RTCCNTL=y +CONFIG_SOC_PM_MODEM_RETENTION_BY_BACKUPDMA=y +CONFIG_SOC_PM_MODEM_PD_BY_SW=y +CONFIG_SOC_CLK_RC_FAST_D256_SUPPORTED=y +CONFIG_SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256=y +CONFIG_SOC_CLK_RC_FAST_SUPPORT_CALIBRATION=y +CONFIG_SOC_CLK_XTAL32K_SUPPORTED=y +CONFIG_SOC_CLK_LP_FAST_SUPPORT_XTAL_D2=y +CONFIG_SOC_EFUSE_DIS_DOWNLOAD_ICACHE=y +CONFIG_SOC_EFUSE_DIS_DOWNLOAD_DCACHE=y +CONFIG_SOC_EFUSE_HARD_DIS_JTAG=y +CONFIG_SOC_EFUSE_DIS_USB_JTAG=y +CONFIG_SOC_EFUSE_SOFT_DIS_JTAG=y +CONFIG_SOC_EFUSE_DIS_DIRECT_BOOT=y +CONFIG_SOC_EFUSE_DIS_ICACHE=y +CONFIG_SOC_EFUSE_BLOCK9_KEY_PURPOSE_QUIRK=y +CONFIG_SOC_SECURE_BOOT_V2_RSA=y +CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS=3 +CONFIG_SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS=y +CONFIG_SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY=y +CONFIG_SOC_FLASH_ENCRYPTED_XTS_AES_BLOCK_MAX=64 +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES=y +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_OPTIONS=y +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_128=y +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_256=y +CONFIG_SOC_MEMPROT_CPU_PREFETCH_PAD_SIZE=16 +CONFIG_SOC_MEMPROT_MEM_ALIGN_SIZE=256 +CONFIG_SOC_PHY_DIG_REGS_MEM_SIZE=21 +CONFIG_SOC_MAC_BB_PD_MEM_SIZE=192 +CONFIG_SOC_WIFI_LIGHT_SLEEP_CLK_WIDTH=12 +CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE=y +CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND=y +CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_RESUME=y +CONFIG_SOC_SPI_MEM_SUPPORT_SW_SUSPEND=y +CONFIG_SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE=y +CONFIG_SOC_SPI_MEM_SUPPORT_TIMING_TUNING=y +CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y +CONFIG_SOC_SPI_MEM_SUPPORT_WRAP=y +CONFIG_SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY=y +CONFIG_SOC_MEMSPI_CORE_CLK_SHARED_WITH_PSRAM=y +CONFIG_SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP=y +CONFIG_SOC_COEX_HW_PTI=y +CONFIG_SOC_EXTERNAL_COEX_LEADER_TX_LINE=y +CONFIG_SOC_SDMMC_USE_GPIO_MATRIX=y +CONFIG_SOC_SDMMC_NUM_SLOTS=2 +CONFIG_SOC_SDMMC_SUPPORT_XTAL_CLOCK=y +CONFIG_SOC_SDMMC_DELAY_PHASE_NUM=4 +CONFIG_SOC_TEMPERATURE_SENSOR_SUPPORT_FAST_RC=y +CONFIG_SOC_WIFI_HW_TSF=y +CONFIG_SOC_WIFI_FTM_SUPPORT=y +CONFIG_SOC_WIFI_GCMP_SUPPORT=y +CONFIG_SOC_WIFI_WAPI_SUPPORT=y +CONFIG_SOC_WIFI_CSI_SUPPORT=y +CONFIG_SOC_WIFI_MESH_SUPPORT=y +CONFIG_SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW=y +CONFIG_SOC_WIFI_PHY_NEEDS_USB_WORKAROUND=y +CONFIG_SOC_BLE_SUPPORTED=y +CONFIG_SOC_BLE_MESH_SUPPORTED=y +CONFIG_SOC_BLE_50_SUPPORTED=y +CONFIG_SOC_BLE_DEVICE_PRIVACY_SUPPORTED=y +CONFIG_SOC_BLUFI_SUPPORTED=y +CONFIG_SOC_ULP_HAS_ADC=y +CONFIG_SOC_PHY_COMBO_MODULE=y +CONFIG_SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV=y +CONFIG_SOC_LCDCAM_CAM_PERIPH_NUM=1 +CONFIG_SOC_LCDCAM_CAM_DATA_WIDTH_MAX=16 +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TOOLCHAIN="gcc" +CONFIG_IDF_TOOLCHAIN_GCC=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET_ARCH="xtensa" +CONFIG_IDF_TARGET="esp32s3" +CONFIG_IDF_INIT_VERSION="5.5.1" +CONFIG_IDF_TARGET_ESP32S3=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009 + +# +# Build type +# +CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y +# CONFIG_APP_BUILD_TYPE_RAM is not set +CONFIG_APP_BUILD_GENERATE_BINARIES=y +CONFIG_APP_BUILD_BOOTLOADER=y +CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y +# CONFIG_APP_REPRODUCIBLE_BUILD is not set +# CONFIG_APP_NO_BLOBS is not set +# end of Build type + +# +# Bootloader config +# + +# +# Bootloader manager +# +CONFIG_BOOTLOADER_COMPILE_TIME_DATE=y +CONFIG_BOOTLOADER_PROJECT_VER=1 +# end of Bootloader manager + +# +# Application Rollback +# +# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set +# end of Application Rollback + +# +# Recovery Bootloader and Rollback +# +# end of Recovery Bootloader and Rollback + +CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x0 +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set + +# +# Log +# +CONFIG_BOOTLOADER_LOG_VERSION_1=y +CONFIG_BOOTLOADER_LOG_VERSION=1 +# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set +CONFIG_BOOTLOADER_LOG_LEVEL=3 + +# +# Format +# +# CONFIG_BOOTLOADER_LOG_COLORS is not set +CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y +# end of Format + +# +# Settings +# +CONFIG_BOOTLOADER_LOG_MODE_TEXT_EN=y +CONFIG_BOOTLOADER_LOG_MODE_TEXT=y +# end of Settings +# end of Log + +# +# Serial Flash Configurations +# +# CONFIG_BOOTLOADER_FLASH_DC_AWARE is not set +CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=y +# end of Serial Flash Configurations + +CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y +# CONFIG_BOOTLOADER_FACTORY_RESET is not set +# CONFIG_BOOTLOADER_APP_TEST is not set +CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y +CONFIG_BOOTLOADER_WDT_ENABLE=y +# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set +CONFIG_BOOTLOADER_WDT_TIME_MS=9000 +# CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set +CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0 +# CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set +# end of Bootloader config + +# +# Security features +# +CONFIG_SECURE_BOOT_V2_RSA_SUPPORTED=y +CONFIG_SECURE_BOOT_V2_PREFERRED=y +# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set +# CONFIG_SECURE_BOOT is not set +# CONFIG_SECURE_FLASH_ENC_ENABLED is not set +CONFIG_SECURE_ROM_DL_MODE_ENABLED=y +# end of Security features + +# +# Application manager +# +CONFIG_APP_COMPILE_TIME_DATE=y +# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set +# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set +# CONFIG_APP_PROJECT_VER_FROM_CONFIG is not set +CONFIG_APP_RETRIEVE_LEN_ELF_SHA=9 +# end of Application manager + +CONFIG_ESP_ROM_HAS_CRC_LE=y +CONFIG_ESP_ROM_HAS_CRC_BE=y +CONFIG_ESP_ROM_HAS_MZ_CRC32=y +CONFIG_ESP_ROM_HAS_JPEG_DECODE=y +CONFIG_ESP_ROM_UART_CLK_IS_XTAL=y +CONFIG_ESP_ROM_HAS_RETARGETABLE_LOCKING=y +CONFIG_ESP_ROM_USB_OTG_NUM=3 +CONFIG_ESP_ROM_USB_SERIAL_DEVICE_NUM=4 +CONFIG_ESP_ROM_HAS_ERASE_0_REGION_BUG=y +CONFIG_ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV=y +CONFIG_ESP_ROM_GET_CLK_FREQ=y +CONFIG_ESP_ROM_HAS_HAL_WDT=y +CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y +CONFIG_ESP_ROM_HAS_LAYOUT_TABLE=y +CONFIG_ESP_ROM_HAS_SPI_FLASH=y +CONFIG_ESP_ROM_HAS_SPI_FLASH_MMAP=y +CONFIG_ESP_ROM_HAS_ETS_PRINTF_BUG=y +CONFIG_ESP_ROM_HAS_NEWLIB=y +CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y +CONFIG_ESP_ROM_HAS_NEWLIB_32BIT_TIME=y +CONFIG_ESP_ROM_NEEDS_SET_CACHE_MMU_SIZE=y +CONFIG_ESP_ROM_RAM_APP_NEEDS_MMU_INIT=y +CONFIG_ESP_ROM_HAS_FLASH_COUNT_PAGES_BUG=y +CONFIG_ESP_ROM_HAS_CACHE_SUSPEND_WAITI_BUG=y +CONFIG_ESP_ROM_HAS_CACHE_WRITEBACK_BUG=y +CONFIG_ESP_ROM_HAS_SW_FLOAT=y +CONFIG_ESP_ROM_HAS_VERSION=y +CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y +CONFIG_ESP_ROM_HAS_OUTPUT_PUTC_FUNC=y +CONFIG_ESP_ROM_CONSOLE_OUTPUT_SECONDARY=y + +# +# Boot ROM Behavior +# +CONFIG_BOOT_ROM_LOG_ALWAYS_ON=y +# CONFIG_BOOT_ROM_LOG_ALWAYS_OFF is not set +# CONFIG_BOOT_ROM_LOG_ON_GPIO_HIGH is not set +# CONFIG_BOOT_ROM_LOG_ON_GPIO_LOW is not set +# end of Boot ROM Behavior + +# +# 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_FLASHMODE_QIO is not set +# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE="dio" +# CONFIG_ESPTOOLPY_FLASHFREQ_120M is not set +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +# CONFIG_ESPTOOLPY_FLASHFREQ_40M is not set +# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE="2MB" +# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set +CONFIG_ESPTOOLPY_BEFORE_RESET=y +# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +# CONFIG_ESPTOOLPY_AFTER_NORESET is not set +CONFIG_ESPTOOLPY_AFTER="hard_reset" +CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 +# end of Serial flasher config + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_SINGLE_APP=y +# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set +# CONFIG_PARTITION_TABLE_TWO_OTA is not set +# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set +# CONFIG_PARTITION_TABLE_CUSTOM is not set +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# Compiler options +# +CONFIG_COMPILER_OPTIMIZATION_DEBUG=y +# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set +# CONFIG_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_COMPILER_OPTIMIZATION_NONE is not set +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set +CONFIG_COMPILER_ASSERT_NDEBUG_EVALUATE=y +CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set +CONFIG_COMPILER_HIDE_PATHS_MACROS=y +# CONFIG_COMPILER_CXX_EXCEPTIONS is not set +# CONFIG_COMPILER_CXX_RTTI is not set +CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y +# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set +# CONFIG_COMPILER_NO_MERGE_CONSTANTS is not set +# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set +CONFIG_COMPILER_DISABLE_DEFAULT_ERRORS=y +# CONFIG_COMPILER_DISABLE_GCC12_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC13_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC14_WARNINGS is not set +# CONFIG_COMPILER_DUMP_RTL_FILES is not set +CONFIG_COMPILER_RT_LIB_GCCLIB=y +CONFIG_COMPILER_RT_LIB_NAME="gcc" +CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=y +# CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE is not set +# CONFIG_COMPILER_STATIC_ANALYZER is not set +# end of Compiler options + +# +# Component config +# + +# +# !!! MINIMAL_BUILD is enabled !!! +# + +# +# Only common components and those transitively required by the main component are listed +# + +# +# If a component configuration is missing, please add it to the main component's requirements +# + +# +# Driver Configurations +# + +# +# Legacy TWAI Driver Configurations +# +# CONFIG_TWAI_SKIP_LEGACY_CONFLICT_CHECK is not set +CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y +# end of Legacy TWAI Driver Configurations + +# +# Legacy ADC Driver Configuration +# +# CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_ADC_SKIP_LEGACY_CONFLICT_CHECK is not set + +# +# Legacy ADC Calibration Configuration +# +# CONFIG_ADC_CALI_SUPPRESS_DEPRECATE_WARN is not set +# end of Legacy ADC Calibration Configuration +# end of Legacy ADC Driver Configuration + +# +# Legacy MCPWM Driver Configurations +# +# CONFIG_MCPWM_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_MCPWM_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy MCPWM Driver Configurations + +# +# Legacy Timer Group Driver Configurations +# +# CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_GPTIMER_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Timer Group Driver Configurations + +# +# Legacy RMT Driver Configurations +# +# CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_RMT_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy RMT Driver Configurations + +# +# Legacy I2S Driver Configurations +# +# CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy I2S Driver Configurations + +# +# Legacy I2C Driver Configurations +# +# CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy I2C Driver Configurations + +# +# Legacy PCNT Driver Configurations +# +# CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_PCNT_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy PCNT Driver Configurations + +# +# Legacy SDM Driver Configurations +# +# CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_SDM_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy SDM Driver Configurations + +# +# Legacy Temperature Sensor Driver Configurations +# +# CONFIG_TEMP_SENSOR_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_TEMP_SENSOR_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Temperature Sensor Driver Configurations + +# +# Legacy Touch Sensor Driver Configurations +# +# CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_TOUCH_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Touch Sensor Driver Configurations +# end of Driver Configurations + +# +# eFuse Bit Manager +# +# CONFIG_EFUSE_CUSTOM_TABLE is not set +# CONFIG_EFUSE_VIRTUAL is not set +CONFIG_EFUSE_MAX_BLK_LEN=256 +# end of eFuse Bit Manager + +# +# ADC and ADC Calibration +# +# CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM is not set +# CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE is not set +# CONFIG_ADC_CONTINUOUS_FORCE_USE_ADC2_ON_C3_S3 is not set +# CONFIG_ADC_ENABLE_DEBUG_LOG is not set +# end of ADC and ADC Calibration + +# +# Common ESP-related +# +CONFIG_ESP_ERR_TO_NAME_LOOKUP=y +# end of Common ESP-related + +# +# ESP-Driver:GPIO Configurations +# +# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:GPIO Configurations + +# +# ESP-Driver:GPTimer Configurations +# +CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y +# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set +# CONFIG_GPTIMER_ISR_CACHE_SAFE is not set +CONFIG_GPTIMER_OBJ_CACHE_SAFE=y +# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:GPTimer Configurations + +# +# ESP-Driver:I2C Configurations +# +# CONFIG_I2C_ISR_IRAM_SAFE is not set +# CONFIG_I2C_ENABLE_DEBUG_LOG is not set +# CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2 is not set +CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y +# end of ESP-Driver:I2C Configurations + +# +# ESP-Driver:I2S Configurations +# +# CONFIG_I2S_ISR_IRAM_SAFE is not set +# CONFIG_I2S_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:I2S Configurations + +# +# ESP-Driver:LEDC Configurations +# +# CONFIG_LEDC_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:LEDC Configurations + +# +# ESP-Driver:MCPWM Configurations +# +CONFIG_MCPWM_ISR_HANDLER_IN_IRAM=y +# CONFIG_MCPWM_ISR_CACHE_SAFE is not set +# CONFIG_MCPWM_CTRL_FUNC_IN_IRAM is not set +CONFIG_MCPWM_OBJ_CACHE_SAFE=y +# CONFIG_MCPWM_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:MCPWM Configurations + +# +# ESP-Driver:PCNT Configurations +# +# CONFIG_PCNT_CTRL_FUNC_IN_IRAM is not set +# CONFIG_PCNT_ISR_IRAM_SAFE is not set +# CONFIG_PCNT_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:PCNT Configurations + +# +# ESP-Driver:RMT Configurations +# +CONFIG_RMT_ENCODER_FUNC_IN_IRAM=y +CONFIG_RMT_TX_ISR_HANDLER_IN_IRAM=y +CONFIG_RMT_RX_ISR_HANDLER_IN_IRAM=y +# CONFIG_RMT_RECV_FUNC_IN_IRAM is not set +# CONFIG_RMT_TX_ISR_CACHE_SAFE is not set +# CONFIG_RMT_RX_ISR_CACHE_SAFE is not set +CONFIG_RMT_OBJ_CACHE_SAFE=y +# CONFIG_RMT_ENABLE_DEBUG_LOG is not set +# CONFIG_RMT_ISR_IRAM_SAFE is not set +# end of ESP-Driver:RMT Configurations + +# +# ESP-Driver:Sigma Delta Modulator Configurations +# +# CONFIG_SDM_CTRL_FUNC_IN_IRAM is not set +# CONFIG_SDM_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:Sigma Delta Modulator Configurations + +# +# ESP-Driver:SPI Configurations +# +# CONFIG_SPI_MASTER_IN_IRAM is not set +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +# CONFIG_SPI_SLAVE_IN_IRAM is not set +CONFIG_SPI_SLAVE_ISR_IN_IRAM=y +# end of ESP-Driver:SPI Configurations + +# +# ESP-Driver:Temperature Sensor Configurations +# +# CONFIG_TEMP_SENSOR_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:Temperature Sensor Configurations + +# +# ESP-Driver:TWAI Configurations +# +# CONFIG_TWAI_ISR_IN_IRAM is not set +# CONFIG_TWAI_ISR_CACHE_SAFE is not set +# CONFIG_TWAI_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:TWAI Configurations + +# +# ESP-Driver:UART Configurations +# +# CONFIG_UART_ISR_IN_IRAM is not set +# end of ESP-Driver:UART Configurations + +# +# ESP-Driver:UHCI Configurations +# +# CONFIG_UHCI_ISR_HANDLER_IN_IRAM is not set +# CONFIG_UHCI_ISR_CACHE_SAFE is not set +# CONFIG_UHCI_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:UHCI Configurations + +# +# ESP-Driver:USB Serial/JTAG Configuration +# +CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=y +# end of ESP-Driver:USB Serial/JTAG Configuration + +# +# Hardware Settings +# + +# +# Chip revision +# +CONFIG_ESP32S3_REV_MIN_0=y +# CONFIG_ESP32S3_REV_MIN_1 is not set +# CONFIG_ESP32S3_REV_MIN_2 is not set +CONFIG_ESP32S3_REV_MIN_FULL=0 +CONFIG_ESP_REV_MIN_FULL=0 + +# +# Maximum Supported ESP32-S3 Revision (Rev v0.99) +# +CONFIG_ESP32S3_REV_MAX_FULL=99 +CONFIG_ESP_REV_MAX_FULL=99 +CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL=0 +CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL=199 + +# +# Maximum Supported ESP32-S3 eFuse Block Revision (eFuse Block Rev v1.99) +# +# end of Chip revision + +# +# 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_ESP32S3_UNIVERSAL_MAC_ADDRESSES_TWO is not set +CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES_FOUR=y +CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES=4 +# CONFIG_ESP_MAC_USE_CUSTOM_MAC_AS_BASE_MAC is not set +# end of MAC Config + +# +# Sleep Config +# +# CONFIG_ESP_SLEEP_POWER_DOWN_FLASH is not set +CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND=y +CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU=y +CONFIG_ESP_SLEEP_RTC_BUS_ISO_WORKAROUND=y +CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND=y +CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY=2000 +# CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION is not set +# CONFIG_ESP_SLEEP_DEBUG is not set +CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS=y +# end of Sleep Config + +# +# RTC Clock Config +# +CONFIG_RTC_CLK_SRC_INT_RC=y +# CONFIG_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_RTC_CLK_SRC_INT_8MD256 is not set +CONFIG_RTC_CLK_CAL_CYCLES=1024 +# end of RTC Clock Config + +# +# Peripheral Control +# +CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=y +CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=y +# end of Peripheral Control + +# +# GDMA Configurations +# +CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y +CONFIG_GDMA_ISR_HANDLER_IN_IRAM=y +CONFIG_GDMA_OBJ_DRAM_SAFE=y +# CONFIG_GDMA_ENABLE_DEBUG_LOG is not set +# CONFIG_GDMA_ISR_IRAM_SAFE is not set +# end of GDMA Configurations + +# +# Main XTAL Config +# +CONFIG_XTAL_FREQ_40=y +CONFIG_XTAL_FREQ=40 +# end of Main XTAL Config + +# +# Power Supplier +# + +# +# Brownout Detector +# +CONFIG_ESP_BROWNOUT_DET=y +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7=y +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_1 is not set +CONFIG_ESP_BROWNOUT_DET_LVL=7 +CONFIG_ESP_BROWNOUT_USE_INTR=y +# end of Brownout Detector +# end of Power Supplier + +CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y +CONFIG_ESP_INTR_IN_IRAM=y +# end of Hardware Settings + +# +# ESP-MM: Memory Management Configurations +# +# CONFIG_ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS is not set +# end of ESP-MM: Memory Management Configurations + +# +# Partition API Configuration +# +# end of Partition API Configuration + +# +# Power Management +# +CONFIG_PM_SLEEP_FUNC_IN_IRAM=y +# CONFIG_PM_ENABLE is not set +CONFIG_PM_SLP_IRAM_OPT=y +CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y +CONFIG_PM_RESTORE_CACHE_TAGMEM_AFTER_LIGHT_SLEEP=y +# end of Power Management + +# +# ESP Ringbuf +# +# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set +# end of ESP Ringbuf + +# +# ESP-ROM +# +CONFIG_ESP_ROM_PRINT_IN_IRAM=y +# end of ESP-ROM + +# +# ESP Security Specific +# +# end of ESP Security Specific + +# +# ESP System Settings +# +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160 + +# +# Cache config +# +CONFIG_ESP32S3_INSTRUCTION_CACHE_16KB=y +# CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB is not set +CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE=0x4000 +# CONFIG_ESP32S3_INSTRUCTION_CACHE_4WAYS is not set +CONFIG_ESP32S3_INSTRUCTION_CACHE_8WAYS=y +CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS=8 +# CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_16B is not set +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32 +# CONFIG_ESP32S3_DATA_CACHE_16KB is not set +CONFIG_ESP32S3_DATA_CACHE_32KB=y +# CONFIG_ESP32S3_DATA_CACHE_64KB is not set +CONFIG_ESP32S3_DATA_CACHE_SIZE=0x8000 +# CONFIG_ESP32S3_DATA_CACHE_4WAYS is not set +CONFIG_ESP32S3_DATA_CACHE_8WAYS=y +CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8 +# CONFIG_ESP32S3_DATA_CACHE_LINE_16B is not set +CONFIG_ESP32S3_DATA_CACHE_LINE_32B=y +# CONFIG_ESP32S3_DATA_CACHE_LINE_64B is not set +CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=32 +# end of Cache config + +# +# Memory +# +# CONFIG_ESP32S3_RTCDATA_IN_FAST_MEM is not set +# CONFIG_ESP32S3_USE_FIXED_STATIC_RAM_SIZE is not set +# end of Memory + +# +# Trace memory +# +# CONFIG_ESP32S3_TRAX is not set +CONFIG_ESP32S3_TRACEMEM_RESERVE_DRAM=0x0 +# end of Trace memory + +CONFIG_ESP_SYSTEM_IN_IRAM=y +# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set +CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y +# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set +CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS=0 +CONFIG_ESP_SYSTEM_RTC_FAST_MEM_AS_HEAP_DEPCHECK=y +CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=y + +# +# Memory protection +# +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=y +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y +# end of Memory protection + +CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584 +CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y +# CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set +# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set +CONFIG_ESP_MAIN_TASK_AFFINITY=0x0 +CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 +# CONFIG_ESP_CONSOLE_UART_DEFAULT is not set +# CONFIG_ESP_CONSOLE_USB_CDC is not set +# CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG is not set +# CONFIG_ESP_CONSOLE_UART_CUSTOM is not set +CONFIG_ESP_CONSOLE_NONE=y +# CONFIG_ESP_CONSOLE_SECONDARY_NONE is not set +CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED=y +CONFIG_ESP_CONSOLE_UART_NUM=-1 +CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=-1 +CONFIG_ESP_INT_WDT=y +CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 +CONFIG_ESP_INT_WDT_CHECK_CPU1=y +CONFIG_ESP_TASK_WDT_EN=y +CONFIG_ESP_TASK_WDT_INIT=y +# CONFIG_ESP_TASK_WDT_PANIC is not set +CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP_PANIC_HANDLER_IRAM is not set +# CONFIG_ESP_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP_DEBUG_OCDAWARE=y +CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4=y +CONFIG_ESP_SYSTEM_BBPLL_RECALIB=y +# end of ESP System Settings + +# +# IPC (Inter-Processor Call) +# +CONFIG_ESP_IPC_ENABLE=y +CONFIG_ESP_IPC_TASK_STACK_SIZE=1280 +CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y +CONFIG_ESP_IPC_ISR_ENABLE=y +# end of IPC (Inter-Processor Call) + +# +# ESP Timer (High Resolution Timer) +# +CONFIG_ESP_TIMER_IN_IRAM=y +# CONFIG_ESP_TIMER_PROFILING is not set +CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y +CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y +CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 +CONFIG_ESP_TIMER_INTERRUPT_LEVEL=1 +# CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL is not set +CONFIG_ESP_TIMER_TASK_AFFINITY=0x0 +CONFIG_ESP_TIMER_TASK_AFFINITY_CPU0=y +CONFIG_ESP_TIMER_ISR_AFFINITY_CPU0=y +# CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD is not set +CONFIG_ESP_TIMER_IMPL_SYSTIMER=y +# end of ESP Timer (High Resolution Timer) + +# +# FreeRTOS +# + +# +# Kernel +# +# CONFIG_FREERTOS_SMP is not set +# CONFIG_FREERTOS_UNICORE is not set +CONFIG_FREERTOS_HZ=100 +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y +CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 +# CONFIG_FREERTOS_USE_IDLE_HOOK is not set +# CONFIG_FREERTOS_USE_TICK_HOOK is not set +CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 +# CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY is not set +CONFIG_FREERTOS_USE_TIMERS=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_NAME="Tmr Svc" +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0 is not set +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU1 is not set +CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_CORE_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 +CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 +CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 +# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set +# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set +# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set +# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set +# end of Kernel + +# +# Port +# +CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y +# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set +CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y +# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set +# CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set +CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y +CONFIG_FREERTOS_ISR_STACKSIZE=1536 +CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y +# CONFIG_FREERTOS_FPU_IN_ISR is not set +CONFIG_FREERTOS_TICK_SUPPORT_SYSTIMER=y +CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y +# CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set +CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y +# CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set +# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set +# end of Port + +# +# Extra +# +# end of Extra + +CONFIG_FREERTOS_PORT=y +CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y +CONFIG_FREERTOS_DEBUG_OCDAWARE=y +CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y +CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y +CONFIG_FREERTOS_NUMBER_OF_CORES=2 +CONFIG_FREERTOS_IN_IRAM=y +# end of FreeRTOS + +# +# Hardware Abstraction Layer (HAL) and Low Level (LL) +# +CONFIG_HAL_ASSERTION_EQUALS_SYSTEM=y +# CONFIG_HAL_ASSERTION_DISABLE is not set +# CONFIG_HAL_ASSERTION_SILENT is not set +# CONFIG_HAL_ASSERTION_ENABLE is not set +CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2 +CONFIG_HAL_WDT_USE_ROM_IMPL=y +# end of Hardware Abstraction Layer (HAL) and Low Level (LL) + +# +# Heap memory debugging +# +CONFIG_HEAP_POISONING_DISABLED=y +# CONFIG_HEAP_POISONING_LIGHT is not set +# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set +CONFIG_HEAP_TRACING_OFF=y +# CONFIG_HEAP_TRACING_STANDALONE is not set +# CONFIG_HEAP_TRACING_TOHOST is not set +# CONFIG_HEAP_USE_HOOKS is not set +# CONFIG_HEAP_TASK_TRACKING is not set +# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set +# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set +# end of Heap memory debugging + +# +# Log +# +CONFIG_LOG_VERSION_1=y +# CONFIG_LOG_VERSION_2 is not set +CONFIG_LOG_VERSION=1 + +# +# Log Level +# +# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set +# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set +# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set +# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y +# CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set +# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set +CONFIG_LOG_MAXIMUM_LEVEL=3 + +# +# Level Settings +# +# CONFIG_LOG_MASTER_LEVEL is not set +CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y +# CONFIG_LOG_TAG_LEVEL_IMPL_NONE is not set +# CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST is not set +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST=y +# CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY is not set +CONFIG_LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP=y +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31 +# end of Level Settings +# end of Log Level + +# +# Format +# +# CONFIG_LOG_COLORS is not set +CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y +# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set +# end of Format + +# +# Settings +# +CONFIG_LOG_MODE_TEXT_EN=y +CONFIG_LOG_MODE_TEXT=y +# end of Settings + +CONFIG_LOG_IN_IRAM=y +# end of Log + +# +# mbedTLS +# +CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y +# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set +# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set +CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +# CONFIG_MBEDTLS_DYNAMIC_BUFFER is not set +# CONFIG_MBEDTLS_DEBUG is not set + +# +# mbedTLS v3.x related +# +# CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 is not set +# CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH is not set +# CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set +# CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set +CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y +# CONFIG_MBEDTLS_SSL_KEYING_MATERIAL_EXPORT is not set +CONFIG_MBEDTLS_PKCS7_C=y +# end of mbedTLS v3.x related + +# +# Certificate Bundle +# +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set +# CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEPRECATED_LIST is not set +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS=200 +# end of Certificate Bundle + +# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set +# CONFIG_MBEDTLS_CMAC_C is not set +CONFIG_MBEDTLS_HARDWARE_AES=y +CONFIG_MBEDTLS_AES_USE_INTERRUPT=y +CONFIG_MBEDTLS_AES_INTERRUPT_LEVEL=0 +CONFIG_MBEDTLS_GCM_SUPPORT_NON_AES_CIPHER=y +CONFIG_MBEDTLS_HARDWARE_MPI=y +# CONFIG_MBEDTLS_LARGE_KEY_SOFTWARE_MPI is not set +CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y +CONFIG_MBEDTLS_MPI_INTERRUPT_LEVEL=0 +CONFIG_MBEDTLS_HARDWARE_SHA=y +CONFIG_MBEDTLS_ROM_MD5=y +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY is not set +CONFIG_MBEDTLS_HAVE_TIME=y +# CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set +# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set +CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y +CONFIG_MBEDTLS_SHA1_C=y +CONFIG_MBEDTLS_SHA512_C=y +# CONFIG_MBEDTLS_SHA3_C is not set +CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y +# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set +# CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set +# CONFIG_MBEDTLS_TLS_DISABLED is not set +CONFIG_MBEDTLS_TLS_SERVER=y +CONFIG_MBEDTLS_TLS_CLIENT=y +CONFIG_MBEDTLS_TLS_ENABLED=y + +# +# TLS Key Exchange Methods +# +# CONFIG_MBEDTLS_PSK_MODES is not set +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y +# end of TLS Key Exchange Methods + +CONFIG_MBEDTLS_SSL_RENEGOTIATION=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +# CONFIG_MBEDTLS_SSL_PROTO_GMTSSL1_1 is not set +# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set +CONFIG_MBEDTLS_SSL_ALPN=y +CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y +CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y + +# +# Symmetric Ciphers +# +CONFIG_MBEDTLS_AES_C=y +# CONFIG_MBEDTLS_CAMELLIA_C is not set +# CONFIG_MBEDTLS_DES_C is not set +# CONFIG_MBEDTLS_BLOWFISH_C is not set +# CONFIG_MBEDTLS_XTEA_C is not set +CONFIG_MBEDTLS_CCM_C=y +CONFIG_MBEDTLS_GCM_C=y +# CONFIG_MBEDTLS_NIST_KW_C is not set +# end of Symmetric Ciphers + +# CONFIG_MBEDTLS_RIPEMD160_C is not set + +# +# Certificates +# +CONFIG_MBEDTLS_PEM_PARSE_C=y +CONFIG_MBEDTLS_PEM_WRITE_C=y +CONFIG_MBEDTLS_X509_CRL_PARSE_C=y +CONFIG_MBEDTLS_X509_CSR_PARSE_C=y +# end of Certificates + +CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_PK_PARSE_EC_EXTENDED=y +CONFIG_MBEDTLS_PK_PARSE_EC_COMPRESSED=y +# CONFIG_MBEDTLS_DHM_C is not set +CONFIG_MBEDTLS_ECDH_C=y +CONFIG_MBEDTLS_ECDSA_C=y +# CONFIG_MBEDTLS_ECJPAKE_C is not set +CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y +CONFIG_MBEDTLS_ECP_NIST_OPTIM=y +# CONFIG_MBEDTLS_ECP_FIXED_POINT_OPTIM is not set +# CONFIG_MBEDTLS_POLY1305_C is not set +# CONFIG_MBEDTLS_CHACHA20_C is not set +# CONFIG_MBEDTLS_HKDF_C is not set +# CONFIG_MBEDTLS_THREADING_C is not set +CONFIG_MBEDTLS_ERROR_STRINGS=y +# CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION is not set +# end of mbedTLS + +# +# LibC +# +CONFIG_LIBC_NEWLIB=y +CONFIG_LIBC_MISC_IN_IRAM=y +CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=y +# CONFIG_LIBC_NEWLIB_NANO_FORMAT is not set +CONFIG_LIBC_TIME_SYSCALL_USE_RTC_HRT=y +# CONFIG_LIBC_TIME_SYSCALL_USE_RTC is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_HRT is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_NONE is not set +# end of LibC + +# +# PThreads +# +CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_PTHREAD_STACK_MIN=768 +CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY=y +# CONFIG_PTHREAD_DEFAULT_CORE_0 is not set +# CONFIG_PTHREAD_DEFAULT_CORE_1 is not set +CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" +# end of PThreads + +# +# MMU Config +# +CONFIG_MMU_PAGE_SIZE_64KB=y +CONFIG_MMU_PAGE_MODE="64KB" +CONFIG_MMU_PAGE_SIZE=0x10000 +# end of MMU Config + +# +# Main Flash configuration +# + +# +# SPI Flash behavior when brownout +# +CONFIG_SPI_FLASH_BROWNOUT_RESET_XMC=y +CONFIG_SPI_FLASH_BROWNOUT_RESET=y +# end of SPI Flash behavior when brownout + +# +# Optional and Experimental Features (READ DOCS FIRST) +# + +# +# Features here require specific hardware (READ DOCS FIRST!) +# +# CONFIG_SPI_FLASH_HPM_ENA is not set +CONFIG_SPI_FLASH_HPM_AUTO=y +# CONFIG_SPI_FLASH_HPM_DIS is not set +CONFIG_SPI_FLASH_HPM_ON=y +CONFIG_SPI_FLASH_HPM_DC_AUTO=y +# CONFIG_SPI_FLASH_HPM_DC_DISABLE is not set +# CONFIG_SPI_FLASH_AUTO_SUSPEND is not set +CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50 +# CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set +# CONFIG_SPI_FLASH_FORCE_ENABLE_C6_H2_SUSPEND is not set +CONFIG_SPI_FLASH_PLACE_FUNCTIONS_IN_IRAM=y +# end of Optional and Experimental Features (READ DOCS FIRST) +# end of Main Flash configuration + +# +# SPI Flash driver +# +# CONFIG_SPI_FLASH_VERIFY_WRITE is not set +# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y +# CONFIG_SPI_FLASH_ROM_IMPL is not set +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set +# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set +CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y +CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 +CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 +CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192 +# CONFIG_SPI_FLASH_SIZE_OVERRIDE is not set +# CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED is not set +# CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST is not set + +# +# Auto-detect flash chips +# +CONFIG_SPI_FLASH_VENDOR_XMC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_GD_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_ISSI_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_MXIC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_WINBOND_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_BOYA_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_TH_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_TH_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP=y +# end of Auto-detect flash chips + +CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=y +# end of SPI Flash driver + +# +# LoRa common options +# +CONFIG_LORA_ROLE_TX=y +# CONFIG_LORA_ROLE_RX is not set +# end of LoRa common options + +# +# Joystick input +# +CONFIG_JOYSTICK_ADC_CHANNEL=5 +CONFIG_JOYSTICK_ADC_LEVEL_LEFT=2630 +CONFIG_JOYSTICK_ADC_LEVEL_UP=1230 +CONFIG_JOYSTICK_ADC_LEVEL_PRESS=0 +CONFIG_JOYSTICK_ADC_LEVEL_DOWN=1970 +CONFIG_JOYSTICK_ADC_LEVEL_RIGHT=680 +CONFIG_JOYSTICK_ADC_TOLERANCE=150 +# end of Joystick input + +# +# LoRa radio (LR1121) +# +CONFIG_LORA_SPI_HOST=2 +CONFIG_LORA_PIN_CS=12 +CONFIG_LORA_PIN_MOSI=10 +CONFIG_LORA_PIN_MISO=9 +CONFIG_LORA_PIN_SCK=11 +CONFIG_LORA_PIN_RST=5 +CONFIG_LORA_PIN_BUSY=13 +CONFIG_LORA_PIN_DIO1=4 +CONFIG_LORA_FREQ_MHZ=433 +CONFIG_LORA_BW_KHZ=125 +CONFIG_LORA_SF=7 +CONFIG_LORA_CR=5 +# end of LoRa radio (LR1121) + +# +# Display +# +CONFIG_DISPLAY_DRIVER="SSD1306 I2C OLED" +CONFIG_DISPLAY_I2C_PORT=0 +CONFIG_DISPLAY_I2C_ADDR=60 +CONFIG_DISPLAY_PIN_SDA=7 +CONFIG_DISPLAY_PIN_RST=-1 +CONFIG_DISPLAY_PIN_SCL=8 +CONFIG_DISPLAY_WIDTH=128 +CONFIG_DISPLAY_HEIGHT=64 +# end of Display +# end of Component config + +# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set + +# Deprecated options for backward compatibility +# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set +# CONFIG_NO_BLOBS is not set +# CONFIG_APP_ROLLBACK_ENABLE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set +CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y +# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set +CONFIG_LOG_BOOTLOADER_LEVEL=3 +# CONFIG_FLASH_ENCRYPTION_ENABLED is not set +# CONFIG_FLASHMODE_QIO is not set +# CONFIG_FLASHMODE_QOUT is not set +CONFIG_FLASHMODE_DIO=y +# CONFIG_FLASHMODE_DOUT is not set +CONFIG_MONITOR_BAUD=115200 +CONFIG_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y +# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set +# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set +CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y +# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set +CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_CXX_EXCEPTIONS is not set +CONFIG_STACK_CHECK_NONE=y +# CONFIG_STACK_CHECK_NORM is not set +# CONFIG_STACK_CHECK_STRONG is not set +# CONFIG_STACK_CHECK_ALL is not set +# CONFIG_WARN_WRITE_STRINGS is not set +# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set +# CONFIG_MCPWM_ISR_IRAM_SAFE is not set +# CONFIG_ESP_SYSTEM_PD_FLASH is not set +CONFIG_ESP32S3_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y +# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_ESP32S3_RTC_CLK_SRC_INT_8MD256 is not set +CONFIG_ESP32S3_RTC_CLK_CAL_CYCLES=1024 +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y +CONFIG_BROWNOUT_DET=y +CONFIG_ESP32S3_BROWNOUT_DET=y +CONFIG_BROWNOUT_DET_LVL_SEL_7=y +CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_7=y +# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_1 is not set +CONFIG_BROWNOUT_DET_LVL=7 +CONFIG_ESP32S3_BROWNOUT_DET_LVL=7 +CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y +CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU=y +CONFIG_PM_POWER_DOWN_TAGMEM_IN_LIGHT_SLEEP=y +# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_80 is not set +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_160=y +# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240 is not set +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=160 +CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_MAIN_TASK_STACK_SIZE=3584 +# CONFIG_CONSOLE_UART_DEFAULT is not set +# CONFIG_CONSOLE_UART_CUSTOM is not set +CONFIG_CONSOLE_UART_NONE=y +CONFIG_ESP_CONSOLE_UART_NONE=y +CONFIG_CONSOLE_UART_NUM=-1 +CONFIG_INT_WDT=y +CONFIG_INT_WDT_TIMEOUT_MS=300 +CONFIG_INT_WDT_CHECK_CPU1=y +CONFIG_TASK_WDT=y +CONFIG_ESP_TASK_WDT=y +# CONFIG_TASK_WDT_PANIC is not set +CONFIG_TASK_WDT_TIMEOUT_S=5 +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP32S3_DEBUG_OCDAWARE=y +CONFIG_IPC_TASK_STACK_SIZE=1280 +CONFIG_TIMER_TASK_STACK_SIZE=3584 +CONFIG_TIMER_TASK_PRIORITY=1 +CONFIG_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_TIMER_QUEUE_LENGTH=10 +# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set +# CONFIG_HAL_ASSERTION_SILIENT is not set +# CONFIG_NEWLIB_NANO_FORMAT is not set +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y +CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_SYSTIMER=y +CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_FRC1=y +# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_SYSTIMER is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_FRC1 is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_NONE is not set +CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_ESP32_PTHREAD_STACK_MIN=768 +CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set +CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set +# End of deprecated options diff --git a/apps/tx/sdkconfig.defaults b/apps/tx/sdkconfig.defaults new file mode 100644 index 0000000..09e031c --- /dev/null +++ b/apps/tx/sdkconfig.defaults @@ -0,0 +1,43 @@ +# Роль за замовчуванням +CONFIG_LORA_ROLE_TX=y + + +# Піни RA01 (налаштуйте під свою плату) +CONFIG_LORA_SPI_HOST=2 +CONFIG_LORA_PIN_CS=12 +CONFIG_LORA_PIN_RST=-1 +CONFIG_LORA_PIN_MOSI=10 +CONFIG_LORA_PIN_MISO=9 +CONFIG_LORA_PIN_SCK=11 +CONFIG_LORA_PIN_BUSY=13 +CONFIG_LORA_PIN_DIO1=-1 + +# Радіо +CONFIG_LORA_FREQ_MHZ=433 +CONFIG_LORA_BW_KHZ=125 +CONFIG_LORA_SF=7 +CONFIG_LORA_CR=5 + +# Консоль через USB Serial/JTAG (CDC), щоб не тримати BOOT +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y +CONFIG_ESP_CONSOLE_UART_NONE=y +# CONFIG_ESP_CONSOLE_UART_DEFAULT is not set +# CONFIG_ESP_CONSOLE_USB_CDC is not set + +# Джойстик +CONFIG_JOYSTICK_ADC_CHANNEL=6 +CONFIG_JOYSTICK_ADC_LEVEL_LEFT=600 +CONFIG_JOYSTICK_ADC_LEVEL_UP=1200 +CONFIG_JOYSTICK_ADC_LEVEL_PRESS=1900 +CONFIG_JOYSTICK_ADC_LEVEL_DOWN=2600 +CONFIG_JOYSTICK_ADC_LEVEL_RIGHT=3300 +CONFIG_JOYSTICK_ADC_TOLERANCE=150 + +# Дисплей +CONFIG_DISPLAY_I2C_PORT=0 +CONFIG_DISPLAY_I2C_ADDR=60 +CONFIG_DISPLAY_PIN_SDA=7 +CONFIG_DISPLAY_PIN_SCL=8 +CONFIG_DISPLAY_PIN_RST=-1 +CONFIG_DISPLAY_WIDTH=128 +CONFIG_DISPLAY_HEIGHT=64 diff --git a/apps/tx/sdkconfig.old b/apps/tx/sdkconfig.old new file mode 100644 index 0000000..9588a5f --- /dev/null +++ b/apps/tx/sdkconfig.old @@ -0,0 +1,1549 @@ +# +# Automatically generated file. DO NOT EDIT. +# Espressif IoT Development Framework (ESP-IDF) 5.5.1 Project Configuration +# +CONFIG_SOC_CAPS_ECO_VER_MAX=301 +CONFIG_SOC_ADC_SUPPORTED=y +CONFIG_SOC_DAC_SUPPORTED=y +CONFIG_SOC_UART_SUPPORTED=y +CONFIG_SOC_MCPWM_SUPPORTED=y +CONFIG_SOC_GPTIMER_SUPPORTED=y +CONFIG_SOC_SDMMC_HOST_SUPPORTED=y +CONFIG_SOC_BT_SUPPORTED=y +CONFIG_SOC_PCNT_SUPPORTED=y +CONFIG_SOC_PHY_SUPPORTED=y +CONFIG_SOC_WIFI_SUPPORTED=y +CONFIG_SOC_SDIO_SLAVE_SUPPORTED=y +CONFIG_SOC_TWAI_SUPPORTED=y +CONFIG_SOC_EFUSE_SUPPORTED=y +CONFIG_SOC_EMAC_SUPPORTED=y +CONFIG_SOC_ULP_SUPPORTED=y +CONFIG_SOC_CCOMP_TIMER_SUPPORTED=y +CONFIG_SOC_RTC_FAST_MEM_SUPPORTED=y +CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED=y +CONFIG_SOC_RTC_MEM_SUPPORTED=y +CONFIG_SOC_I2S_SUPPORTED=y +CONFIG_SOC_RMT_SUPPORTED=y +CONFIG_SOC_SDM_SUPPORTED=y +CONFIG_SOC_GPSPI_SUPPORTED=y +CONFIG_SOC_LEDC_SUPPORTED=y +CONFIG_SOC_I2C_SUPPORTED=y +CONFIG_SOC_SUPPORT_COEXISTENCE=y +CONFIG_SOC_AES_SUPPORTED=y +CONFIG_SOC_MPI_SUPPORTED=y +CONFIG_SOC_SHA_SUPPORTED=y +CONFIG_SOC_FLASH_ENC_SUPPORTED=y +CONFIG_SOC_SECURE_BOOT_SUPPORTED=y +CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y +CONFIG_SOC_BOD_SUPPORTED=y +CONFIG_SOC_ULP_FSM_SUPPORTED=y +CONFIG_SOC_CLK_TREE_SUPPORTED=y +CONFIG_SOC_MPU_SUPPORTED=y +CONFIG_SOC_WDT_SUPPORTED=y +CONFIG_SOC_SPI_FLASH_SUPPORTED=y +CONFIG_SOC_RNG_SUPPORTED=y +CONFIG_SOC_LIGHT_SLEEP_SUPPORTED=y +CONFIG_SOC_DEEP_SLEEP_SUPPORTED=y +CONFIG_SOC_LP_PERIPH_SHARE_INTERRUPT=y +CONFIG_SOC_PM_SUPPORTED=y +CONFIG_SOC_DPORT_WORKAROUND_DIS_INTERRUPT_LVL=5 +CONFIG_SOC_XTAL_SUPPORT_26M=y +CONFIG_SOC_XTAL_SUPPORT_40M=y +CONFIG_SOC_XTAL_SUPPORT_AUTO_DETECT=y +CONFIG_SOC_ADC_RTC_CTRL_SUPPORTED=y +CONFIG_SOC_ADC_DIG_CTRL_SUPPORTED=y +CONFIG_SOC_ADC_DMA_SUPPORTED=y +CONFIG_SOC_ADC_PERIPH_NUM=2 +CONFIG_SOC_ADC_MAX_CHANNEL_NUM=10 +CONFIG_SOC_ADC_ATTEN_NUM=4 +CONFIG_SOC_ADC_DIGI_CONTROLLER_NUM=2 +CONFIG_SOC_ADC_PATT_LEN_MAX=16 +CONFIG_SOC_ADC_DIGI_MIN_BITWIDTH=9 +CONFIG_SOC_ADC_DIGI_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_DIGI_RESULT_BYTES=2 +CONFIG_SOC_ADC_DIGI_DATA_BYTES_PER_CONV=4 +CONFIG_SOC_ADC_DIGI_MONITOR_NUM=0 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH=2 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW=20 +CONFIG_SOC_ADC_RTC_MIN_BITWIDTH=9 +CONFIG_SOC_ADC_RTC_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_SHARED_POWER=y +CONFIG_SOC_BROWNOUT_RESET_SUPPORTED=y +CONFIG_SOC_SHARED_IDCACHE_SUPPORTED=y +CONFIG_SOC_IDCACHE_PER_CORE=y +CONFIG_SOC_CPU_CORES_NUM=2 +CONFIG_SOC_CPU_INTR_NUM=32 +CONFIG_SOC_CPU_HAS_FPU=y +CONFIG_SOC_HP_CPU_HAS_MULTIPLE_CORES=y +CONFIG_SOC_CPU_BREAKPOINTS_NUM=2 +CONFIG_SOC_CPU_WATCHPOINTS_NUM=2 +CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=0x40 +CONFIG_SOC_DAC_CHAN_NUM=2 +CONFIG_SOC_DAC_RESOLUTION=8 +CONFIG_SOC_DAC_DMA_16BIT_ALIGN=y +CONFIG_SOC_GPIO_PORT=1 +CONFIG_SOC_GPIO_PIN_COUNT=40 +CONFIG_SOC_GPIO_VALID_GPIO_MASK=0xFFFFFFFFFF +CONFIG_SOC_GPIO_IN_RANGE_MAX=39 +CONFIG_SOC_GPIO_OUT_RANGE_MAX=33 +CONFIG_SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK=0xEF0FEA +CONFIG_SOC_GPIO_CLOCKOUT_BY_IO_MUX=y +CONFIG_SOC_GPIO_CLOCKOUT_CHANNEL_NUM=3 +CONFIG_SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP=y +CONFIG_SOC_I2C_NUM=2 +CONFIG_SOC_HP_I2C_NUM=2 +CONFIG_SOC_I2C_FIFO_LEN=32 +CONFIG_SOC_I2C_CMD_REG_NUM=16 +CONFIG_SOC_I2C_SUPPORT_SLAVE=y +CONFIG_SOC_I2C_SUPPORT_APB=y +CONFIG_SOC_I2C_SUPPORT_10BIT_ADDR=y +CONFIG_SOC_I2C_STOP_INDEPENDENT=y +CONFIG_SOC_I2S_NUM=2 +CONFIG_SOC_I2S_HW_VERSION_1=y +CONFIG_SOC_I2S_SUPPORTS_APLL=y +CONFIG_SOC_I2S_SUPPORTS_PLL_F160M=y +CONFIG_SOC_I2S_SUPPORTS_PDM=y +CONFIG_SOC_I2S_SUPPORTS_PDM_TX=y +CONFIG_SOC_I2S_SUPPORTS_PCM2PDM=y +CONFIG_SOC_I2S_SUPPORTS_PDM_RX=y +CONFIG_SOC_I2S_SUPPORTS_PDM2PCM=y +CONFIG_SOC_I2S_PDM_MAX_TX_LINES=1 +CONFIG_SOC_I2S_PDM_MAX_RX_LINES=1 +CONFIG_SOC_I2S_SUPPORTS_ADC_DAC=y +CONFIG_SOC_I2S_SUPPORTS_ADC=y +CONFIG_SOC_I2S_SUPPORTS_DAC=y +CONFIG_SOC_I2S_SUPPORTS_LCD_CAMERA=y +CONFIG_SOC_I2S_MAX_DATA_WIDTH=24 +CONFIG_SOC_I2S_TRANS_SIZE_ALIGN_WORD=y +CONFIG_SOC_I2S_LCD_I80_VARIANT=y +CONFIG_SOC_LCD_I80_SUPPORTED=y +CONFIG_SOC_LCD_I80_BUSES=2 +CONFIG_SOC_LCD_I80_BUS_WIDTH=24 +CONFIG_SOC_LEDC_HAS_TIMER_SPECIFIC_MUX=y +CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y +CONFIG_SOC_LEDC_SUPPORT_REF_TICK=y +CONFIG_SOC_LEDC_SUPPORT_HS_MODE=y +CONFIG_SOC_LEDC_TIMER_NUM=4 +CONFIG_SOC_LEDC_CHANNEL_NUM=8 +CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=20 +CONFIG_SOC_MCPWM_GROUPS=2 +CONFIG_SOC_MCPWM_TIMERS_PER_GROUP=3 +CONFIG_SOC_MCPWM_OPERATORS_PER_GROUP=3 +CONFIG_SOC_MCPWM_COMPARATORS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_GENERATORS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_TRIGGERS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_GPIO_FAULTS_PER_GROUP=3 +CONFIG_SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP=y +CONFIG_SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER=3 +CONFIG_SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP=3 +CONFIG_SOC_MMU_PERIPH_NUM=2 +CONFIG_SOC_MMU_LINEAR_ADDRESS_REGION_NUM=3 +CONFIG_SOC_MPU_MIN_REGION_SIZE=0x20000000 +CONFIG_SOC_MPU_REGIONS_MAX_NUM=8 +CONFIG_SOC_PCNT_GROUPS=1 +CONFIG_SOC_PCNT_UNITS_PER_GROUP=8 +CONFIG_SOC_PCNT_CHANNELS_PER_UNIT=2 +CONFIG_SOC_PCNT_THRES_POINT_PER_UNIT=2 +CONFIG_SOC_RMT_GROUPS=1 +CONFIG_SOC_RMT_TX_CANDIDATES_PER_GROUP=8 +CONFIG_SOC_RMT_RX_CANDIDATES_PER_GROUP=8 +CONFIG_SOC_RMT_CHANNELS_PER_GROUP=8 +CONFIG_SOC_RMT_MEM_WORDS_PER_CHANNEL=64 +CONFIG_SOC_RMT_SUPPORT_REF_TICK=y +CONFIG_SOC_RMT_SUPPORT_APB=y +CONFIG_SOC_RMT_CHANNEL_CLK_INDEPENDENT=y +CONFIG_SOC_RTCIO_PIN_COUNT=18 +CONFIG_SOC_RTCIO_INPUT_OUTPUT_SUPPORTED=y +CONFIG_SOC_RTCIO_HOLD_SUPPORTED=y +CONFIG_SOC_RTCIO_WAKE_SUPPORTED=y +CONFIG_SOC_SDM_GROUPS=1 +CONFIG_SOC_SDM_CHANNELS_PER_GROUP=8 +CONFIG_SOC_SDM_CLK_SUPPORT_APB=y +CONFIG_SOC_SPI_HD_BOTH_INOUT_SUPPORTED=y +CONFIG_SOC_SPI_AS_CS_SUPPORTED=y +CONFIG_SOC_SPI_PERIPH_NUM=3 +CONFIG_SOC_SPI_DMA_CHAN_NUM=2 +CONFIG_SOC_SPI_MAX_CS_NUM=3 +CONFIG_SOC_SPI_SUPPORT_CLK_APB=y +CONFIG_SOC_SPI_MAXIMUM_BUFFER_SIZE=64 +CONFIG_SOC_SPI_MAX_PRE_DIVIDER=8192 +CONFIG_SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_26M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_20M_SUPPORTED=y +CONFIG_SOC_TIMER_GROUPS=2 +CONFIG_SOC_TIMER_GROUP_TIMERS_PER_GROUP=2 +CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=64 +CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=4 +CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y +CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32 +CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16 +CONFIG_SOC_TOUCH_SENSOR_VERSION=1 +CONFIG_SOC_TOUCH_SENSOR_NUM=10 +CONFIG_SOC_TOUCH_MIN_CHAN_ID=0 +CONFIG_SOC_TOUCH_MAX_CHAN_ID=9 +CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y +CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1 +CONFIG_SOC_TWAI_CONTROLLER_NUM=1 +CONFIG_SOC_TWAI_MASK_FILTER_NUM=1 +CONFIG_SOC_TWAI_BRP_MIN=2 +CONFIG_SOC_TWAI_CLK_SUPPORT_APB=y +CONFIG_SOC_TWAI_SUPPORT_MULTI_ADDRESS_LAYOUT=y +CONFIG_SOC_UART_NUM=3 +CONFIG_SOC_UART_HP_NUM=3 +CONFIG_SOC_UART_SUPPORT_APB_CLK=y +CONFIG_SOC_UART_SUPPORT_REF_TICK=y +CONFIG_SOC_UART_FIFO_LEN=128 +CONFIG_SOC_UART_BITRATE_MAX=5000000 +CONFIG_SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE=y +CONFIG_SOC_SPIRAM_SUPPORTED=y +CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y +CONFIG_SOC_SHA_SUPPORT_PARALLEL_ENG=y +CONFIG_SOC_SHA_ENDIANNESS_BE=y +CONFIG_SOC_SHA_SUPPORT_SHA1=y +CONFIG_SOC_SHA_SUPPORT_SHA256=y +CONFIG_SOC_SHA_SUPPORT_SHA384=y +CONFIG_SOC_SHA_SUPPORT_SHA512=y +CONFIG_SOC_MPI_MEM_BLOCKS_NUM=4 +CONFIG_SOC_MPI_OPERATIONS_NUM=1 +CONFIG_SOC_RSA_MAX_BIT_LEN=4096 +CONFIG_SOC_AES_SUPPORT_AES_128=y +CONFIG_SOC_AES_SUPPORT_AES_192=y +CONFIG_SOC_AES_SUPPORT_AES_256=y +CONFIG_SOC_SECURE_BOOT_V1=y +CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS=1 +CONFIG_SOC_FLASH_ENCRYPTED_XTS_AES_BLOCK_MAX=32 +CONFIG_SOC_PHY_DIG_REGS_MEM_SIZE=21 +CONFIG_SOC_PM_SUPPORT_EXT0_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_EXT1_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_EXT_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_RTC_PERIPH_PD=y +CONFIG_SOC_PM_SUPPORT_RTC_FAST_MEM_PD=y +CONFIG_SOC_PM_SUPPORT_RTC_SLOW_MEM_PD=y +CONFIG_SOC_PM_SUPPORT_RC_FAST_PD=y +CONFIG_SOC_PM_SUPPORT_VDDSDIO_PD=y +CONFIG_SOC_PM_SUPPORT_MODEM_PD=y +CONFIG_SOC_CONFIGURABLE_VDDSDIO_SUPPORTED=y +CONFIG_SOC_PM_MODEM_PD_BY_SW=y +CONFIG_SOC_CLK_APLL_SUPPORTED=y +CONFIG_SOC_CLK_RC_FAST_D256_SUPPORTED=y +CONFIG_SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256=y +CONFIG_SOC_CLK_RC_FAST_SUPPORT_CALIBRATION=y +CONFIG_SOC_CLK_XTAL32K_SUPPORTED=y +CONFIG_SOC_CLK_LP_FAST_SUPPORT_XTAL_D4=y +CONFIG_SOC_SDMMC_USE_IOMUX=y +CONFIG_SOC_SDMMC_NUM_SLOTS=2 +CONFIG_SOC_WIFI_WAPI_SUPPORT=y +CONFIG_SOC_WIFI_CSI_SUPPORT=y +CONFIG_SOC_WIFI_MESH_SUPPORT=y +CONFIG_SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW=y +CONFIG_SOC_WIFI_NAN_SUPPORT=y +CONFIG_SOC_BLE_SUPPORTED=y +CONFIG_SOC_BLE_MESH_SUPPORTED=y +CONFIG_SOC_BT_CLASSIC_SUPPORTED=y +CONFIG_SOC_BLUFI_SUPPORTED=y +CONFIG_SOC_BT_H2C_ENC_KEY_CTRL_ENH_VSC_SUPPORTED=y +CONFIG_SOC_BLE_MULTI_CONN_OPTIMIZATION=y +CONFIG_SOC_ULP_HAS_ADC=y +CONFIG_SOC_PHY_COMBO_MODULE=y +CONFIG_SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK=y +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TOOLCHAIN="gcc" +CONFIG_IDF_TOOLCHAIN_GCC=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET_ARCH="xtensa" +CONFIG_IDF_TARGET="esp32" +CONFIG_IDF_INIT_VERSION="5.5.1" +CONFIG_IDF_TARGET_ESP32=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 + +# +# Build type +# +CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y +# CONFIG_APP_BUILD_TYPE_RAM is not set +CONFIG_APP_BUILD_GENERATE_BINARIES=y +CONFIG_APP_BUILD_BOOTLOADER=y +CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y +# CONFIG_APP_REPRODUCIBLE_BUILD is not set +# CONFIG_APP_NO_BLOBS is not set +# CONFIG_APP_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set +# CONFIG_APP_COMPATIBLE_PRE_V3_1_BOOTLOADERS is not set +# end of Build type + +# +# Bootloader config +# + +# +# Bootloader manager +# +CONFIG_BOOTLOADER_COMPILE_TIME_DATE=y +CONFIG_BOOTLOADER_PROJECT_VER=1 +# end of Bootloader manager + +# +# Application Rollback +# +# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set +# end of Application Rollback + +# +# Recovery Bootloader and Rollback +# +# end of Recovery Bootloader and Rollback + +CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x1000 +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set + +# +# Log +# +CONFIG_BOOTLOADER_LOG_VERSION_1=y +CONFIG_BOOTLOADER_LOG_VERSION=1 +# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set +CONFIG_BOOTLOADER_LOG_LEVEL=3 + +# +# Format +# +# CONFIG_BOOTLOADER_LOG_COLORS is not set +CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y +# end of Format + +# +# Settings +# +CONFIG_BOOTLOADER_LOG_MODE_TEXT_EN=y +CONFIG_BOOTLOADER_LOG_MODE_TEXT=y +# end of Settings +# end of Log + +# +# Serial Flash Configurations +# +# CONFIG_BOOTLOADER_FLASH_DC_AWARE is not set +CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=y +# end of Serial Flash Configurations + +# CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_8V is not set +CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y +# CONFIG_BOOTLOADER_FACTORY_RESET is not set +# CONFIG_BOOTLOADER_APP_TEST is not set +CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y +CONFIG_BOOTLOADER_WDT_ENABLE=y +# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set +CONFIG_BOOTLOADER_WDT_TIME_MS=9000 +# CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set +CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0 +# CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set +# end of Bootloader config + +# +# Security features +# +CONFIG_SECURE_BOOT_V1_SUPPORTED=y +# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set +# CONFIG_SECURE_BOOT is not set +# CONFIG_SECURE_FLASH_ENC_ENABLED is not set +# end of Security features + +# +# Application manager +# +CONFIG_APP_COMPILE_TIME_DATE=y +# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set +# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set +# CONFIG_APP_PROJECT_VER_FROM_CONFIG is not set +CONFIG_APP_RETRIEVE_LEN_ELF_SHA=9 +# end of Application manager + +CONFIG_ESP_ROM_HAS_CRC_LE=y +CONFIG_ESP_ROM_HAS_CRC_BE=y +CONFIG_ESP_ROM_HAS_MZ_CRC32=y +CONFIG_ESP_ROM_HAS_JPEG_DECODE=y +CONFIG_ESP_ROM_HAS_UART_BUF_SWITCH=y +CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y +CONFIG_ESP_ROM_HAS_NEWLIB=y +CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y +CONFIG_ESP_ROM_HAS_NEWLIB_32BIT_TIME=y +CONFIG_ESP_ROM_HAS_SW_FLOAT=y +CONFIG_ESP_ROM_USB_OTG_NUM=-1 +CONFIG_ESP_ROM_USB_SERIAL_DEVICE_NUM=-1 +CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y +CONFIG_ESP_ROM_HAS_OUTPUT_PUTC_FUNC=y + +# +# Serial flasher config +# +# CONFIG_ESPTOOLPY_NO_STUB is not set +# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set +# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE="dio" +# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set +CONFIG_ESPTOOLPY_FLASHFREQ_40M=y +# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set +# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set +CONFIG_ESPTOOLPY_FLASHFREQ="40m" +# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE="2MB" +# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set +CONFIG_ESPTOOLPY_BEFORE_RESET=y +# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +# CONFIG_ESPTOOLPY_AFTER_NORESET is not set +CONFIG_ESPTOOLPY_AFTER="hard_reset" +CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 +# end of Serial flasher config + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_SINGLE_APP=y +# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set +# CONFIG_PARTITION_TABLE_TWO_OTA is not set +# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set +# CONFIG_PARTITION_TABLE_CUSTOM is not set +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# Compiler options +# +CONFIG_COMPILER_OPTIMIZATION_DEBUG=y +# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set +# CONFIG_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_COMPILER_OPTIMIZATION_NONE is not set +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set +CONFIG_COMPILER_ASSERT_NDEBUG_EVALUATE=y +CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set +CONFIG_COMPILER_HIDE_PATHS_MACROS=y +# CONFIG_COMPILER_CXX_EXCEPTIONS is not set +# CONFIG_COMPILER_CXX_RTTI is not set +CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y +# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set +# CONFIG_COMPILER_NO_MERGE_CONSTANTS is not set +# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set +CONFIG_COMPILER_DISABLE_DEFAULT_ERRORS=y +# CONFIG_COMPILER_DISABLE_GCC12_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC13_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC14_WARNINGS is not set +# CONFIG_COMPILER_DUMP_RTL_FILES is not set +CONFIG_COMPILER_RT_LIB_GCCLIB=y +CONFIG_COMPILER_RT_LIB_NAME="gcc" +CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=y +# CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE is not set +# CONFIG_COMPILER_STATIC_ANALYZER is not set +# end of Compiler options + +# +# Component config +# + +# +# !!! MINIMAL_BUILD is enabled !!! +# + +# +# Only common components and those transitively required by the main component are listed +# + +# +# If a component configuration is missing, please add it to the main component's requirements +# + +# +# Driver Configurations +# + +# +# Legacy TWAI Driver Configurations +# +# CONFIG_TWAI_SKIP_LEGACY_CONFLICT_CHECK is not set +CONFIG_TWAI_ERRATA_FIX_BUS_OFF_REC=y +CONFIG_TWAI_ERRATA_FIX_TX_INTR_LOST=y +CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID=y +CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT=y +CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y +# end of Legacy TWAI Driver Configurations + +# +# Legacy ADC Driver Configuration +# +CONFIG_ADC_DISABLE_DAC=y +# CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_ADC_SKIP_LEGACY_CONFLICT_CHECK is not set + +# +# Legacy ADC Calibration Configuration +# +CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y +CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y +CONFIG_ADC_CAL_LUT_ENABLE=y +# CONFIG_ADC_CALI_SUPPRESS_DEPRECATE_WARN is not set +# end of Legacy ADC Calibration Configuration +# end of Legacy ADC Driver Configuration + +# +# Legacy DAC Driver Configurations +# +# CONFIG_DAC_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_DAC_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy DAC Driver Configurations + +# +# Legacy MCPWM Driver Configurations +# +# CONFIG_MCPWM_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_MCPWM_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy MCPWM Driver Configurations + +# +# Legacy Timer Group Driver Configurations +# +# CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_GPTIMER_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Timer Group Driver Configurations + +# +# Legacy RMT Driver Configurations +# +# CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_RMT_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy RMT Driver Configurations + +# +# Legacy I2S Driver Configurations +# +# CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy I2S Driver Configurations + +# +# Legacy I2C Driver Configurations +# +# CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy I2C Driver Configurations + +# +# Legacy PCNT Driver Configurations +# +# CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_PCNT_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy PCNT Driver Configurations + +# +# Legacy SDM Driver Configurations +# +# CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_SDM_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy SDM Driver Configurations + +# +# Legacy Touch Sensor Driver Configurations +# +# CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_TOUCH_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Touch Sensor Driver Configurations +# end of Driver Configurations + +# +# eFuse Bit Manager +# +# CONFIG_EFUSE_CUSTOM_TABLE is not set +# CONFIG_EFUSE_VIRTUAL is not set +# CONFIG_EFUSE_CODE_SCHEME_COMPAT_NONE is not set +CONFIG_EFUSE_CODE_SCHEME_COMPAT_3_4=y +# CONFIG_EFUSE_CODE_SCHEME_COMPAT_REPEAT is not set +CONFIG_EFUSE_MAX_BLK_LEN=192 +# end of eFuse Bit Manager + +# +# ADC and ADC Calibration +# +# CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM is not set +# CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE is not set + +# +# ADC Calibration Configurations +# +CONFIG_ADC_CALI_EFUSE_TP_ENABLE=y +CONFIG_ADC_CALI_EFUSE_VREF_ENABLE=y +CONFIG_ADC_CALI_LUT_ENABLE=y +# end of ADC Calibration Configurations + +CONFIG_ADC_DISABLE_DAC_OUTPUT=y +# CONFIG_ADC_ENABLE_DEBUG_LOG is not set +# end of ADC and ADC Calibration + +# +# Common ESP-related +# +CONFIG_ESP_ERR_TO_NAME_LOOKUP=y +# end of Common ESP-related + +# +# ESP-Driver:DAC Configurations +# +# CONFIG_DAC_CTRL_FUNC_IN_IRAM is not set +# CONFIG_DAC_ISR_IRAM_SAFE is not set +# CONFIG_DAC_ENABLE_DEBUG_LOG is not set +CONFIG_DAC_DMA_AUTO_16BIT_ALIGN=y +# end of ESP-Driver:DAC Configurations + +# +# ESP-Driver:GPIO Configurations +# +# CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL is not set +# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:GPIO Configurations + +# +# ESP-Driver:GPTimer Configurations +# +CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y +# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set +# CONFIG_GPTIMER_ISR_CACHE_SAFE is not set +CONFIG_GPTIMER_OBJ_CACHE_SAFE=y +# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:GPTimer Configurations + +# +# ESP-Driver:I2C Configurations +# +# CONFIG_I2C_ISR_IRAM_SAFE is not set +# CONFIG_I2C_ENABLE_DEBUG_LOG is not set +# CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2 is not set +CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y +# end of ESP-Driver:I2C Configurations + +# +# ESP-Driver:I2S Configurations +# +# CONFIG_I2S_ISR_IRAM_SAFE is not set +# CONFIG_I2S_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:I2S Configurations + +# +# ESP-Driver:LEDC Configurations +# +# CONFIG_LEDC_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:LEDC Configurations + +# +# ESP-Driver:MCPWM Configurations +# +CONFIG_MCPWM_ISR_HANDLER_IN_IRAM=y +# CONFIG_MCPWM_ISR_CACHE_SAFE is not set +# CONFIG_MCPWM_CTRL_FUNC_IN_IRAM is not set +CONFIG_MCPWM_OBJ_CACHE_SAFE=y +# CONFIG_MCPWM_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:MCPWM Configurations + +# +# ESP-Driver:PCNT Configurations +# +# CONFIG_PCNT_CTRL_FUNC_IN_IRAM is not set +# CONFIG_PCNT_ISR_IRAM_SAFE is not set +# CONFIG_PCNT_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:PCNT Configurations + +# +# ESP-Driver:RMT Configurations +# +CONFIG_RMT_ENCODER_FUNC_IN_IRAM=y +CONFIG_RMT_TX_ISR_HANDLER_IN_IRAM=y +CONFIG_RMT_RX_ISR_HANDLER_IN_IRAM=y +# CONFIG_RMT_RECV_FUNC_IN_IRAM is not set +# CONFIG_RMT_TX_ISR_CACHE_SAFE is not set +# CONFIG_RMT_RX_ISR_CACHE_SAFE is not set +CONFIG_RMT_OBJ_CACHE_SAFE=y +# CONFIG_RMT_ENABLE_DEBUG_LOG is not set +# CONFIG_RMT_ISR_IRAM_SAFE is not set +# end of ESP-Driver:RMT Configurations + +# +# ESP-Driver:Sigma Delta Modulator Configurations +# +# CONFIG_SDM_CTRL_FUNC_IN_IRAM is not set +# CONFIG_SDM_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:Sigma Delta Modulator Configurations + +# +# ESP-Driver:SPI Configurations +# +# CONFIG_SPI_MASTER_IN_IRAM is not set +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +# CONFIG_SPI_SLAVE_IN_IRAM is not set +CONFIG_SPI_SLAVE_ISR_IN_IRAM=y +# end of ESP-Driver:SPI Configurations + +# +# ESP-Driver:TWAI Configurations +# +# CONFIG_TWAI_ISR_IN_IRAM is not set +# CONFIG_TWAI_ISR_CACHE_SAFE is not set +# CONFIG_TWAI_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:TWAI Configurations + +# +# ESP-Driver:UART Configurations +# +# CONFIG_UART_ISR_IN_IRAM is not set +# end of ESP-Driver:UART Configurations + +# +# ESP-Driver:UHCI Configurations +# +# CONFIG_UHCI_ISR_HANDLER_IN_IRAM is not set +# CONFIG_UHCI_ISR_CACHE_SAFE is not set +# CONFIG_UHCI_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:UHCI Configurations + +# +# Hardware Settings +# + +# +# Chip revision +# +CONFIG_ESP32_REV_MIN_0=y +# CONFIG_ESP32_REV_MIN_1 is not set +# CONFIG_ESP32_REV_MIN_1_1 is not set +# CONFIG_ESP32_REV_MIN_2 is not set +# CONFIG_ESP32_REV_MIN_3 is not set +# CONFIG_ESP32_REV_MIN_3_1 is not set +CONFIG_ESP32_REV_MIN=0 +CONFIG_ESP32_REV_MIN_FULL=0 +CONFIG_ESP_REV_MIN_FULL=0 + +# +# Maximum Supported ESP32 Revision (Rev v3.99) +# +CONFIG_ESP32_REV_MAX_FULL=399 +CONFIG_ESP_REV_MAX_FULL=399 +CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL=0 +CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL=99 + +# +# Maximum Supported ESP32 eFuse Block Revision (eFuse Block Rev v0.99) +# +# end of Chip revision + +# +# 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 is not set +# CONFIG_ESP_MAC_USE_CUSTOM_MAC_AS_BASE_MAC is not set +# end of MAC Config + +# +# Sleep Config +# +# CONFIG_ESP_SLEEP_POWER_DOWN_FLASH is not set +CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND=y +# CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU is not set +CONFIG_ESP_SLEEP_RTC_BUS_ISO_WORKAROUND=y +# CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND is not set +CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY=2000 +# CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION is not set +# CONFIG_ESP_SLEEP_DEBUG is not set +CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS=y +# end of Sleep Config + +# +# RTC Clock Config +# +CONFIG_RTC_CLK_SRC_INT_RC=y +# CONFIG_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_RTC_CLK_SRC_INT_8MD256 is not set +CONFIG_RTC_CLK_CAL_CYCLES=1024 +# end of RTC Clock Config + +# +# Peripheral Control +# +CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=y +CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=y +# end of Peripheral Control + +# +# Main XTAL Config +# +# CONFIG_XTAL_FREQ_26 is not set +# CONFIG_XTAL_FREQ_32 is not set +CONFIG_XTAL_FREQ_40=y +# CONFIG_XTAL_FREQ_AUTO is not set +CONFIG_XTAL_FREQ=40 +# end of Main XTAL Config + +# +# Power Supplier +# + +# +# Brownout Detector +# +CONFIG_ESP_BROWNOUT_DET=y +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_0=y +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7 is not set +CONFIG_ESP_BROWNOUT_DET_LVL=0 +CONFIG_ESP_BROWNOUT_USE_INTR=y +# end of Brownout Detector +# end of Power Supplier + +CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y +CONFIG_ESP_INTR_IN_IRAM=y +# end of Hardware Settings + +# +# ESP-MM: Memory Management Configurations +# +# end of ESP-MM: Memory Management Configurations + +# +# Partition API Configuration +# +# end of Partition API Configuration + +# +# Power Management +# +CONFIG_PM_SLEEP_FUNC_IN_IRAM=y +# CONFIG_PM_ENABLE is not set +CONFIG_PM_SLP_IRAM_OPT=y +# end of Power Management + +# +# ESP Ringbuf +# +# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set +# end of ESP Ringbuf + +# +# ESP-ROM +# +CONFIG_ESP_ROM_PRINT_IN_IRAM=y +# end of ESP-ROM + +# +# ESP Security Specific +# +# end of ESP Security Specific + +# +# ESP System Settings +# +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160 + +# +# Memory +# +# CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE is not set + +# +# Non-backward compatible options +# +# CONFIG_ESP_SYSTEM_ESP32_SRAM1_REGION_AS_IRAM is not set +# end of Non-backward compatible options +# end of Memory + +# +# Trace memory +# +# CONFIG_ESP32_TRAX is not set +CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x0 +# end of Trace memory + +CONFIG_ESP_SYSTEM_IN_IRAM=y +# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set +CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y +# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set +CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS=0 + +# +# Memory protection +# +# end of Memory protection + +CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584 +CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y +# CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set +# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set +CONFIG_ESP_MAIN_TASK_AFFINITY=0x0 +CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 +CONFIG_ESP_CONSOLE_UART_DEFAULT=n +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y +CONFIG_ESP_CONSOLE_UART_CUSTOM=n +CONFIG_ESP_CONSOLE_NONE=n +CONFIG_ESP_CONSOLE_UART=n +# CONFIG_ESP_CONSOLE_UART_NUM is not set +CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=0 +# CONFIG_ESP_CONSOLE_UART_BAUDRATE is not set +CONFIG_ESP_INT_WDT=y +CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 +CONFIG_ESP_INT_WDT_CHECK_CPU1=y +CONFIG_ESP_TASK_WDT_EN=y +CONFIG_ESP_TASK_WDT_INIT=y +# CONFIG_ESP_TASK_WDT_PANIC is not set +CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP_PANIC_HANDLER_IRAM is not set +# CONFIG_ESP_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP_DEBUG_OCDAWARE=y +# CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 is not set +CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4=y +# CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE is not set +# end of ESP System Settings + +# +# IPC (Inter-Processor Call) +# +CONFIG_ESP_IPC_ENABLE=y +CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 +CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y +CONFIG_ESP_IPC_ISR_ENABLE=y +# end of IPC (Inter-Processor Call) + +# +# ESP Timer (High Resolution Timer) +# +CONFIG_ESP_TIMER_IN_IRAM=y +# CONFIG_ESP_TIMER_PROFILING is not set +CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y +CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y +CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 +CONFIG_ESP_TIMER_INTERRUPT_LEVEL=1 +# CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL is not set +CONFIG_ESP_TIMER_TASK_AFFINITY=0x0 +CONFIG_ESP_TIMER_TASK_AFFINITY_CPU0=y +CONFIG_ESP_TIMER_ISR_AFFINITY_CPU0=y +# CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD is not set +CONFIG_ESP_TIMER_IMPL_TG0_LAC=y +# end of ESP Timer (High Resolution Timer) + +# +# FreeRTOS +# + +# +# Kernel +# +# CONFIG_FREERTOS_SMP is not set +# CONFIG_FREERTOS_UNICORE is not set +CONFIG_FREERTOS_HZ=100 +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y +CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 +# CONFIG_FREERTOS_USE_IDLE_HOOK is not set +# CONFIG_FREERTOS_USE_TICK_HOOK is not set +CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 +# CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY is not set +CONFIG_FREERTOS_USE_TIMERS=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_NAME="Tmr Svc" +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0 is not set +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU1 is not set +CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_CORE_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 +CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 +CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 +# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set +# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set +# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set +# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set +# end of Kernel + +# +# Port +# +CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y +# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set +CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y +# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set +# CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set +CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y +CONFIG_FREERTOS_ISR_STACKSIZE=1536 +CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y +# CONFIG_FREERTOS_FPU_IN_ISR is not set +CONFIG_FREERTOS_TICK_SUPPORT_CORETIMER=y +CONFIG_FREERTOS_CORETIMER_0=y +# CONFIG_FREERTOS_CORETIMER_1 is not set +CONFIG_FREERTOS_SYSTICK_USES_CCOUNT=y +# CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set +# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set +# end of Port + +# +# Extra +# +# end of Extra + +CONFIG_FREERTOS_PORT=y +CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y +CONFIG_FREERTOS_DEBUG_OCDAWARE=y +CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y +CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y +CONFIG_FREERTOS_NUMBER_OF_CORES=2 +CONFIG_FREERTOS_IN_IRAM=y +# end of FreeRTOS + +# +# Hardware Abstraction Layer (HAL) and Low Level (LL) +# +CONFIG_HAL_ASSERTION_EQUALS_SYSTEM=y +# CONFIG_HAL_ASSERTION_DISABLE is not set +# CONFIG_HAL_ASSERTION_SILENT is not set +# CONFIG_HAL_ASSERTION_ENABLE is not set +CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2 +# end of Hardware Abstraction Layer (HAL) and Low Level (LL) + +# +# Heap memory debugging +# +CONFIG_HEAP_POISONING_DISABLED=y +# CONFIG_HEAP_POISONING_LIGHT is not set +# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set +CONFIG_HEAP_TRACING_OFF=y +# CONFIG_HEAP_TRACING_STANDALONE is not set +# CONFIG_HEAP_TRACING_TOHOST is not set +# CONFIG_HEAP_USE_HOOKS is not set +# CONFIG_HEAP_TASK_TRACKING is not set +# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set +# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set +# end of Heap memory debugging + +# +# Log +# +CONFIG_LOG_VERSION_1=y +# CONFIG_LOG_VERSION_2 is not set +CONFIG_LOG_VERSION=1 + +# +# Log Level +# +# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set +# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set +# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set +# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y +# CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set +# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set +CONFIG_LOG_MAXIMUM_LEVEL=3 + +# +# Level Settings +# +# CONFIG_LOG_MASTER_LEVEL is not set +CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y +# CONFIG_LOG_TAG_LEVEL_IMPL_NONE is not set +# CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST is not set +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST=y +# CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY is not set +CONFIG_LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP=y +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31 +# end of Level Settings +# end of Log Level + +# +# Format +# +# CONFIG_LOG_COLORS is not set +CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y +# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set +# end of Format + +# +# Settings +# +CONFIG_LOG_MODE_TEXT_EN=y +CONFIG_LOG_MODE_TEXT=y +# end of Settings + +CONFIG_LOG_IN_IRAM=y +# end of Log + +# +# mbedTLS +# +CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y +# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set +# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set +CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +# CONFIG_MBEDTLS_DYNAMIC_BUFFER is not set +# CONFIG_MBEDTLS_DEBUG is not set + +# +# mbedTLS v3.x related +# +# CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 is not set +# CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH is not set +# CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set +# CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set +CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y +# CONFIG_MBEDTLS_SSL_KEYING_MATERIAL_EXPORT is not set +CONFIG_MBEDTLS_PKCS7_C=y +# end of mbedTLS v3.x related + +# +# Certificate Bundle +# +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set +# CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEPRECATED_LIST is not set +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS=200 +# end of Certificate Bundle + +# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set +# CONFIG_MBEDTLS_CMAC_C is not set +CONFIG_MBEDTLS_HARDWARE_AES=y +CONFIG_MBEDTLS_GCM_SUPPORT_NON_AES_CIPHER=y +CONFIG_MBEDTLS_HARDWARE_MPI=y +# CONFIG_MBEDTLS_LARGE_KEY_SOFTWARE_MPI is not set +CONFIG_MBEDTLS_HARDWARE_SHA=y +CONFIG_MBEDTLS_ROM_MD5=y +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY is not set +CONFIG_MBEDTLS_HAVE_TIME=y +# CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set +# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set +CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y +CONFIG_MBEDTLS_SHA1_C=y +CONFIG_MBEDTLS_SHA512_C=y +# CONFIG_MBEDTLS_SHA3_C is not set +CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y +# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set +# CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set +# CONFIG_MBEDTLS_TLS_DISABLED is not set +CONFIG_MBEDTLS_TLS_SERVER=y +CONFIG_MBEDTLS_TLS_CLIENT=y +CONFIG_MBEDTLS_TLS_ENABLED=y + +# +# TLS Key Exchange Methods +# +# CONFIG_MBEDTLS_PSK_MODES is not set +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y +# end of TLS Key Exchange Methods + +CONFIG_MBEDTLS_SSL_RENEGOTIATION=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +# CONFIG_MBEDTLS_SSL_PROTO_GMTSSL1_1 is not set +# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set +CONFIG_MBEDTLS_SSL_ALPN=y +CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y +CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y + +# +# Symmetric Ciphers +# +CONFIG_MBEDTLS_AES_C=y +# CONFIG_MBEDTLS_CAMELLIA_C is not set +# CONFIG_MBEDTLS_DES_C is not set +# CONFIG_MBEDTLS_BLOWFISH_C is not set +# CONFIG_MBEDTLS_XTEA_C is not set +CONFIG_MBEDTLS_CCM_C=y +CONFIG_MBEDTLS_GCM_C=y +# CONFIG_MBEDTLS_NIST_KW_C is not set +# end of Symmetric Ciphers + +# CONFIG_MBEDTLS_RIPEMD160_C is not set + +# +# Certificates +# +CONFIG_MBEDTLS_PEM_PARSE_C=y +CONFIG_MBEDTLS_PEM_WRITE_C=y +CONFIG_MBEDTLS_X509_CRL_PARSE_C=y +CONFIG_MBEDTLS_X509_CSR_PARSE_C=y +# end of Certificates + +CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_PK_PARSE_EC_EXTENDED=y +CONFIG_MBEDTLS_PK_PARSE_EC_COMPRESSED=y +# CONFIG_MBEDTLS_DHM_C is not set +CONFIG_MBEDTLS_ECDH_C=y +CONFIG_MBEDTLS_ECDSA_C=y +# CONFIG_MBEDTLS_ECJPAKE_C is not set +CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y +CONFIG_MBEDTLS_ECP_NIST_OPTIM=y +# CONFIG_MBEDTLS_ECP_FIXED_POINT_OPTIM is not set +# CONFIG_MBEDTLS_POLY1305_C is not set +# CONFIG_MBEDTLS_CHACHA20_C is not set +# CONFIG_MBEDTLS_HKDF_C is not set +# CONFIG_MBEDTLS_THREADING_C is not set +CONFIG_MBEDTLS_ERROR_STRINGS=y +# CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION is not set +# end of mbedTLS + +# +# LibC +# +CONFIG_LIBC_NEWLIB=y +CONFIG_LIBC_MISC_IN_IRAM=y +CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=y +# CONFIG_LIBC_NEWLIB_NANO_FORMAT is not set +CONFIG_LIBC_TIME_SYSCALL_USE_RTC_HRT=y +# CONFIG_LIBC_TIME_SYSCALL_USE_RTC is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_HRT is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_NONE is not set +# end of LibC + +# +# PThreads +# +CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_PTHREAD_STACK_MIN=768 +CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY=y +# CONFIG_PTHREAD_DEFAULT_CORE_0 is not set +# CONFIG_PTHREAD_DEFAULT_CORE_1 is not set +CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" +# end of PThreads + +# +# MMU Config +# +CONFIG_MMU_PAGE_SIZE_64KB=y +CONFIG_MMU_PAGE_MODE="64KB" +CONFIG_MMU_PAGE_SIZE=0x10000 +# end of MMU Config + +# +# Main Flash configuration +# + +# +# SPI Flash behavior when brownout +# +CONFIG_SPI_FLASH_BROWNOUT_RESET_XMC=y +CONFIG_SPI_FLASH_BROWNOUT_RESET=y +# end of SPI Flash behavior when brownout + +# +# Optional and Experimental Features (READ DOCS FIRST) +# + +# +# Features here require specific hardware (READ DOCS FIRST!) +# +CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50 +# CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set +# CONFIG_SPI_FLASH_FORCE_ENABLE_C6_H2_SUSPEND is not set +CONFIG_SPI_FLASH_PLACE_FUNCTIONS_IN_IRAM=y +# end of Optional and Experimental Features (READ DOCS FIRST) +# end of Main Flash configuration + +# +# SPI Flash driver +# +# CONFIG_SPI_FLASH_VERIFY_WRITE is not set +# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set +# CONFIG_SPI_FLASH_SHARE_SPI1_BUS is not set +# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set +CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y +CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 +CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 +CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192 +# CONFIG_SPI_FLASH_SIZE_OVERRIDE is not set +# CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED is not set +# CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST is not set + +# +# Auto-detect flash chips +# +CONFIG_SPI_FLASH_VENDOR_XMC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_GD_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_ISSI_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_MXIC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_WINBOND_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP=y +# CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP is not set +# CONFIG_SPI_FLASH_SUPPORT_TH_CHIP is not set +# end of Auto-detect flash chips + +CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=y +# end of SPI Flash driver + +# +# LoRa common options +# +CONFIG_LORA_ROLE_TX=y +# CONFIG_LORA_ROLE_RX is not set +# end of LoRa common options + +# +# Joystick input +# +CONFIG_JOYSTICK_ADC_CHANNEL=6 +CONFIG_JOYSTICK_ADC_LEVEL_LEFT=600 +CONFIG_JOYSTICK_ADC_LEVEL_UP=1200 +CONFIG_JOYSTICK_ADC_LEVEL_PRESS=1900 +CONFIG_JOYSTICK_ADC_LEVEL_DOWN=2600 +CONFIG_JOYSTICK_ADC_LEVEL_RIGHT=3300 +CONFIG_JOYSTICK_ADC_TOLERANCE=150 +# end of Joystick input + +# +# LoRa radio (LR1121) +# +CONFIG_LORA_SPI_HOST=2 +CONFIG_LORA_PIN_CS=12 +CONFIG_LORA_PIN_MOSI=10 +CONFIG_LORA_PIN_MISO=9 +CONFIG_LORA_PIN_SCK=11 +CONFIG_LORA_PIN_RST=-1 +CONFIG_LORA_PIN_BUSY=13 +CONFIG_LORA_PIN_DIO1=-1 +CONFIG_LORA_FREQ_MHZ=868 +CONFIG_LORA_BW_KHZ=125 +CONFIG_LORA_SF=7 +CONFIG_LORA_CR=5 +# end of LoRa radio (LR1121) + +# +# Display +# +CONFIG_DISPLAY_DRIVER="ST7735" +CONFIG_DISPLAY_I2C_PORT=0 +CONFIG_DISPLAY_I2C_ADDR=60 +CONFIG_DISPLAY_PIN_SDA=7 +CONFIG_DISPLAY_PIN_RST=-1 +CONFIG_DISPLAY_PIN_SCL=8 +CONFIG_DISPLAY_WIDTH=128 +CONFIG_DISPLAY_HEIGHT=160 +# end of Display +# end of Component config + +# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set + +# Deprecated options for backward compatibility +# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set +# CONFIG_NO_BLOBS is not set +# CONFIG_ESP32_NO_BLOBS is not set +# CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set +# CONFIG_ESP32_COMPATIBLE_PRE_V3_1_BOOTLOADERS is not set +# CONFIG_APP_ROLLBACK_ENABLE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set +CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y +# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set +CONFIG_LOG_BOOTLOADER_LEVEL=3 +# CONFIG_FLASH_ENCRYPTION_ENABLED is not set +# CONFIG_FLASHMODE_QIO is not set +# CONFIG_FLASHMODE_QOUT is not set +CONFIG_FLASHMODE_DIO=y +# CONFIG_FLASHMODE_DOUT is not set +CONFIG_MONITOR_BAUD=115200 +CONFIG_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y +# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set +# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set +CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y +# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set +CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_CXX_EXCEPTIONS is not set +CONFIG_STACK_CHECK_NONE=y +# CONFIG_STACK_CHECK_NORM is not set +# CONFIG_STACK_CHECK_STRONG is not set +# CONFIG_STACK_CHECK_ALL is not set +# CONFIG_WARN_WRITE_STRINGS is not set +CONFIG_ADC2_DISABLE_DAC=y +# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set +# CONFIG_MCPWM_ISR_IRAM_SAFE is not set +# CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set +CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y +CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 +# CONFIG_ESP_SYSTEM_PD_FLASH is not set +CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y +CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y +# CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL is not set +# CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC is not set +# CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256 is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set +CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y +# CONFIG_ESP32_XTAL_FREQ_26 is not set +CONFIG_ESP32_XTAL_FREQ_40=y +# CONFIG_ESP32_XTAL_FREQ_AUTO is not set +CONFIG_ESP32_XTAL_FREQ=40 +CONFIG_BROWNOUT_DET=y +CONFIG_ESP32_BROWNOUT_DET=y +CONFIG_BROWNOUT_DET_LVL_SEL_0=y +CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_0=y +# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7 is not set +CONFIG_BROWNOUT_DET_LVL=0 +CONFIG_ESP32_BROWNOUT_DET_LVL=0 +CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y +# CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set +CONFIG_ESP32_DEFAULT_CPU_FREQ_160=y +# CONFIG_ESP32_DEFAULT_CPU_FREQ_240 is not set +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=160 +CONFIG_TRACEMEM_RESERVE_DRAM=0x0 +# CONFIG_ESP32_PANIC_PRINT_HALT is not set +CONFIG_ESP32_PANIC_PRINT_REBOOT=y +# CONFIG_ESP32_PANIC_SILENT_REBOOT is not set +CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_MAIN_TASK_STACK_SIZE=3584 +CONFIG_CONSOLE_UART_DEFAULT=n +# CONFIG_CONSOLE_UART_CUSTOM is not set +CONFIG_CONSOLE_UART_NONE=y +CONFIG_ESP_CONSOLE_UART_NONE=y +# CONFIG_CONSOLE_UART is not set +# CONFIG_CONSOLE_UART_NUM is not set +# CONFIG_CONSOLE_UART_BAUDRATE is not set +CONFIG_INT_WDT=y +CONFIG_INT_WDT_TIMEOUT_MS=300 +CONFIG_INT_WDT_CHECK_CPU1=y +CONFIG_TASK_WDT=y +CONFIG_ESP_TASK_WDT=y +# CONFIG_TASK_WDT_PANIC is not set +CONFIG_TASK_WDT_TIMEOUT_S=5 +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP32_DEBUG_OCDAWARE=y +# CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set +CONFIG_IPC_TASK_STACK_SIZE=1024 +CONFIG_TIMER_TASK_STACK_SIZE=3584 +CONFIG_TIMER_TASK_PRIORITY=1 +CONFIG_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_TIMER_QUEUE_LENGTH=10 +# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set +# CONFIG_HAL_ASSERTION_SILIENT is not set +# CONFIG_NEWLIB_NANO_FORMAT is not set +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y +CONFIG_ESP32_TIME_SYSCALL_USE_RTC_HRT=y +CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y +# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_HRT is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set +CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_ESP32_PTHREAD_STACK_MIN=768 +CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set +CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set +# End of deprecated options diff --git a/components/common/CMakeLists.txt b/components/common/CMakeLists.txt new file mode 100644 index 0000000..00e01f0 --- /dev/null +++ b/components/common/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "common.c" + INCLUDE_DIRS "include" + PRIV_REQUIRES spi_flash +) diff --git a/components/common/Kconfig b/components/common/Kconfig new file mode 100644 index 0000000..27a183e --- /dev/null +++ b/components/common/Kconfig @@ -0,0 +1,17 @@ +menu "LoRa common options" + +choice LORA_ROLE + prompt "Device role" + default LORA_ROLE_TX + help + Виберіть роль прошивки за замовчуванням. Для кожної збірки можна + вказати власні sdkconfig.defaults з потрібним вибором. + + config LORA_ROLE_TX + bool "Transmitter" + + config LORA_ROLE_RX + bool "Receiver" +endchoice + +endmenu diff --git a/components/common/common.c b/components/common/common.c new file mode 100644 index 0000000..3049ce7 --- /dev/null +++ b/components/common/common.c @@ -0,0 +1,44 @@ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_chip_info.h" +#include "esp_flash.h" +#include "esp_system.h" +#include "esp_log.h" + +#define TAG "common" + +void common_print_boot_info(void) +{ + esp_chip_info_t chip_info; + uint32_t flash_size = 0; + esp_chip_info(&chip_info); + + ESP_LOGI(TAG, "Chip: %s, cores: %d, features: %s%s%s%s", + CONFIG_IDF_TARGET, + chip_info.cores, + (chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "", + (chip_info.features & CHIP_FEATURE_BT) ? "BT" : "", + (chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "", + (chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4" : ""); + + unsigned major_rev = chip_info.revision / 100; + unsigned minor_rev = chip_info.revision % 100; + ESP_LOGI(TAG, "Revision: v%u.%u", major_rev, minor_rev); + + if (esp_flash_get_size(NULL, &flash_size) != ESP_OK) { + ESP_LOGW(TAG, "Cannot read flash size"); + } else { + ESP_LOGI(TAG, "Flash: %" PRIu32 " MB %s", + flash_size / (uint32_t)(1024 * 1024), + (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); + } + + ESP_LOGI(TAG, "Min free heap: %" PRIu32 " bytes", esp_get_minimum_free_heap_size()); + + for (int i = 3; i > 0; i--) { + ESP_LOGI(TAG, "Start in %d...", i); + vTaskDelay(pdMS_TO_TICKS(500)); + } +} diff --git a/components/common/include/common.h b/components/common/include/common.h new file mode 100644 index 0000000..2ce28ea --- /dev/null +++ b/components/common/include/common.h @@ -0,0 +1,5 @@ +#pragma once + +#include "esp_chip_info.h" + +void common_print_boot_info(void); diff --git a/components/esp_lora_1121/CMakeLists.txt b/components/esp_lora_1121/CMakeLists.txt new file mode 100755 index 0000000..ae3db37 --- /dev/null +++ b/components/esp_lora_1121/CMakeLists.txt @@ -0,0 +1,20 @@ +file(GLOB_RECURSE SRCS src/*.c + src/lr1121_printers/*.c + src/lr1121_modem/*.c + src/lr1121_common/*.c + src/lr11xx_driver/*.c +) + +set(INCLUDE_DIRS + include + include/lr1121_printers + include/lr1121_modem + include/lr1121_common + include/lr11xx_driver +) + +idf_component_register( + SRCS ${SRCS} + INCLUDE_DIRS ${INCLUDE_DIRS} + REQUIRES esp_timer esp_driver_i2c esp_driver_spi esp_driver_gpio +) diff --git a/components/esp_lora_1121/LICENSE b/components/esp_lora_1121/LICENSE new file mode 100755 index 0000000..d645695 --- /dev/null +++ b/components/esp_lora_1121/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/components/esp_lora_1121/README.md b/components/esp_lora_1121/README.md new file mode 100755 index 0000000..cd59f0a --- /dev/null +++ b/components/esp_lora_1121/README.md @@ -0,0 +1,117 @@ +# LoRa LR1121 + +[![Component Registry](https://components.espressif.com/components/waveshare/esp_lora_1121/badge.svg)](https://components.espressif.com/components/waveshare/esp_lora_1121) + +LR1121 transceiver driver, LR1121 is a multi-band, ultra-low-power RF transceiver. + +| LoRa controller | Communication interface | Component name | Link to datasheet | +| :--------------: | :---------------------: | :------------: | :---------------: | +| LR1121 | SPI | esp_lora_1121 | [WIKI](https://files.waveshare.com/wiki/Core1121/LR1121_H2_DS_v2_0.pdf) | + +## Add to project + +Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/). +You can add them to your project via `idf.py add-dependancy`, e.g. +``` + idf.py add-dependency waveshare/esp_lora_1121==1.0.0 +``` + +Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). + +## Example use +### Initialization of the SPI bus +```c +static esp_err_t spi_bus_init() { + spi_bus_config_t buscfg = { + .mosi_io_num = TEST_SPI_MOSI, + .miso_io_num = TEST_SPI_MISO, + .sclk_io_num = TEST_SPI_CLK, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = 64, + }; + + spi_device_interface_config_t devcfg = { + .clock_speed_hz = TEST_SPI_CLK_SPEED_HZ, // 8 MHz + .mode = 0, // SPI mode 0: CPOL=0, CPHA=0 + .spics_io_num = -1, + .queue_size = 1, + }; + // Initialize SPI bus + ESP_ERROR_CHECK(spi_bus_initialize(TEST_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO)); + ESP_ERROR_CHECK(spi_bus_add_device(TEST_SPI_NUM, &devcfg, &spi_handle)); + + return ESP_OK; +} +``` +### LoRa initialization and configuration +```c + lora_init_io_context(&lr1121,TEST_SPI_CS,TEST_SPI_RESET,TEST_SPI_BUSY,TEST_SPI_INT); // Initialize the I/O context for the LR1121 + lora_init_io(&lr1121); // Initialize the I/O for the LR1121 + + lora_spi_init(&lr1121, spi_handle); // Initialize the SPI interface for the LR1121 + + printf( "===== LR11xx Ping-Pong example =====\n\n" ); + printf( "LR11XX driver version: %s\n", lr11xx_driver_version_get_version_string( ) ); + + // Initialize the system + lora_system_init(&lr1121); + // Print the version number for verification + lora_print_version(&lr1121); + // Initialize the LoRa radio + lora_radio_init(&lr1121); + + lora_init_irq(&lr1121, isr); // Initialize the interrupt service routine + + + ASSERT_LR11XX_RC( lr11xx_system_set_dio_irq_params( &lr1121, IRQ_MASK, 0 ) ); + ASSERT_LR11XX_RC( lr11xx_system_clear_irq_status( &lr1121, LR11XX_SYSTEM_IRQ_ALL_MASK ) ); + +``` +### Set the data to be sent and send it. +```c + memcpy( buffer_tx, ping_msg, PING_PONG_PREFIX_SIZE ); + buffer_tx[PING_PONG_PREFIX_SIZE] = ( uint8_t ) 0; + buffer_tx[ITERATION_INDEX] = ( uint8_t ) ( iteration ); + for( int i = PING_PONG_PREFIX_SIZE + 1 + 1; i < PAYLOAD_LENGTH; i++ ) + { + buffer_tx[i] = i; + } + + ASSERT_LR11XX_RC( lr11xx_regmem_write_buffer8( &lr1121, buffer_tx, PAYLOAD_LENGTH ) ); + ASSERT_LR11XX_RC( lr11xx_radio_set_tx( &lr1121, 0 ) ); +``` +### Read the current time and check if the alarm has been triggered +```c + while (1) + { + if(irq_flag) + lora_irq_process( &lr1121, IRQ_MASK ); + vTaskDelay(1 / portTICK_PERIOD_MS); // Short delay to control the loop speed + } +``` + +### Running results +```shell + Interrupt flags = 0x00000004 + Interrupt flags (after filtering) = 0x00000004 + Tx done + Sent message PING, iteration 50 + + Interrupt flags = 0x00000038 + Interrupt flags (after filtering) = 0x00000008 + Rx done + Packet content - (7 bytes): + 50 4F 4E 47 00 00 33 + Packet status: + - RSSI packet = -6 dBm + - Signal RSSI packet = -5 dBm + - SNR packet = 15 dB + Received message PONG, iteration 52 + + Interrupt flags = 0x00000004 + Interrupt flags (after filtering) = 0x00000004 + Tx done + Sent message PING, iteration 52 +------------ +``` diff --git a/components/esp_lora_1121/idf_component.yml b/components/esp_lora_1121/idf_component.yml new file mode 100755 index 0000000..fa40d52 --- /dev/null +++ b/components/esp_lora_1121/idf_component.yml @@ -0,0 +1,6 @@ +dependencies: + idf: + version: '>=5.3.0' + +description: Board Support Package for LoRa 1121 +version: 1.0.0 diff --git a/components/esp_lora_1121/include/esp_lora_1121.h b/components/esp_lora_1121/include/esp_lora_1121.h new file mode 100755 index 0000000..429612f --- /dev/null +++ b/components/esp_lora_1121/include/esp_lora_1121.h @@ -0,0 +1,80 @@ +#ifndef WAVESHARE_LORA_SPI_H +#define WAVESHARE_LORA_SPI_H + +#include +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include "esp_timer.h" +#include "esp_log.h" + +#include "lr11xx_bootloader.h" +#include "lr11xx_hal.h" +#include "lr11xx_system.h" +#include "lr11xx_radio.h" +#include "lr11xx_regmem.h" +#include "lr11xx_lr_fhss.h" +#include "lr11xx_driver_version.h" + + +#include "lr1121_modem_helper.h" +#include "lr1121_modem_system_types.h" + +#include "lr1121_modem_common.h" +#include "lr1121_modem_modem.h" +#include "lr1121_modem_hal.h" +#include "lr1121_modem_system.h" +#include "lr1121_modem_bsp.h" +#include "lr1121_modem_radio.h" + +#include "lr11xx_bootloader_types_str.h" +#include "lr11xx_crypto_engine_types_str.h" +#include "lr11xx_lr_fhss_types_str.h" +#include "lr11xx_radio_types_str.h" +#include "lr11xx_rttof_types_str.h" +#include "lr11xx_system_types_str.h" +#include "lr11xx_types_str.h" +#include "lr11xx_printf_info.h" +#include "lr1121_modem_printf_info.h" + +#include "lr1121_common.h" + +// #define USE_LR11XX_CRC_OVER_SPI + +typedef struct lr1121_s +{ + int cs; + int reset; + int busy; + int irq; + int led; + spi_device_handle_t spi; +} lr1121_t; + +/** + * @brief Initializes the radio I/Os pins context + * + * @param [in] context Radio abstraction + */ +void lora_init_io_context(const void *context,int cs,int reset,int busy,int irq); +/** + * @brief Initializes the radio I/Os pins interface + * + * @param [in] context Radio abstraction + */ +void lora_init_io( const void* context ); + +void lora_init_irq(const void *context, gpio_isr_t handler); + +void lora_spi_init(const void* context, spi_device_handle_t spi); +void lora_spi_write_bytes(const void* context,const uint8_t *wirte,const uint16_t wirte_length); +void lora_spi_read_bytes(const void* context, uint8_t *read,const uint16_t read_length); +/** + * @brief Flush the modem event queue + * + * @param [in] context Radio abstraction + * + * @returns Modem-E response code + */ +lr1121_modem_response_code_t lr1121_modem_board_event_flush( const void* context ); + +#endif diff --git a/components/esp_lora_1121/include/lr1121_common/lr1121_common.h b/components/esp_lora_1121/include/lr1121_common/lr1121_common.h new file mode 100755 index 0000000..d9a8b73 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_common/lr1121_common.h @@ -0,0 +1,162 @@ +#ifndef LR1121_COMMON_H +#define LR1121_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lr11xx_driver/lr11xx_radio_types.h" +#include "lr11xx_driver/lr11xx_radio.h" + +#include "lr11xx_driver/lr11xx_system_types.h" +#include "lr11xx_driver/lr11xx_regmem.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + + #define SMTC_SHIELD_LR11XX_SUBGHZ_FREQ_MIN 150000000 + #define SMTC_SHIELD_LR11XX_SUBGHZ_FREQ_MAX 960000000 + + #define SMTC_SHIELD_LR112X_2GHZ_FREQ_MIN 2000000000 + #define SMTC_SHIELD_LR112X_2GHZ_FREQ_MAX 2100000000 + + #define SMTC_SHIELD_LR112X_2_4GHZ_FREQ_MIN 2400000000 + #define SMTC_SHIELD_LR112X_2_4GHZ_FREQ_MAX 2500000000 + + #define SMTC_SHIELD_LR11XX_MIN_PWR -9 + #define SMTC_SHIELD_LR11XX_MAX_PWR 22 + + #define SMTC_SHIELD_LR112X_MIN_PWR_HF -18 + #define SMTC_SHIELD_LR112X_MAX_PWR_HF 13 + + /*! + * @brief Stringify constants + */ +#define xstr( a ) str( a ) +#define str( a ) #a + + /*! + * @brief Helper macro that returned a human-friendly message if a command does not return LR11XX_STATUS_OK + * + * @remark The macro is implemented to be used with functions returning a @ref lr11xx_status_t + * + * @param[in] rc Return code + */ +#define ASSERT_LR11XX_RC( rc ) \ +{ \ + const lr11xx_status_t status = rc; \ + if( status != LR11XX_STATUS_OK ) \ + { \ + if( status == LR11XX_STATUS_ERROR ) \ + { \ + printf( "In %s - %s (line %d): %s\n", __FILE__, __func__, __LINE__, \ + xstr( LR11XX_STATUS_ERROR ) ); \ + } \ + } \ +} + + /* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + + /* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief Power amplifier and output power configurations structure definition + */ +typedef struct smtc_shield_lr11xx_pa_pwr_cfg_s +{ + int8_t power; + lr11xx_radio_pa_cfg_t pa_config; +} smtc_shield_lr11xx_pa_pwr_cfg_t; + +/** + * @brief External 32MHz oscillator configuration structure definition + */ +typedef struct smtc_shield_lr11xx_xosc_cfg_s +{ + bool has_tcxo; + lr11xx_system_tcxo_supply_voltage_t supply; + uint32_t startup_time_in_tick; +} smtc_shield_lr11xx_xosc_cfg_t; + +/** + * @brief 32kHz clock configuration structure definition + */ +typedef struct smtc_shield_lr11xx_lfclk_cfg_s +{ + lr11xx_system_lfclk_cfg_t lf_clk_cfg; + bool wait_32k_ready; +} smtc_shield_lr11xx_lfclk_cfg_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/** + * @see smtc_shield_lr11xx_get_rssi_calibration_table + */ +const lr11xx_radio_rssi_calibration_table_t* smtc_shield_lr11xx_get_rssi_calibration_table( + const uint32_t rf_freq_in_hz ); + + /** + * @see smtc_shield_lr11xx_get_rf_switch_cfg + */ +const lr11xx_system_rfswitch_cfg_t* smtc_shield_lr11xx_common_get_rf_switch_cfg( void ); + +/** + * @see smtc_shield_lr11xx_get_reg_mode + */ +lr11xx_system_reg_mode_t smtc_shield_lr11xx_common_get_reg_mode( void ); + +/** + * @see smtc_shield_lr11xx_get_lfclk_cfg + */ +const smtc_shield_lr11xx_lfclk_cfg_t* smtc_shield_lr11xx_common_get_lfclk_cfg( void ); + +/** + * @see smtc_shield_lr11xx_get_pa_pwr_cfg + */ +const smtc_shield_lr11xx_pa_pwr_cfg_t* smtc_shield_lr1121mb1gis_get_pa_pwr_cfg( const uint32_t rf_freq_in_hz, + int8_t expected_output_pwr_in_dbm ); + +const uint8_t smtc_shield_lr11xx_common_compute_lora_ldro( const lr11xx_radio_lora_sf_t sf, const lr11xx_radio_lora_bw_t bw ); + +/*! + * \brief Given the length of a BPSK frame, in bits, calculate the space necessary to hold the frame after differential + * encoding, in bits. + * + * \param [in] bpsk_pld_len_in_bits Length of a BPSK frame, in bits + * \returns Space required for DBPSK frame, after addition of start/stop bits [bits] + */ +static inline int smtc_dbpsk_get_pld_len_in_bits( int bpsk_pld_len_in_bits ) +{ + // Hold the last bit one extra bit-time + return bpsk_pld_len_in_bits + 2; +} + +/*! + * \brief Given the length of a BPSK frame, in bits, calculate the space necessary to hold the frame after differential + * encoding, in bytes. + * + * \param [in] bpsk_pld_len_in_bits Length of a BPSK frame, in bits + * \returns Space required for DBPSK frame, after addition of start/stop bits [bytes] + */ +static inline int smtc_dbpsk_get_pld_len_in_bytes( int bpsk_pld_len_in_bits ) +{ + return ( smtc_dbpsk_get_pld_len_in_bits( bpsk_pld_len_in_bits ) + 7 ) >> 3; +} + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_bsp.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_bsp.h new file mode 100755 index 0000000..3347272 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_bsp.h @@ -0,0 +1,294 @@ +/*! + * @file lr1121_modem_bsp.h + * + * @brief BSP driver for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_BSP_H +#define LR1121_MODEM_BSP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr1121_modem_common.h" +#include "lr1121_modem_bsp_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief This command gets the board-specific correction offset for transmission power to be used (signed integer in + * dB). + * + * @param [in] context Chip implementation context + * @param [out] tx_power_offset Tx power offset correction in dBm + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_tx_power_offset( const void* context, int8_t* tx_power_offset ); + +/*! + * @brief This command sets the board-specific correction offset for transmission power to be used + * + * The offset depends on the board design and antenna matching and is expressed in dB (signed integer). + * The default value is -2dB. + * + * @param [in] context Chip implementation context + * @param [in] tx_power_offset Tx power offset correction in dBm + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_tx_power_offset( const void* context, const int8_t tx_power_offset ); + +/*! + * @brief Configure the Output RF per power + * + * This command overload the output RF configuration for the given @p expected_power. + * + * @param [in] context Chip implementation context + * @param [in] output_power_configs Array of Tx output power configurations to set. It is up to the user to ensure it + * contains at least @p n_output_power_configs + * @param [in] n_output_power_configs Number of output power configuration to set. Valid values in range [1:41] + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_output_power_config( + const void* context, const lr1121_modem_output_power_config_t* output_power_configs, + uint8_t n_output_power_configs ); + +/*! + * @brief This command gets the 41 Tx output power configurations for all Tx powers + * + * In case an @p expected_power is not available due to hardware limitation, all corresponding values in @p + * lr1121_modem_output_power_config_t wil be set to 0x7F. + * + * @param [in] context Chip implementation context + * @param [out] output_power_config Tx output power configuration block list, \see + * lr1121_modem_output_power_config_list_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_output_power_config( + const void* context, lr1121_modem_output_power_config_list_t output_power_config ); + +/*! + * @brief Configure RF output configuration + * + * @param [in] context Chip implementation context + * @param [in] output RF output @ref lr1121_modem_bsp_radio_pa_selection_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_rf_output( const void* context, + const lr1121_modem_bsp_radio_pa_selection_t output ); + +/*! + * @brief Get RF output configuration + * + * @param [in] context Chip implementation context + * @param [out] output RF output @ref lr1121_modem_bsp_radio_pa_selection_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_rf_output( const void* context, + const lr1121_modem_bsp_radio_pa_selection_t* output ); + +/*! + * @brief Get the Crystal error + * + * @param [in] context Chip implementation context + * @param [out] crystal_error_ppm Crystal error in PPM + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_crystal_error( const void* context, uint32_t* crystal_error_ppm ); + +/*! + * @brief Set the Crystal error of the MCU to fine adjust the rx window for LoRaWAN + * + * The default value is: + * - When the Low-Frequency clock selected is @ref LR1121_MODEM_SYSTEM_LFCLK_RC : 16000 PPM + * - Otherwise: 50 PPM + * + * @param [in] context Chip implementation context + * @param [in] crystal_error_ppm Crystal error in PPM + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_crystal_error( const void* context, const uint32_t crystal_error_ppm ); + +/*! + * @brief Get the XOSC trimming capacitor + * + * @param [in] context Chip implementation context + * @param [out] capa_trim_a Trimming value for capacitance XTA + * @param [out] capa_trim_b Trimming value for capacitance XTB + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_xosc_capa_trim_a_b( const void* context, uint8_t* capa_trim_a, + uint8_t* capa_trim_b ); + +/*! + * @brief Set the XOSC trimming capacitor + * + * @p capa_trim_a and @p capa_trim_b both takes value within [0, 47]. It configures the corresponding trimming + * capacitance following \f$ C_{x,uF} = 0.47 \times capa\_trim\_x + N_x \f$ Where: + * - \f$ N_a = 11.3 \f$ + * - \f$ N_b = 10.1 \f$ + * + * So that \f$ C_{a,uF} \f$ goes from 11.3pF to 33.4pF ; and \f$ C_{b,uF} \f$ goes from 10.1pF to 32.2pF. + * + * Default value is 0x12 for both @p capa_trim_a and @p capa_trim_b which makes respectively 19.7pF and 18.5pF. + * + * Note that when the chip enters sleep mode, the capacitances XTA and XTB are respectively set to 13.6pF and 12.4pF. + * When leaving sleep mode, XTA and XTB are set to the value configured by this command (or to the default ones). + * + * @param [in] context Chip implementation context + * @param [in] capa_trim_a Trimming value for capacitance XTA + * @param [in] capa_trim_b Trimming value for capacitance XTB + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_xosc_capa_trim_a_b( const void* context, const uint8_t capa_trim_a, + const uint8_t capa_trim_b ); + +/** + * @brief Get the instantaneous power consumption table + * + * The instantaneous power consumption values are used to evaluate the power consumption statistics. + * + * @param [in] context Chip implementation context + * @param [out] consumption_per_power Table of instantaneous consumption + * @return Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_tx_power_consumption_ua( + const void* context, lr1121_modem_tx_power_consumption_list_t consumption_per_power ); + +/** + * @brief Set the instantaneous power consumption table + * + * The instantaneous power consumption values are used to evaluate the power consumption statistics. + * + * @param [in] context Chip implementation context + * @param [in] consumption_per_power Array of the instantaneous power consumption to set. Setting the field + * consumed_power_ua to 0 reset the instantaneous consumption corresponding to the given Tx RF power to 0. It is up to + * the caller to ensure this array contains at least n_consumption_per_power elements + * @param [in] n_consumption_per_power Number of elements in the array @p consumption_per_power to set. Valid values + * in range of [1:41] + * @return Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_tx_power_consumption_ua( + const void* context, const lr1121_modem_tx_power_consumption_value_t* consumption_per_power, + uint8_t n_consumption_per_power ); + +/** + * @brief Get LoRa Rx consumption configured + * + * The instantaneous power consumption values are used to evaluate the power consumption statistics. + * + * @param [in] context Chip implementation context + * @param [out] rx_consumption The LoRa instantaneous Rx consumption configured + * @return lr1121_modem_response_code_t + */ +lr1121_modem_response_code_t lr1121_modem_get_lora_rx_power_consumption_ua( + const void* context, lr1121_modem_rx_power_consumption_t* rx_consumption ); + +/** + * @brief Set the LoRa Rx power consumption configuration + * + * The instantaneous power consumption values are used to evaluate the power consumption statistics. + * + * @param [in] context Chip implementation context + * @param [in] rx_consumption The LoRa Rx consumption structure to set. Setting one field to 0 reset it to its internal + * value. + * @return lr1121_modem_response_code_t + */ +lr1121_modem_response_code_t lr1121_modem_set_lora_rx_power_consumption_ua( + const void* context, const lr1121_modem_rx_power_consumption_t* rx_consumption ); + +/** + * @brief Get GFSK Rx power consumption configured + * + * The instantaneous power consumption values are used to evaluate the power consumption statistics. + * + * @param [in] context Chip implementation context + * @param [out] rx_consumption The GFSK instantaneous Rx consumption configured + * @return lr1121_modem_response_code_t + */ +lr1121_modem_response_code_t lr1121_modem_get_gfsk_rx_power_consumption_ua( + const void* context, lr1121_modem_rx_power_consumption_t* rx_consumption ); + +/** + * @brief Set the GFSK Rx power consumption configuration + * + * The instantaneous power consumption values are used to evaluate the power consumption statistics. + * + * @param [in] context Chip implementation context + * @param [in] rx_consumption The GFSK Rx consumption structure to set. Setting one field to 0 reset it to its internal + * value. + * @return lr1121_modem_response_code_t + */ +lr1121_modem_response_code_t lr1121_modem_set_gfsk_rx_power_consumption_ua( + const void* context, const lr1121_modem_rx_power_consumption_t* rx_consumption ); + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_BSP_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_bsp_types.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_bsp_types.h new file mode 100755 index 0000000..f9d027b --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_bsp_types.h @@ -0,0 +1,203 @@ +/*! + * @file lr1121_modem_bsp_types.h + * + * @brief BSP driver types for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_BSP_TYPES_H +#define LR1121_MODEM_BSP_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/*! + * @brief Number of output power config blocks + */ +#define LR1121_MODEM_NB_OUTPUT_POWER_CONFIG_BLOCKS ( 41 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Power Amplifier Selection values + * + * - Low-power Power Amplifier can reach up to 14dBm + * - High-power Power Amplifier can reach up to 22 dBm + */ +typedef enum +{ + LR1121_MODEM_BSP_RADIO_PA_SEL_LP = 0x00, //!< Low-power Power Amplifier + LR1121_MODEM_BSP_RADIO_PA_SEL_HP = 0x01, //!< High-power Power Amplifier + LR1121_MODEM_BSP_RADIO_PA_SEL_LP_HP_LF = + 0x02, //!< Automatic selection between Low-power and High-power depending on requested power + LR1121_MODEM_BSP_RADIO_PA_SEL_HF = 0x03, //!< High Frequency Power Amplifier +} lr1121_modem_bsp_radio_pa_selection_t; + +/*! + * @brief Ramping time for PA + * + * This parameter is the ramping time of the PA. A high value improves spectral quality. + */ +typedef enum +{ + LR1121_MODEM_RAMP_16_US = 0x00, //!< 16 us Ramp Time + LR1121_MODEM_RAMP_32_US = 0x01, //!< 32 us Ramp Time + LR1121_MODEM_RAMP_48_US = 0x02, //!< 48 us Ramp Time (Default) + LR1121_MODEM_RAMP_64_US = 0x03, //!< 64 us Ramp Time + LR1121_MODEM_RAMP_80_US = 0x04, //!< 80 us Ramp Time + LR1121_MODEM_RAMP_96_US = 0x05, //!< 96 us Ramp Time + LR1121_MODEM_RAMP_112_US = 0x06, //!< 112 us Ramp Time + LR1121_MODEM_RAMP_128_US = 0x07, //!< 128 us Ramp Time + LR1121_MODEM_RAMP_144_US = 0x08, //!< 144 us Ramp Time + LR1121_MODEM_RAMP_160_US = 0x09, //!< 160 us Ramp Time + LR1121_MODEM_RAMP_176_US = 0x0A, //!< 176 us Ramp Time + LR1121_MODEM_RAMP_192_US = 0x0B, //!< 192 us Ramp Time + LR1121_MODEM_RAMP_208_US = 0x0C, //!< 208 us Ramp Time + LR1121_MODEM_RAMP_240_US = 0x0D, //!< 240 us Ramp Time + LR1121_MODEM_RAMP_272_US = 0x0E, //!< 272 us Ramp Time + LR1121_MODEM_RAMP_304_US = 0x0F, //!< 304 us Ramp Time +} lr1121_modem_ramp_time_t; + +/*! + * @brief Select power amplifier supply source + */ +typedef enum +{ + LR1121_MODEM_PA_REG_SUPPLY_VREG = 0x00, //!< Power amplifier supplied by the main regulator + LR1121_MODEM_PA_REG_SUPPLY_VBAT = 0x01 //!< Power amplifier supplied by the battery +} lr1121_modem_pa_reg_supply_t; + +/*! + * @brief Power Amplifier selection for RF output configuration table + */ +typedef enum +{ + LR1121_MODEM_OUTPUT_POWER_CONFIGURATION_PA_SEL_LP = 0x00, //!< Low-power Power Amplifier + LR1121_MODEM_OUTPUT_POWER_CONFIGURATION_PA_SEL_HP = 0x01, //!< High-power Power Amplifier + LR1121_MODEM_OUTPUT_POWER_CONFIGURATION_PA_SEL_HF = 0x02, //!< High Frequency Power Amplifier +} lr1121_modem_output_power_configuration_pa_sel_t; + +/*! + * @brief Output Power Config structure + * + * A power configuration for an @p expected_power can be reset to its internal configuration by setting one of the other + * structure field to value 0x7F. + * + * @p pa_duty_cycle controls the duty cycle of Power Amplifier according to: + * \f$ dutycycle = 0.2 + 0.04 \times pa\_duty\_cycle \f$ + * It can be used to adapt the TX multi-band operation using a single-matching network. + * + * The allowed duty cycle values for LPA are from 0.2 to 0.48 (by step of 0.04). Therefore possible values for + * pa_duty_cycle go from 0 to 7. + * + * The allowed duty cycle values for HPA go from 0.2 to 0.36 (by step of 0.04). Therefore in this case, the possible + * values for pa_duty_cycle go from 0 to 4. + * + * @p pa_hp_sel controls the number of slices for HPA according to: \f$ \#slices = pa\_hp\_sel + 1 \f$ + */ +typedef struct +{ + uint8_t expected_power; //!< Expected power in dBm + uint8_t configured_power; //!< Configured power in dBm + lr1121_modem_pa_reg_supply_t pa_supply; //!< Power Amplifier regulator supply source + uint8_t pa_duty_cycle; //!< Power Amplifier duty cycle (Default 0x04) + lr1121_modem_output_power_configuration_pa_sel_t pa_sel; //!< Power Amplifier selection + uint8_t pa_hp_sel; //!< Number of slices for HPA (Default 0x07) + lr1121_modem_ramp_time_t pa_ramp_time; //!< Power amplifier ramp time +} lr1121_modem_output_power_config_t; + +/*! + * @brief Output power config type + */ +typedef lr1121_modem_output_power_config_t + lr1121_modem_output_power_config_list_t[LR1121_MODEM_NB_OUTPUT_POWER_CONFIG_BLOCKS]; + +/** + * @brief Tx power consumption mapping structure + */ +typedef struct +{ + int8_t tx_power_dbm; //!< Tx RF output power + uint32_t consumed_power_ua; //!< Corresponding instantaneous power consumption (uA) +} lr1121_modem_tx_power_consumption_value_t; + +/** + * @brief Power consumption table + */ +typedef lr1121_modem_tx_power_consumption_value_t + lr1121_modem_tx_power_consumption_list_t[LR1121_MODEM_NB_OUTPUT_POWER_CONFIG_BLOCKS]; + +/** + * @brief Rx power consumption structure + * + * The Rx power consumption depends on the Rx boosted configuration. + * + * @see lr1121_modem_radio_cfg_rx_boosted + */ +typedef struct +{ + uint32_t consumption_rx_boosted_off_ua; //!< Instantaneous consumption without Rx boosted disabled (uA) + uint32_t consumption_rx_boosted_on_ua; //!< Instantaneous consumption without Rx boosted enable (uA) +} lr1121_modem_rx_power_consumption_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_BSP_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_common.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_common.h new file mode 100755 index 0000000..b95dbc1 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_common.h @@ -0,0 +1,107 @@ +/*! + * @file lr1121_modem_common.h + * + * @brief modem driver common definition for LR1121 + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_COMMON_H +#define LR1121_MODEM_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Command group identifier + */ +typedef enum +{ + LR1121_MODEM_GROUP_ID_BSP = 0x0600, //!< Group ID for BSP commands + LR1121_MODEM_GROUP_ID_MODEM = 0x0601, //!< Group ID for modem commands + LR1121_MODEM_GROUP_ID_LORAWAN = 0x0602, //!< Group ID for LoRaWAN commands + LR1121_MODEM_GROUP_ID_RELAY = 0x0603, //!< Group ID for relay commands +} lr1121_modem_api_group_id_t; + +/*! + * @brief Command return code (RC) + */ +typedef enum +{ + LR1121_MODEM_RESPONSE_CODE_OK = 0x00, //!< Driver command executed successfully + LR1121_MODEM_RESPONSE_CODE_UNKOWN = 0x01, //!< Command code unknown + LR1121_MODEM_RESPONSE_CODE_NOT_IMPLEMENTED = 0x02, //!< Command not implemented + LR1121_MODEM_RESPONSE_CODE_NOT_INITIALIZED = 0x03, //!< Command not initialized + LR1121_MODEM_RESPONSE_CODE_INVALID = 0x04, //!< Invalid command parameters + LR1121_MODEM_RESPONSE_CODE_BUSY = 0x05, //!< Command cannot be executed now + LR1121_MODEM_RESPONSE_CODE_FAIL = 0x06, //!< Command execution failed + LR1121_MODEM_RESPONSE_CODE_BAD_CRC = 0x08, //!< CRC check failed + LR1121_MODEM_RESPONSE_CODE_BAD_SIZE = 0x0A, //!< Size check failed + LR1121_MODEM_RESPONSE_CODE_FRAME_ERROR = 0x0F, //!< SPI command checksum failed or CRC failed + LR1121_MODEM_RESPONSE_CODE_NO_TIME = 0x10, //!< Modem time is not synchronized + LR1121_MODEM_RESPONSE_CODE_NO_EVENT = 0x12, //!< No Event +} lr1121_modem_response_code_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_COMMON_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_driver_version.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_driver_version.h new file mode 100755 index 0000000..62f3a4d --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_driver_version.h @@ -0,0 +1,56 @@ +/*! + * @file lr1121_modem_driver_version.h + * + * @brief Placeholder to keep the version of LR1121 driver. + * + * The Clear BSD License + * Copyright Semtech Corporation 2024. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR1121_MODEM_DRIVER_VERSION_H +#define LR1121_MODEM_DRIVER_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Version of the driver + */ +#define LR1121_MODEM_DRIVER_VERSION "v1.0.0" + +/** + * @brief Returns version string + */ +const char* lr1121_modem_driver_version_get_version_string( void ); + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_DRIVER_VERSION_H diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_hal.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_hal.h new file mode 100755 index 0000000..2dd4f3c --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_hal.h @@ -0,0 +1,227 @@ +/*! + * @file lr1121_modem_hal.h + * + * @brief Hardware Abstraction Layer (HAL) interface for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_HAL_H +#define LR1121_MODEM_HAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include +#include "lr1121_modem_common.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/*! + * @brief Status reported by the HAL layer + */ +typedef enum lr1121_modem_hal_status_e +{ + LR1121_MODEM_HAL_STATUS_OK = 0x00, //!< Operation terminated successfully + LR1121_MODEM_HAL_STATUS_ERROR = 0x01, //!< Operation terminated with error + LR1121_MODEM_HAL_STATUS_BAD_FRAME = 0x0F, //!< Bad frame detected in the exchange + LR1121_MODEM_HAL_STATUS_BUSY_TIMEOUT = 0xFF, //!< Timeout occured while waiting for Busy line state +} lr1121_modem_hal_status_t; + +/* + * ============================================================================ + * API definitions to be implemented by the user + * ============================================================================ + */ + +/*! + * @brief Return the computed CRC + * + * @param [in] crc_initial_value initial value of the CRC + * @param [in] buffer Buffer used to compute the CRC + * @param [out] crc CRC computed + * + * @returns CRC value + */ +inline static uint8_t lr1121_modem_compute_crc( const uint8_t crc_initial_value, const uint8_t* buffer, + uint16_t length ) +{ + uint8_t crc = crc_initial_value; + uint8_t extract; + uint8_t sum; + for( int i = 0; i < length; i++ ) + { + extract = *buffer; + for( uint8_t j = 8; j; j-- ) + { + sum = ( crc ^ extract ) & 0x01; + crc >>= 1; + if( sum ) + { + crc ^= 0x65; + } + extract >>= 1; + } + buffer++; + } + return crc; +} + +/*! + * Radio data transfer - write + * + * @remark Must be implemented by the upper layer + * + * @param [in] context Radio implementation parameters + * @param [in] command Pointer to the buffer to be transmitted + * @param [in] command_length Buffer size to be transmitted + * @param [in] data Pointer to the buffer to be transmitted + * @param [in] data_length Buffer size to be transmitted + * + * @returns Operation status + */ +lr1121_modem_hal_status_t lr1121_modem_hal_write( const void* context, const uint8_t* command, + const uint16_t command_length, const uint8_t* data, + const uint16_t data_length ); + +/*! + * Radio data transfer - read + * + * @remark Must be implemented by the upper layer + * + * @param [in] context Radio implementation parameters + * @param [in] command Pointer to the buffer to be transmitted + * @param [in] command_length Buffer size to be transmitted + * @param [out] data Pointer to the buffer to be received + * @param [in] data_length Buffer size to be received + * + * @returns Operation status + */ +lr1121_modem_hal_status_t lr1121_modem_hal_read( const void* context, const uint8_t* command, + const uint16_t command_length, uint8_t* data, + const uint16_t data_length ); + +/*! + * @brief Radio data transfer - write & read in single operation + * + * @remark Must be implemented by the upper layer + * @remark Only required by lr1121_system_get_status command + * + * @param [in] context Radio implementation parameters + * @param [in] command Pointer to the buffer to be transmitted + * @param [out] data Pointer to the buffer to be received + * @param [in] data_length Buffer size to be received + * + * @returns Operation status + */ +lr1121_modem_hal_status_t lr1121_modem_hal_write_read( const void* context, const uint8_t* command, uint8_t* data, + const uint16_t data_length ); + +/*! + * @brief Direct read from the SPI bus + * + * @remark Unlike @ref lr1121_modem_hal_read, this is a simple direct SPI bus SS/read/nSS operation. While reading the + * response data, the implementation of this function must ensure that only zero bytes (NOP) are written to the SPI bus. + * + * @remark Only required by the @ref lr1121_modem_system_get_status command + * + * @param [in] context Radio implementation parameters + * @param [out] data Pointer to the buffer to be received + * @param [in] data_length Buffer size to be received + * + * @returns Operation status + */ +lr1121_modem_hal_status_t lr1121_modem_hal_direct_read( const void* context, uint8_t* data, + const uint16_t data_length ); + +/*! + * Radio data transfer - write without wait the return code - this API is dedicated to the functions which reset the + * Modem-E + * + * @remark Must be implemented by the upper layer + * + * @param [in] context Radio implementation parameters + * @param [in] command Pointer to the buffer to be transmitted + * @param [in] command_length Buffer size to be transmitted + * @param [in] data Pointer to the buffer to be transmitted + * @param [in] data_length Buffer size to be transmitted + * + * @returns Operation status + */ +lr1121_modem_hal_status_t lr1121_modem_hal_write_without_rc( const void* context, const uint8_t* command, + const uint16_t command_length, const uint8_t* data, + const uint16_t data_length ); + +/*! + * Reset the radio + * + * @remark Must be implemented by the upper layer + * + * @param [in] context Radio implementation parameters + * + * @returns Operation status + */ +lr1121_modem_hal_status_t lr1121_modem_hal_reset( const void* context ); + +/*! + * Switch the radio in DFU mode + * + * @remark Must be implemented by the upper layer + * + * @param [in] context Radio implementation parameters + */ +void lr1121_modem_hal_enter_dfu( const void* context ); + +/*! + * Wake the radio up. + * + * @remark Must be implemented by the upper layer + * + * @param [in] context Radio implementation parameters + + * @returns Operation status + */ +lr1121_modem_hal_status_t lr1121_modem_hal_wakeup( const void* context ); + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_HAL_H diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_helper.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_helper.h new file mode 100755 index 0000000..1c902a5 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_helper.h @@ -0,0 +1,295 @@ +/*! + * @file lr1121_modem_helper.h + * + * @brief helper functions definition for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_HELPER_H +#define LR1121_MODEM_HELPER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr1121_modem_modem_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief LR1121 Modem helper status + */ +typedef enum +{ + LR1121_MODEM_HELPER_STATUS_OK = 0, + LR1121_MODEM_HELPER_STATUS_ERROR = 3, +} lr1121_modem_helper_status_t; + +/*! + * @brief TX status values + * + * @see LR1121_MODEM_LORAWAN_EVENT_TX_DONE + */ +typedef enum +{ + LR1121_MODEM_TX_NOT_SENT = 0x00, + LR1121_MODEM_UNCONFIRMED_TX = 0x01, + LR1121_MODEM_CONFIRMED_TX = 0x02, +} lr1121_modem_tx_done_event_t; + +/*! + * @brief Link check request values + */ +typedef enum +{ + LR1121_MODEM_LINK_CHECK_NOT_RECEIVED = 0x00, //!< Link check response has not been received + LR1121_MODEM_LINK_CHECK_RECEIVED = 0x01, //!< Link check response has been received +} lr1121_modem_link_check_event_t; + +/*! + * @brief Time request values + * + * @see LR1121_MODEM_LORAWAN_EVENT_LORAWAN_MAC_TIME + */ +typedef enum +{ + LR1121_MODEM_TIME_NOT_VALID = 0x00, //!< Time is not valid + LR1121_MODEM_TIME_VALID = 0x01, //!< Time is valid and has been synchronized + LR1121_MODEM_TIME_VALID_BUT_NOT_SYNC = 0x02, //!< Time is still valid but has not been synchronized +} lr1121_modem_mac_time_event_t; + +/*! + * @brief class b ping slot info + * + * @see LR1121_MODEM_LORAWAN_EVENT_CLASS_B_PING_SLOT_INFO + */ +typedef enum +{ + LR1121_MODEM_CLASS_B_PING_SLOT_INFO_NOT_ANSWERED = 0x00, + LR1121_MODEM_CLASS_B_PING_SLOT_INFO_ANSWERED = 0x01, +} lr1121_modem_class_b_ping_slot_info_t; + +/*! + * @brief class b ping slot status + * + * @see LR1121_MODEM_LORAWAN_EVENT_CLASS_B_STATUS + */ +typedef enum +{ + LR1121_MODEM_CLASS_B_PING_SLOT_STATUS_NOT_READY = 0x00, + LR1121_MODEM_CLASS_B_PING_SLOT_STATUS_READY = 0x01, +} lr1121_modem_class_b_ping_slot_status_t; + +/*! + * @brief Event status for Wake On Radio protocol status change + * + * @see LR1121_MODEM_LORAWAN_EVENT_RELAY_TX_DYNAMIC + */ +typedef enum +{ + LR1121_MODEM_RELAY_TX_DYNAMIC_WOR_DISABLED = 0x00, + LR1121_MODEM_RELAY_TX_DYNAMIC_WOR_ENABLED = 0x01, +} lr1121_modem_relay_tx_dynamic_status_t; + +/*! + * @brief Event status for relay Tx activation change + * + * @see LR1121_MODEM_LORAWAN_EVENT_RELAY_TX_MODE + */ +typedef enum +{ + LR1121_MODEM_RELAY_TX_MODE_DISABLED = 0x00, + LR1121_MODEM_RELAY_TX_MODE_ENABLED = 0x01, + LR1121_MODEM_RELAY_TX_MODE_DYNAMIC = 0x02, + LR1121_MODEM_RELAY_TX_MODE_DEVICE_CONTROLLED = 0x03, +} lr1121_modem_relay_tx_mode_status_t; + +/*! + * @brief Event status for relay synchronization change + * + * @see LR1121_MODEM_LORAWAN_EVENT_RELAY_TX_SYNC + */ +typedef enum +{ + LR1121_MODEM_RELAY_TX_SYNCHRONIZATION_INIT = 0x00, + LR1121_MODEM_RELAY_TX_UNSYNCHRONIZED = 0x01, + LR1121_MODEM_RELAY_TX_SYNCHRONIZED = 0x02, +} lr1121_modem_relay_tx_sync_status_t; + +/** + * @brief Event status on FUOTA done event + * + * @see LR1121_MODEM_LORAWAN_EVENT_FUOTA_DONE + */ +typedef enum +{ + LR1121_MODEM_FUOTA_STATUS_TERMINATED_SUCCESSFULLY = 0x00, //!< FUOTA terminated successfully + LR1121_MODEM_FUOTA_STATUS_FAILED = 0x01, //!< FUOTA failed + LR1121_MODEM_FUOTA_STATUS_ONGOING = 0xFF, //!< FUOTA is ongoing + LR1121_MODEM_FUOTA_STATUS_NOT_STARTED = 0xFE, //!< FUOTA is not started +} lr1121_modem_fuota_status_t; + +/** + * @brief Event status on test mode event + * + * @see LR1121_MODEM_LORAWAN_EVENT_TEST_MODE + */ +typedef enum +{ + LR1121_MODEM_TEST_MODE_STATUS_TX_NOT_SENT = 0x00, + LR1121_MODEM_TEST_MODE_STATUS_TX_SENT = 0x01, + LR1121_MODEM_TEST_MODE_STATUS_TERMINATED_ACTION = 0x02, +} lr1121_modem_test_mode_status_t; + +/** + * @brief Event status on regional duty cycle event + * + * The duty cycle status can be obtained by calling @ref lr1121_modem_get_duty_cycle_status. + * + * @see lr1121_modem_get_duty_cycle_status + */ +typedef enum +{ + LR1121_MODEM_REGINAL_DUTY_CYCLE_TX_ALLOWED = + 0x00, //!< Previously duty cycle constrained transmissions are now allowed + LR1121_MODEM_REGINAL_DUTY_CYCLE_TX_CONSTRAINED = 0x01, //!< Transmissions are constrained by regional duty cycle +} lr1121_modem_regional_duty_cycle_status_t; + +/** + * @brief Structure holding event-related data + */ +typedef struct +{ + lr1121_modem_lorawan_event_type_t event_type; //!< Type of the event + uint8_t missed_events; //!< Number of @p event_type events missed before the current one + union + { + struct + { + uint16_t count; + } reset; + struct + { + lr1121_modem_tx_done_event_t status; + } txdone; + struct + { + lr1121_modem_link_check_event_t status; + } link_check; + struct + { + lr1121_modem_mac_time_event_t status; + } mac_time; + struct + { + lr1121_modem_class_b_ping_slot_info_t status; + } ping_slot_info; + struct + { + lr1121_modem_class_b_ping_slot_status_t status; + } ping_slot_status; + struct + { + uint8_t mc_group_id; + } new_multicast_class_c_groupid; + struct + { + uint8_t mc_group_id; + } new_multicast_class_b_groupid; + struct + { + lr1121_modem_relay_tx_dynamic_status_t status; + } relay_tx_dynamic_status; + struct + { + lr1121_modem_relay_tx_mode_status_t status; + } relay_tx_mode_status; + struct + { + lr1121_modem_relay_tx_sync_status_t status; + } relay_tx_sync_status; + struct + { + lr1121_modem_fuota_status_t status; + } fuota_status; + struct + { + lr1121_modem_test_mode_status_t status; + } test_mode_status; + struct + { + lr1121_modem_regional_duty_cycle_status_t status; + } regional_duty_cycle_status; + } event_data; //!< Status data associated to the event +} lr1121_modem_event_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/** + * @brief Extract the event data contained in the event field buffer + * + * @param [in] context Chip implementation context + * @param [out] modem_event Struct containing the event data \see lr1121_modem_event_t + * + * @returns Operation status + */ +lr1121_modem_helper_status_t lr1121_modem_helper_get_event_data( const void* context, + lr1121_modem_event_t* modem_event ); + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_HELPER_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lorawan.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lorawan.h new file mode 100755 index 0000000..6714d41 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lorawan.h @@ -0,0 +1,1006 @@ +/*! + * @file lr1121_modem_lorawan.h + * + * @brief LoRaWAN driver for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_LORAWAN_H +#define LR1121_MODEM_LORAWAN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include +#include "lr1121_modem_common.h" +#include "lr1121_modem_lorawan_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Return the version of the LoRaWAN standard and of the regional parameters implemented + * + * @param [in] context Chip implementation context + * @param [out] lorawan_version LoRaWAN and regional parameters version implemented + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_lorawan_version( const void* context, + lr1121_modem_lorawan_version_t* lorawan_version ); + +/*! + * @brief Return the DeviceEUI + * + * @param [in] context Chip implementation context + * @param [out] dev_eui Device EUI buffer on 8 bytes + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_dev_eui( const void* context, lr1121_modem_dev_eui_t dev_eui ); + +/*! + * @brief Set the DeviceEUI + * + * The device EUI is stored in non-volatile upon this call but the keys are not derived. + * Refer to @ref lr1121_modem_derive_keys for keys derivation. + * + * @param [in] context Chip implementation context + * @param [in] dev_eui Device EUI buffer on 8 bytes + * + * @returns Operation status + * + * @see lr1121_modem_derive_keys + */ +lr1121_modem_response_code_t lr1121_modem_set_dev_eui( const void* context, const lr1121_modem_dev_eui_t dev_eui ); + +/*! + * @brief Return the join EUI + * + * @remark Join EUI is also known as Application Identifier in LoRaWan v1.0.4. + * + * @param [in] context Chip implementation context + * @param [out] join_eui Join EUI buffer on 8 bytes + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_join_eui( const void* context, lr1121_modem_join_eui_t join_eui ); + +/*! + * @brief Set the Join EUI + * + * The device EUI is stored in non-volatile upon this call but the keys are not derived. + * Refer to @ref lr1121_modem_derive_keys for keys derivation. + * + * @remark Join EUI is also known as Application Identifier in LoRaWan v1.0.4. + * + * @param [in] context Chip implementation context + * @param [in] join_eui Join EUI buffer on 8 bytes + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_join_eui( const void* context, const lr1121_modem_join_eui_t join_eui ); + +/*! + * @brief Set the network key + * + * The network key is named: + * - Application Key (AppKey) in LoRaWAN L2 1.0.4 Specification + * - NwkKey in LoRaWAN 1.1 Specification and after + * + * This key is reset by a factory reset @ref lr1121_modem_factory_reset. + * + * @param [in] context Chip implementation context + * @param [in] nwk_key Network Key buffer on 16 bytes + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_nwk_key( const void* context, const lr1121_modem_nwk_key_t nwk_key ); + +/*! + * @brief Set the application key + * + * The application key is named GenAppKey in LoRaWAN Remote Multicast Setup v1.0.0 Specification. + * + * This key is reset by a factory reset @ref lr1121_modem_factory_reset. + * + * @note This does not set the key named AppKey in LoRaWAN L2 1.0.4 Specification. To set the AppKey corresponding to + * LoRaWAN L2 1.0.4 Specification, use @ref lr1121_modem_set_nwk_key. + * + * @param [in] context Chip implementation context + * @param [in] app_key Application Key buffer on 16 bytes + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_app_key( const void* context, const lr1121_modem_app_key_t app_key ); + +/*! + * @brief Use the previously set of JoinEUI/DevEUI to derive the app keys used in the Semtech join server + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_derive_keys( const void* context ); + +/*! + * @brief Get the LoRaWAN device class + * + * @param [in] context Chip implementation context + * @param [out] modem_class LoRaWAN device class @ref lr1121_modem_classes_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_class( const void* context, lr1121_modem_classes_t* modem_class ); + +/*! + * @brief Set the LoRaWAN device class + * + * @param [in] context Chip implementation context + * @param [in] modem_class LoRaWAN device class @ref lr1121_modem_classes_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_class( const void* context, const lr1121_modem_classes_t modem_class ); + +/*! + * @brief Return the regulatory region + * + * @param [in] context Chip implementation context + * @param [out] region LoRaWAN regulatory region @ref lr1121_modem_regions_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_region( const void* context, lr1121_modem_regions_t* region ); + +/*! + * @brief Set the regulatory region + * + * Additionally this command resets the ADR profile to Network Server Controlled. If different ADR profile is desired, + * the profile needs to be set again. + * + * @param [in] context Chip implementation context + * @param [in] region LoRaWAN regulatory region @ref lr1121_modem_regions_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_region( const void* context, const lr1121_modem_regions_t region ); + +/*! + * @brief This command starts joining the network + * + * The join procedure started by this command stops either if the join succeed or if @ref lr1121_modem_leave_network is + * called to cancel the join procedure. In case of join failure, the procedure automatically starts again. + * + * During the join procedure no further transmissions can occur. When the network has been successfully joined, a Joined + * event is generated. If the device is already joined to a network, or is in the process of joining, this command has + * no effect. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_join( const void* context ); + +/*! + * @brief Leave joined network or cancel ongoing join process + * + * After leaving the network, no further transmissions can occur. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_leave_network( const void* context ); + +/*! + * @brief Get maximal length of next uplink payload + * + * This command returns the maximum application payload size possible according to the LoRaWAN regional + * parameters for the next transmission using the current data rate, while assuming no FOpts are present and that a + * device is not behind a repeater. + * + * @param [in] context Chip implementation context + * @param [out] tx_max_payload Maximum application payload size possible + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_next_tx_max_payload( const void* context, uint8_t* tx_max_payload ); + +/*! + * @brief Request uplink + * + * This command requests sending the given data on the specified port as an unconfirmed (0x00) or confirmed + * (0x01) frame. The request will be queued and the frame will be sent as soon as the current bandwidth usage of the + * regulatory region permits. A TxDone event is generated when the frame either has been sent, or if it couldn’t be sent + * because the specified data exceeded the maximum possible payload size. + * + * @param [in] context Chip implementation context + * @param [in] port LoRaWAN port + * @param [in] uplink_type Uplink type unconfirmed (0x00) or confirmed (0x01) @ref lr1121_modem_uplink_type_t + * @param [in] data Data buffer. It is up to the caller that @p data contains at least @p length elements + * @param [in] length Data length. Valid value in [0,242] + * + * @remark The application shall not use port 0 or the LoRaWAN test port 224 as well as the ports from 225 to 255 since + * they are reserved for future standardized application extensions. + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_request_tx( const void* context, const uint8_t port, + const lr1121_modem_uplink_type_t uplink_type, const uint8_t* data, + const uint8_t length ); + +/*! + * @brief Request empty uplink + * + * This command requests sending an empty payload on the specified port as an unconfirmed (0x00) or confirmed + * (0x01) frame. The request will be queued and the frame will be sent as soon as the current bandwidth usage of the + * regulatory region permits. A TxDone event is generated when the frame has been sent. + * + * @param [in] context Chip implementation context + * @param [in] is_fport_populated Indicate whether the fport should be included in the empty frame or not + * @param [in] port LoRaWAN port. Only meaningful if @p is_fport_populated is true + * @param [in] uplink_type Uplink type unconfirmed (0x00) or confirmed (0x01) @ref lr1121_modem_uplink_type_t + * + * @remark The application shall not use port 0 or the LoRaWAN test port 224 as well as the ports from 225 to 255 since + * they are reserved for future standardized application extensions. + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_request_empty_tx( const void* context, bool is_fport_populated, + const uint8_t port, + const lr1121_modem_uplink_type_t uplink_type ); + +/*! + * @brief Request emergency uplink + * + * An emergency uplink request has highest precedence than any other actions the chip may be doing, and the duty-cycle + * limitation is not honored. LoRaWAN payload size limitations still applies. + * + * It is intended to be used for alarm-like event that requires real time signaling. However, due to the relaxed + * limitations, it must be used with care. + * + * This command sends the given data on the specified port as an unconfirmed (0x00) or confirmed (0x01) frame + * immediately. It has higher priority than all other services and does not take duty cycle or payload size restrictions + * into account + * + * @param [in] context Chip implementation context + * @param [in] port LoRaWAN port + * @param [in] uplink_type Uplink type unconfirmed (0x00) or confirmed (0x01) @ref lr1121_modem_uplink_type_t + * @param [in] data Data buffer. It is up to the caller that @p data contains at least @p length elements + * @param [in] length Data length. Valid value in [0,242] + * + * @remark The application shall not use port 0 or the LoRaWAN test port 224 as well as the ports from 225 to 255 since + * they are reserved for future standardized application extensions. + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_emergency_tx( const void* context, const uint8_t port, + const lr1121_modem_uplink_type_t uplink_type, + const uint8_t* data, const uint8_t length ); + +/*! + * @brief Get the size of latest downlink + * + * This command must be called in the following sequence, without any other commands in between: + * 1. @ref lr1121_modem_get_downlink_data_size + * 2. @ref lr1121_modem_get_downlink_data + * 3. @ref lr1121_modem_get_downlink_metadata + * + * @param [in] context Chip implementation context + * @param [out] downlink_data_size Size of the received downlink + * @param [out] remaining_downlinks Number of remaining downlinks in the internal FiFo + * + * @see lr1121_modem_get_downlink_data, lr1121_modem_get_downlink_metadata + */ +lr1121_modem_response_code_t lr1121_modem_get_downlink_data_size( const void* context, uint8_t* downlink_data_size, + uint8_t* remaining_downlinks ); + +/*! + * @brief Read the last downlink data + * + * This command must be called in the following sequence, without any other commands in between: + * 1. @ref lr1121_modem_get_downlink_data_size + * 2. @ref lr1121_modem_get_downlink_data + * 3. @ref lr1121_modem_get_downlink_metadata + * + * @param [in] context Chip implementation context + * @param [in] buffer buffer containing the downlink data + * @param [in] downlink_data_size Size of the payload to read + * + * @see lr1121_modem_get_downlink_data_size, lr1121_modem_get_downlink_metadata + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_downlink_data( const void* context, uint8_t* buffer, + uint8_t downlink_data_size ); + +/*! + * @brief Get the metadata of the last downlink + * + * This command must be called in the following sequence, without any other commands in between: + * 1. @ref lr1121_modem_get_downlink_data_size + * 2. @ref lr1121_modem_get_downlink_data + * 3. @ref lr1121_modem_get_downlink_metadata + * + * @param [in] context Chip implementation context + * @param [out] metadata Downlink metada, @ref lr1121_modem_downlink_metadata_t + * + * @see lr1121_modem_get_downlink_data_size, lr1121_modem_get_downlink_data + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_downlink_metadata( const void* context, + lr1121_modem_downlink_metadata_t* metadata ); + +/*! + * @brief Get count of uplinks and duration since last received downlink + * + * @p lost_connection_counter is increased even in case of uplink retransmission (nb_trans), and is reset when a valid + * downlink is received. + * @p lost_connection_since_sec is reset when a downlink is received. + * + * @param [in] context Chip implementation context + * @param [out] lost_connection_counter Number of uplinks sent without downlink since last received downlink + * @param [out] lost_connection_since_sec Duration since last downlink received + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_lost_connection_counter( const void* context, + uint16_t* lost_connection_counter, + uint32_t* lost_connection_since_sec ); + +/*! + * @brief Get the LoRaWAN network type + * + * @param [in] context Chip implementation context + * @param [out] network_type Type of LoRaWAN network (see @ref lr1121_modem_network_type_t) + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_network_type( const void* context, + lr1121_modem_network_type_t* network_type ); + +/*! + * @brief Set the LoRaWAN network type + * + * By default the @ref LR1121_MODEM_LORAWAN_PUBLIC_NETWORK is set. + * + * @param [in] context Chip implementation context + * @param [in] network_type @ref lr1121_modem_network_type_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_network_type( const void* context, + const lr1121_modem_network_type_t network_type ); + +/*! + * @brief Get the LoRaWAN certification mode enable status + * + * @param [in] context Chip implementation context + * @param [out] enable @ref lr1121_modem_certification_mode_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_certification_mode( const void* context, + lr1121_modem_certification_mode_t* enable ); + +/*! + * @brief Activate/deactivate the LoRaWAN certification mode + * + * Upon certification activation, the LR1121 Modem start a join sequence. The duty-cycle is disabled after the join is + * accepted. The the LR1121 Modem automatically replies to certification test commands. + * + * The certification mode activation status is stored in non-volatile memory so that the LR1121 Modem can re-join with + * certification mode enable after a reset during certification test sequence. + * + * When certification tests are terminated, this command must be called to explicitly disable the certification mode. + * The duty-cycle is re-enabled when the certification mode is disabled. + * + * @param [in] context Chip implementation context + * @param [in] enable @ref lr1121_modem_certification_mode_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_certification_mode( const void* context, + const lr1121_modem_certification_mode_t enable ); + +/*! + * @brief Get Duty cycle status info + * + * The sign determines the meaning of the value: + * - duty_cycle >= 0: duty_cycle is the time budget in millisecond still available for transmission + * - duty_cycle < 0: Abs(duty_cycle) is the time in millisecond before it can start transmitting again + * + * @param [in] context Chip implementation context + * @param [out] duty_cycle Time in milliseconds (see notes for explanations) + * + * @note When duty cycle is deactivated, the returned value is 0. + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_duty_cycle_status( const void* context, int32_t* duty_cycle ); + +/*! + * @brief Get the available data rate mask + * + * One bit indicates one data rate. Bit n = 1 mean Data Rate n is available. + * + * @param [in] context Chip implementation context + * @param [out] available_data_rate Available data rate bit mask + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_available_data_rate( const void* context, uint16_t* available_data_rate ); + +/*! + * @brief Return the ADR profile type + * + * @param [in] context Chip implementation context + * @param [out] adr_profile ADR profile type @ref lr1121_modem_adr_profiles_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_adr_profile( const void* context, + lr1121_modem_adr_profiles_t* adr_profile ); + +/*! + * @brief Set the ADR profile and parameters + * + * @param [in] context Chip implementation context + * @param [in] adr_profile ADR profile type @ref lr1121_modem_adr_profiles_t + * @param [in] adr_custom_list custom ADR profile consisting of a list of 16 preferred data rates + * + * @remark each call to the function reinitialize the data rate distribution. + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_adr_profile( const void* context, + const lr1121_modem_adr_profiles_t adr_profile, + const uint8_t* adr_custom_list ); + +/*! + * @brief Set the join data rate distribution + * + * @param [in] context Chip implementation context + * @param [in] distribution 16-byte-array describing the distribution. It is up to the user to ensure it contains at + * least 16 bytes + * + * @remark each call to the function reinitialize the data rate distribution. + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_join_data_rate_distribution( + const void* context, const uint8_t distribution[LR1121_MODEM_DATARATE_DISTRIBUTION_LENGTH] ); + +/*! + * @brief Get the LoRaWAN number of retransmission + * + * @param [in] context Chip implementation context + * @param [out] nb_trans Number of retransmission + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_nb_trans( const void* context, uint8_t* nb_trans ); + +/*! + * @brief Set the LoRaWAN number of retransmission + * + * @param [in] context Chip implementation context + * @param [in] nb_trans Number of retransmission. Valid values in [1:15] + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_nb_trans( const void* context, const uint8_t nb_trans ); + +/*! + * @brief Get the ADR fallback mechanism parameters + * + * @param [in] context Chip implementation context + * @param [out] limit + * @param [out] delay + * + * @returns Operation status + * + * @see lr1121_modem_set_adr_ack_limit_delay + */ +lr1121_modem_response_code_t lr1121_modem_get_adr_ack_limit_delay( const void* context, uint8_t* limit, + uint8_t* delay ); + +/*! + * @brief Set the ADR fallback mechanism parameters + * + * The ADR fallback mechanism is described in LoRaWAN specificiation ADR backoff sequence. + * + * @param [in] context Chip implementation context + * @param [in] limit The ADR ACK limit. Valid values in [2:127] + * @param [in] delay The ADR ACK delay. Valid values in [2:127] + * + * @returns Operation status + * + * @see lr1121_modem_get_adr_ack_limit_delay + */ +lr1121_modem_response_code_t lr1121_modem_set_adr_ack_limit_delay( const void* context, const uint8_t limit, + const uint8_t delay ); + +/*! + * @brief Get the listen before talk state + * + * @param [in] context Chip implementation context + * @param [out] enable @ref lr1121_modem_lbt_mode_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_lbt_state( const void* context, lr1121_modem_lbt_mode_t* enable ); + +/*! + * @brief Activate the listen before talk + * + * @param [in] context Chip implementation context + * @param [in] enable @ref lr1121_modem_lbt_mode_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_lbt_state( const void* context, const lr1121_modem_lbt_mode_t enable ); + +/*! + * @brief Configure the listen before talk + * + * @param [in] context Chip implementation context + * @param [in] duration LBT duration in ms, default value is 5 ms + * @param [in] threshold LBT treshold in dBm, default value is -80 dBm + * @param [in] bandwidth LBT bandwidth in Hz, default value is 200000 Hz + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_lbt_params( const void* context, const uint32_t duration, + const int16_t threshold, const uint32_t bandwidth ); + +/*! + * @brief Get the listen before talk configuration + * + * @param [in] context Chip implementation context + * @param [out] duration LBT duration in ms, default value is 5 ms + * @param [out] threshold LBT treshold in dBm, default value is -80 dBm + * @param [out] bandwidth LBT bandwidth in Hz, default value is 200000 Hz + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_lbt_params( const void* context, uint32_t* duration, int16_t* threshold, + uint32_t* bandwidth ); + +/*! + * @brief Get the Carrier Sense Multiple Access (CSMA) state + * + * @param [in] context Chip implementation context + * @param [out] enable @ref lr1121_modem_csma_mode_t + * + * @returns Operation status + * + * @see lr1121_modem_set_csma_state, lr1121_modem_set_csma_params + */ +lr1121_modem_response_code_t lr1121_modem_get_csma_state( const void* context, lr1121_modem_csma_mode_t* enable ); + +/*! + * @brief Enable or disable Carrier Sense Multiple Access (CSMA) + * + * The CSMA mechanism is the one described in LoRa-Alliance document TR013-1.0.0 "Enabling CSMA for LoRaWAN". + * It is enabled by default. + * + * @param [in] context Chip implementation context + * @param [in] enable @ref lr1121_modem_csma_mode_t + * + * @returns Operation status + * + * @see lr1121_modem_get_csma_state, lr1121_modem_set_csma_params + */ +lr1121_modem_response_code_t lr1121_modem_set_csma_state( const void* context, const lr1121_modem_csma_mode_t enable ); + +/*! + * @brief Configure the Carrier Sense Multiple Access (CSMA) + * + * Refer to LoRa-Alliance document TR013-1.0.0 "Enabling CSMA for LoRaWAN" for details concerning the configuration. + * + * @param [in] context Chip implementation context + * @param [in] max_channel_change Maximum number of sensing to execute on a channel before selecting another one + * @param [in] backoff_enable Indicate if backoff is enabled or disabled + * @param [in] nb_backoff_max Maximal number of sensing per channel to conclude it is free + * + * @returns Operation status + * + * @see lr1121_modem_get_csma_state, lr1121_modem_set_csma_state + */ +lr1121_modem_response_code_t lr1121_modem_set_csma_params( const void* context, const uint8_t max_channel_change, + const lr1121_modem_csma_backoff_mode_t backoff_enable, + const uint8_t nb_backoff_max ); + +/*! + * @brief Get the Carrier Sense Multiple Access (CSMA) configuration + * + * @param [in] context Chip implementation context + * @param [out] max_channel_change Maximum number of sensing to execute on a channel before selecting another one + * @param [out] backoff_enable Indicate if backoff is enabled or disabled + * @param [out] nb_backoff_max Maximal number of sensing per channel to conclude it is free + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_csma_params( const void* context, uint8_t* max_channel_change, + uint8_t* backoff_enable, uint8_t* nb_backoff_max ); + +/*! + * @brief Request a MAC command uplink + * + * @param [in] context Chip implementation context + * @param [in] mac_request Mask of lr1121_modem_mac_request_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_mac_request_tx( const void* context, + const lr1121_modem_mac_request_bitmask_t mac_request ); + +/*! + * @brief Get time from LoRaWAN network + * + * Time expressed as number of seconds since GPS epoch (January 6th 1980, 00:00:00). + * The MAC time is available after a successfully responded call @ref lr1121_modem_mac_request_tx with @ref + * LR1121_MODEM_MAC_REQUEST_TIME bit set. + * + * @param [in] context Chip implementation context + * @param [out] gps_time_sec + * @param [out] gps_fractionnal_sec + * + * @returns Operation status + * + * @see lr1121_modem_mac_request_tx, lr1121_modem_mac_request_bitmask_t + */ +lr1121_modem_response_code_t lr1121_modem_get_lorawan_mac_time( const void* context, uint32_t* gps_time_sec, + uint32_t* gps_fractionnal_sec ); + +/*! + * @brief Get the link check result + * + * The Link Check Request is initiated by calling @ref lr1121_modem_mac_request_tx with @ref + * LR1121_MODEM_MAC_REQUEST_LINK_CHECK bit set. + * + * @param [in] context Chip implementation context + * @param [out] margin The link demodulation margin obtained from last network received Link Check Request in dB + * @param [out] gateway_count Number of gateways that received the last Link Check Request + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_link_check_data( const void* context, uint8_t* margin, + uint8_t* gateway_count ); + +/*! + * @brief Set the battery level + * + * The battery level is used by the LR1121 Modem to respond to Device Status Request from LoRaWAN Network Server. + * + * @param [in] context Chip implementation context + * @param [in] modem_vs_user Indicate if the battery level should be obtained from internal value or not + * @param [in] value Battery level value to use. Only effective if @p modem_vs_user is @ref + * LR1121_MODEM_BATTERY_LEVEL_FROM_USER_VALUE. Possible values: 0 means the end device is connected to an external power + * source, 1-254 is the battery level (1is the minimum, 254 the maximum), 255 means the end device was not able to + * measure the battery level + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_battery_level( + const void* context, const lr1121_modem_battery_level_source_value_t modem_vs_user, uint8_t value ); + +/*! + * @brief Get the current class B ping slot periodicity + * + * @param [in] context Chip implementation context + * @param [in] ping_slot_periodicity Current class B ping slot periodicity + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_class_b_ping_slot_periodicity( + const void* context, lr1121_modem_class_b_ping_slot_t* ping_slot_periodicity ); + +/*! + * @brief Set the class B ping slot periodicity + * + * @param [in] context Chip implementation context + * @param [out] ping_slot_periodicity Class B ping slot periodicity to set + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_class_b_ping_slot_periodicity( + const void* context, const lr1121_modem_class_b_ping_slot_t ping_slot_periodicity ); + +/*! + * @brief Get the current multicast group configuration + * + * @param [in] context Chip implementation context + * @param [in] mc_group_id Multicast group identifier + * @param [in] mc_group_address Multicast group address + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_multicast_group_config( const void* context, const uint8_t mc_group_id, + uint32_t* mc_group_address ); + +/*! + * @brief Set the multicast group configuration + * + * @param [in] context Chip implementation context + * @param [in] mc_group_id Multicast group identifier + * @param [in] mc_group_address Multicast group address + * @param [in] mc_nwkskey Multicast network session key for this group + * @param [in] mc_appskey Multicast application session key for this group + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_multicast_group_config( const void* context, const uint8_t mc_group_id, + const uint32_t mc_group_address, + const uint8_t* mc_nwkskey, + const uint8_t* mc_appskey ); + +/*! + * @brief Start a class C multicast session + * + * Several class C multicast sessions can run in parallel if configured with the same downlink_frequency and + * downlink_data_rate. + * + * To have the class C multicast session started immediately upon call to @ref + * lr1121_modem_start_session_multicast_class_c, it is recommended to start class C before calling it. + * + * @param [in] context Chip implementation context + * @param [in] mc_group_id Multicast group identifier + * @param [in] downlink_frequency Downlink frequency for this session + * @param [in] downlink_data_rate Datarate for this session + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_start_session_multicast_class_c( const void* context, + const uint8_t mc_group_id, + const uint32_t downlink_frequency, + const uint8_t downlink_data_rate ); + +/*! + * @brief Get the class C multicast session status + * + * @param [in] context Chip implementation context + * @param [in] mc_group_id Multicast group identifier to get the status + * @param [out] lr1121_modem_multicast_class_c_status Status of the multicast class C session corresponding to @p + * mc_group_id + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_multicast_class_c_session_status( + const void* context, const uint8_t mc_group_id, + lr1121_modem_multicast_class_c_status_t* lr1121_modem_multicast_class_c_status ); + +/*! + * @brief Stop given class C multicast session + * + * @param [in] context Chip implementation context + * @param [in] mc_group_id Multicast group identifier to stop session + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_stop_session_multicast_class_c( const void* context, + const uint8_t mc_group_id ); + +/*! + * @brief Stop all class C multicast sessions + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_stop_all_session_multicast_class_c( const void* context ); + +/*! + * @brief Start class B multicast session + * + * @param [in] context Chip implementation context + * @param [in] mc_group_id Multicast group identifier + * @param [in] downlink_frequency Downlink frequency for this session + * @param [in] downlink_data_rate Datarate for this session + * @param [in] ping_slot Ping slot periodicity for this session + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_start_session_multicast_class_b( + const void* context, const uint8_t mc_group_id, const uint32_t downlink_frequency, const uint8_t downlink_data_rate, + const lr1121_modem_class_b_ping_slot_t ping_slot ); + +/*! + * @brief Get current status of given class B multicast session + * + * @param [in] context Chip implementation context + * @param [in] mc_group_id Multicast group identifier + * @param [out] lr1121_modem_multicast_class_b_status Status of the multicast class B session corresponding to @p + * mc_group_id + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_multicast_class_b_session_status( + const void* context, const uint8_t mc_group_id, + lr1121_modem_multicast_class_b_status_t* lr1121_modem_multicast_class_b_status ); + +/*! + * @brief Stop given class B multicast session + * + * @param [in] context Chip implementation context + * @param [in] mc_group_id multicast group identifier to stop session + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_stop_session_multicast_class_b( const void* context, + const uint8_t mc_group_id ); +/*! + * @brief Stop all class B multicast sessions + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_stop_all_session_multicast_class_b( const void* context ); + +/** + * @brief Start the Applicative Layer Clock synchronization service + * + * This corresponds to the LoRaWAN Application Layer Clock Synchronization v2.0.0 available in LoRa Alliance document + * TS003-2.0.0. + * + * @param context Chip implementation context + * + * @return Operation status + * + * @see lr1121_modem_alc_sync_stop_service, lr1121_modem_alc_sync_get_time, lr1121_modem_alc_sync_trig_request + */ +lr1121_modem_response_code_t lr1121_modem_alc_sync_start_service( const void* context ); + +/** + * @brief Stop the Applicative Layer Clock synchronization service + * + * @param context Chip implementation context + * + * @return Operation status + * + * @see lr1121_modem_alc_sync_start_service, lr1121_modem_alc_sync_get_time, lr1121_modem_alc_sync_trig_request + */ +lr1121_modem_response_code_t lr1121_modem_alc_sync_stop_service( const void* context ); + +/** + * @brief Get the current time of the chip since GPS epoch + * + * This time is maintained by the chip after successful reception of an ALC sync downlink. + * It is expressed as number of seconds elapsed since January 6th 1980 modulo 2^32. + * + * @param context Chip implementation context + * @param alc_sync_epoch_time The current GPS time + * + * @return Operation status + * + * @see lr1121_modem_alc_sync_start_service, lr1121_modem_alc_sync_stop_service, lr1121_modem_alc_sync_trig_request + */ +lr1121_modem_response_code_t lr1121_modem_alc_sync_get_time( const void* context, uint32_t* alc_sync_epoch_time ); + +/** + * @brief Trig an Applicative Layer Clock synchronization time request + * + * @param context Chip implementation context + * + * @return Operation status + * + * @see lr1121_modem_alc_sync_start_service, lr1121_modem_alc_sync_stop_service, lr1121_modem_alc_sync_get_time + */ +lr1121_modem_response_code_t lr1121_modem_alc_sync_trig_request( const void* context ); + +/** + * @brief Get the FUOTA received file size and CRC + * + * This command must be called after @ref LR1121_MODEM_LORAWAN_EVENT_FUOTA_DONE event. + * + * @param context Chip implementation context + * @param file_size Size of the received file in bytes + * @param file_crc CRC of the received file + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_fuota_get_file_size_crc( const void* context, uint32_t* file_size, + uint32_t* file_crc ); + +/** + * @brief Read a FUOTA file fragment + * + * The maximal FUOTA file size the chip can store is 32768 bytes. And the maximal number of data that can be read in a + * single SPI transaction is 350 bytes. + * Therefore this function may be called several times to fetch a complete FUOTA file received. + * + * The complete size of the FUOTA file to read is obtained by calling @ref lr1121_modem_fuota_get_file_size_crc. + * + * @param [in] context Chip implementation context + * @param [in] base_address The base address to read fragment from. Valid values in [0:32768] + * @param [in] fragment_size The size of the fragment to read. Valid values in [0:350] + * @param [out] fragment A memory buffer to receive the fragment to read. It is up to the caller that it can store at + * least @p fragment_size bytes + * + * @return Operation status + * + * @see lr1121_modem_fuota_get_file_size_crc + */ +lr1121_modem_response_code_t lr1121_modem_fuota_read_file_fragment( const void* context, uint32_t base_address, + uint16_t fragment_size, uint8_t* fragment ); + +/** + * @brief Helper function to check the CRC of a file + * + * This function does not send any command to the chip. + * It is a helper function that check the CRC of a complete received file matches the expected value. + * The complete received file is obtained through successive @ref lr1121_modem_fuota_read_file_fragment calls, and the + * expected CRC is obtained by calling @ref lr1121_modem_fuota_get_file_size_crc. + * + * @param file Pointer to the buffer of the complete FUOTA file. It is up to the caller to ensure it is @p file_size + * byte long + * @param file_size The number of bytes of the complete FUOTA file + * @param expected_crc The expected CRC of the file + * @return true The CRC computed on the file content matches the expected CRC + * @return false The CRC computed on the file content does not match the expected CRC + */ +bool lr1121_modem_fuota_check_crc( const uint8_t* file, uint32_t file_size, uint32_t expected_crc ); + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_LORAWAN_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lorawan_types.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lorawan_types.h new file mode 100755 index 0000000..31f1d28 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lorawan_types.h @@ -0,0 +1,339 @@ +/*! + * @file lr1121_modem_lorawan_types.h + * + * @brief LoRaWAN driver types for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_LORAWAN_TYPES_H +#define LR1121_MODEM_LORAWAN_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/*! + * @brief Length in bytes of a LoRaWAN device eui + */ +#define LR1121_MODEM_DEV_EUI_LENGTH ( 8 ) + +/*! + * @brief Length in bytes of a LoRaWAN join eui + */ +#define LR1121_MODEM_JOIN_EUI_LENGTH ( 8 ) + +/*! + * @brief Length in bytes of a LoRaWAN application key + */ +#define LR1121_MODEM_APP_KEY_LENGTH ( 16 ) + +/*! + * @brief Length in bytes of a LoRaWAN network key + */ +#define LR1121_MODEM_NWK_KEY_LENGTH ( 16 ) + +/*! + * @brief Length of datarate distribution array + */ +#define LR1121_MODEM_DATARATE_DISTRIBUTION_LENGTH ( 16 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief LoRaWAN class type + */ +typedef enum +{ + LR1121_LORAWAN_CLASS_A = 0x00, + LR1121_LORAWAN_CLASS_B = 0x01, + LR1121_LORAWAN_CLASS_C = 0x02, +} lr1121_modem_classes_t; + +/** + * @brief LoRaWAN network type + */ +typedef enum +{ + LR1121_MODEM_LORAWAN_PRIVATE_NETWORK = 0x00, //!< LoRaWAN private network + LR1121_MODEM_LORAWAN_PUBLIC_NETWORK = 0x01 //!< LoRaWAN public network +} lr1121_modem_network_type_t; + +/*! + * @brief LoRaWAN region type + */ +typedef enum +{ + LR1121_LORAWAN_REGION_EU868 = 0x01, + LR1121_LORAWAN_REGION_AS923_GRP1 = 0x02, + LR1121_LORAWAN_REGION_US915 = 0x03, + LR1121_LORAWAN_REGION_AU915 = 0x04, + LR1121_LORAWAN_REGION_CN470 = 0x05, + LR1121_LORAWAN_REGION_WW2G4 = 0x06, + LR1121_LORAWAN_REGION_AS923_GRP2 = 0x07, + LR1121_LORAWAN_REGION_AS923_GRP3 = 0x08, + LR1121_LORAWAN_REGION_IN865 = 0x09, + LR1121_LORAWAN_REGION_KR920 = 0x0A, + LR1121_LORAWAN_REGION_RU864 = 0x0B, + LR1121_LORAWAN_REGION_CN470_RP_1_0 = 0x0C, + LR1121_LORAWAN_REGION_AS923_GRP4 = 0x0D, +} lr1121_modem_regions_t; + +/*! + * @brief Adaptative Data Rate profiles type + */ +typedef enum +{ + LR1121_MODEM_ADR_PROFILE_NETWORK_SERVER_CONTROLLED = 0x00, //!< Network Server Controlled + LR1121_MODEM_ADR_PROFILE_MOBILE_LONG_RANGE = 0x01, //!< Mobile Long Range : 50% MinDr, 25% MinDr + 1, 25% MinDr + 2 + LR1121_MODEM_ADR_PROFILE_MOBILE_LOW_POWER = + 0x02, //!< Mobile Low Power : 25% MaxDr, 25% MaxDr - 1, 25% MaxDr - 2, 25% MaxDr - 3 + LR1121_MODEM_ADR_PROFILE_CUSTOM = + 0x03, //!< Custom List A custom ADR profile consists of a list of 16 preferred data rates. + //!< For every transmission, a random entry in that list is selected. +} lr1121_modem_adr_profiles_t; + +/*! + * @brief LoRaWAN uplink type + */ +typedef enum +{ + LR1121_MODEM_UPLINK_UNCONFIRMED = 0x00, + LR1121_MODEM_UPLINK_CONFIRMED = 0x01, +} lr1121_modem_uplink_type_t; + +/*! + * @brief LoRaWAN Duty Cycle activation type + */ +typedef enum +{ + LR1121_MODEM_CERTIFICATION_MODE_DISABLE = 0x00, + LR1121_MODEM_CERTIFICATION_MODE_ENABLE = 0x01, +} lr1121_modem_certification_mode_t; + +/*! + * @brief Listen Before Talk (LBT) activation type + */ +typedef enum +{ + LR1121_MODEM_LBT_MODE_DISABLE = 0x00, + LR1121_MODEM_LBT_MODE_ENABLE = 0x01, +} lr1121_modem_lbt_mode_t; + +/*! + * @brief Carrier Sense Multiple Access (CSMA) activation type + */ +typedef enum +{ + LR1121_MODEM_CSMA_MODE_DISABLE = 0x00, + LR1121_MODEM_CSMA_MODE_ENABLE = 0x01, +} lr1121_modem_csma_mode_t; + +/** + * @brief Carrier Sense Multiple Access (CSMA) backoff activation type + */ +typedef enum +{ + LR1121_MODEM_CSMA_BACKOFF_DISABLE = 0x00, //!< Disable CMSA backoff + LR1121_MODEM_CSMA_BACKOFF_ENABLE = 0x01, //!< Enable CMSA backoff +} lr1121_modem_csma_backoff_mode_t; + +/*! + * @brief LoRaWAN mac request field + */ +typedef enum +{ + LR1121_MODEM_MAC_REQUEST_LINK_CHECK = 0x01, //!< Enable the MAC Link Check request + LR1121_MODEM_MAC_REQUEST_TIME = 0x02, //!< Enable the MAC Time request + LR1121_MODEM_MAC_REQUEST_PING_SLOT_INFO = 0x04, //!< Enable the MAC Ping Slot Info request +} lr1121_modem_mac_request_t; + +/** + * @brief Bit mask for lr1121_modem_mac_request_t + * + * @see lr1121_modem_mac_request_t + */ +typedef uint8_t lr1121_modem_mac_request_bitmask_t; + +/*! + * @brief RX flags encoding + */ +typedef enum +{ + LR1121_MODEM_DOWNLINK_WINDOW_RX1 = 0x01, //!< received on RX1 unicast + LR1121_MODEM_DOWNLINK_WINDOW_RX2 = 0x02, //!< received on RX2 unicast + LR1121_MODEM_DOWNLINK_WINDOW_RXC = 0x03, //!< received on Class C RX unicast + LR1121_MODEM_DOWNLINK_WINDOW_RXC_MULTICAST_GROUP0 = 0x04, //!< received on Class C Multicast RX for group 0 + LR1121_MODEM_DOWNLINK_WINDOW_RXC_MULTICAST_GROUP1 = 0x05, //!< received on Class C Multicast RX for group 1 + LR1121_MODEM_DOWNLINK_WINDOW_RXC_MULTICAST_GROUP2 = 0x06, //!< received on Class C Multicast RX for group 2 + LR1121_MODEM_DOWNLINK_WINDOW_RXC_MULTICAST_GROUP3 = 0x07, //!< received on Class C Multicast RX for group 3 + LR1121_MODEM_DOWNLINK_WINDOW_RXB = 0x08, //!< received on Class B RX unicast + LR1121_MODEM_DOWNLINK_WINDOW_RXB_MULTICAST_GROUP0 = 0x09, //!< received on Class B Multicast RX for group 0 + LR1121_MODEM_DOWNLINK_WINDOW_RXB_MULTICAST_GROUP1 = 0x0A, //!< received on Class B Multicast RX for group 1 + LR1121_MODEM_DOWNLINK_WINDOW_RXB_MULTICAST_GROUP2 = 0x0B, //!< received on Class B Multicast RX for group 2 + LR1121_MODEM_DOWNLINK_WINDOW_RXB_MULTICAST_GROUP3 = 0x0C, //!< received on Class B Multicast RX for group 3 + LR1121_MODEM_DOWNLINK_WINDOW_RXBEACON = 0x0D, //!< received a Class B beacon + LR1121_MODEM_DOWNLINK_WINDOW_RXRELAY = 0x0E, //!< received on Relay window +} lr1121_modem_downlink_window_t; + +/*! + * @brief class b ping slot status + */ +typedef enum +{ + LR1121_MODEM_CLASS_B_PING_SLOT_1_S = 0x00, //!< 1 second ping-slot periodicity + LR1121_MODEM_CLASS_B_PING_SLOT_2_S = 0x01, //!< 2 seconds ping-slot periodicity + LR1121_MODEM_CLASS_B_PING_SLOT_4_S = 0x02, //!< 4 seconds ping-slot periodicity + LR1121_MODEM_CLASS_B_PING_SLOT_8_S = 0x03, //!< 8 seconds ping-slot periodicity + LR1121_MODEM_CLASS_B_PING_SLOT_16_S = 0x04, //!< 16 seconds ping-slot periodicity + LR1121_MODEM_CLASS_B_PING_SLOT_32_S = 0x05, //!< 32 seconds ping-slot periodicity + LR1121_MODEM_CLASS_B_PING_SLOT_64_S = 0x06, //!< 64 seconds ping-slot periodicity + LR1121_MODEM_CLASS_B_PING_SLOT_128_S = 0x07, //!< 128 seconds ping-slot periodicity +} lr1121_modem_class_b_ping_slot_t; + +/*! + * @brief Select the source of battery level value to use for DevStatusAns MAC command + * + */ +typedef enum +{ + LR1121_MODEM_BATTERY_LEVEL_FROM_INTERNAL_VALUE = + 0x00, //!< Battery level is obtained from LR1121 VBat (see @ref lr1121_modem_system_get_vbat) + LR1121_MODEM_BATTERY_LEVEL_FROM_USER_VALUE = 0x01, //!< Battery level is provided by user application +} lr1121_modem_battery_level_source_value_t; + +/*! + * @brief Join EUI type + */ +typedef uint8_t lr1121_modem_join_eui_t[LR1121_MODEM_JOIN_EUI_LENGTH]; + +/*! + * @brief Device EUI type + */ +typedef uint8_t lr1121_modem_dev_eui_t[LR1121_MODEM_DEV_EUI_LENGTH]; + +/*! + * @brief Application key type + */ +typedef uint8_t lr1121_modem_app_key_t[LR1121_MODEM_APP_KEY_LENGTH]; + +/*! + * @brief Application key type + */ +typedef uint8_t lr1121_modem_nwk_key_t[LR1121_MODEM_NWK_KEY_LENGTH]; + +/*! + * @brief modem downlink metadata structure + * + * The Signal to Noise Ratio (SNR) is returned as an integer part, and a decimal part in 0.25 dB. + * The SNR in dB is therefore obtained by: \f$ SNR_{dB} = snr\_integer + 0.25 \times snr\_quarter \f$ + */ +typedef struct +{ + uint8_t stack_id; //!< The stack identifier that receives the downlink + int16_t rssi; //!< The RSSI of the received downlink in dBm + int8_t snr_integer; //!< Signal to Noise Ratio of the received downlink (integer part, in dB) + uint8_t snr_quarter; //!< Signal to Noise Ratio of the received downlink (0.25dB counts) + lr1121_modem_downlink_window_t window; //!< Rx window of the received downlink + uint8_t fport; //!< LoRaWAN port of the received downlink + uint8_t fpending_bit; //!< Frame pending bit of the received downlink + uint32_t frequency_hz; //!< RF frequency of the received downlink + uint8_t datarate; //!< Datarate of the received downlink +} lr1121_modem_downlink_metadata_t; + +/*! + * @brief multicast class c status structure + */ +typedef struct +{ + uint8_t is_session_started; //!< Indicate if the multicast class C session is started + uint32_t downlink_frequency; //!< Downlink frequency of the multicast class C session + uint8_t downlink_datarate; //!< Datarate of the multicast class C session +} lr1121_modem_multicast_class_c_status_t; + +/*! + * @brief multicast class b status structure + */ +typedef struct +{ + uint8_t is_session_started; //!< Indicate if the multicast class B session is started + uint32_t downlink_frequency; //!< Downlink frequency of the multicast class B session + uint8_t downlink_datarate; //!< Datarate of the multicast class B session + uint8_t is_session_waiting_for_beacon; //!< Indicates whether the multicast class B session have received a beacon + uint8_t ping_slot_periodicity; //!< Ping slot periodicity of the multicast class B session +} lr1121_modem_multicast_class_b_status_t; + +/*! + * @brief LR1121 LoRaWAN version structure + */ +typedef struct +{ + uint8_t lorawan_major; //!< Major number of the LoRaWAN standard implemented + uint8_t lorawan_minor; //!< Minor number of the LoRaWAN standard implemented + uint8_t lorawan_patch; //!< Patch number of the LoRaWAN standard implemented + uint8_t lorawan_revision; //!< Revision number of the LoRaWAN standard implemented + uint8_t rp_major; //!< Major number of the regional parameters implemented + uint8_t rp_minor; //!< Minor number of the regional parameters implemented + uint8_t rp_patch; //!< Patch number of the regional parameters implemented + uint8_t rp_revision; //!< Revision number of the regional parameters implemented +} lr1121_modem_lorawan_version_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_LORAWAN_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lr_fhss.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lr_fhss.h new file mode 100755 index 0000000..0dbdb7a --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lr_fhss.h @@ -0,0 +1,144 @@ +/*! + * @file lr1121_modem_lr_fhss.h + * + * @brief LR_FHSS driver definition for LR1121 + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_LR_FHSS_H +#define LR1121_MODEM_LR_FHSS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr1121_modem_lr_fhss_types.h" +#include "lr1121_modem_common.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/** + * @brief Length, in bytes, of a LR-FHSS sync word + */ +#define LR_FHSS_SYNC_WORD_BYTES ( 4 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Initialize the LR_FHSS + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_lr_fhss_init( const void* context ); + +/** + * @brief Get the delay in microsecond between the last bit sent and the TX done interrupt + * + * @param [in] params lr1121 LR-FHSS parameter structure + * @param [in] payload_length Length of application-layer payload + * + * @returns Delay in microseconds + */ +uint16_t lr1121_modem_lr_fhss_get_bit_delay_in_us( const lr1121_modem_lr_fhss_params_t* params, + uint16_t payload_length ); + +/*! + * @brief Configure a payload to be sent with LR_FHSS + * + * When calling this method, lr1121_modem_radio_set_lr_fhss_sync_word is implicitely called to configure the sync word. + * Note that the syncword must be 4 bytes long. + * + * @param [in] context Chip implementation context + * @param [in] lr_fhss_params Parameter configuration structure of the LRFHSS + * @param [in] hop_sequence_id Seed used to derive the hopping sequence pattern. Only the nine LSBs are taken into + * account + * @param [in] payload The payload to send. It is the responsibility of the caller to ensure that this references an + * array containing at least payload_length elements + * @param [in] payload_length The length of the payload + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_lr_fhss_build_frame( const void* context, + const lr1121_modem_lr_fhss_params_t* lr_fhss_params, + uint16_t hop_sequence_id, const uint8_t* payload, + uint8_t payload_length ); + +/*! + * @brief Get the time on air in ms for LR-FHSS transmission + * + * @param [in] params LR1121 LR-FHSS parameter structure + * @param [in] payload_length Length of application-layer payload + * + * @returns Time-on-air value in ms for LR-FHSS transmission + */ +uint32_t lr1121_modem_lr_fhss_get_time_on_air_in_ms( const lr1121_modem_lr_fhss_params_t* params, + uint16_t payload_length ); + +/** + * @brief Return the number of hop sequences available using the given parameters + * + * @param [in] lr_fhss_params Parameter configuration structure of the LRFHSS + * + * @return Returns the number of valid hop sequences (512 or 384) + */ +unsigned int lr1121_modem_lr_fhss_get_hop_sequence_count( const lr1121_modem_lr_fhss_params_t* lr_fhss_params ); + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_LR_FHSS_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lr_fhss_types.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lr_fhss_types.h new file mode 100755 index 0000000..14c6c95 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_lr_fhss_types.h @@ -0,0 +1,66 @@ +/*! + * @file lr1121_modem_lr_fhss_types.h + * + * @brief LR_FHSS types definition for LR1121 + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_LR_FHSS_TYPES_H +#define LR1121_MODEM_LR_FHSS_TYPES_H + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr_fhss_v1_base_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief LR FHSS parameter structure + */ +typedef struct +{ + lr_fhss_v1_params_t lr_fhss_params; //!< Base LR FHSS parameters + int8_t device_offset; //!< Per device offset to avoid collisions over the air. Possible values: + //!< - if lr_fhss_params.grid == LR_FHSS_V1_GRID_25391_HZ: + //!< [-26, 25] + //!< - if lr_fhss_params.grid == LR_FHSS_V1_GRID_3906_HZ: + //!< [-4, 3] +} lr1121_modem_lr_fhss_params_t; + +#endif // LR1121_MODEM_LR_FHSS_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_modem.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_modem.h new file mode 100755 index 0000000..bd3aa6d --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_modem.h @@ -0,0 +1,393 @@ +/*! + * @file lr1121_modem_modem.h + * + * @brief Modem driver for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_MODEM_H +#define LR1121_MODEM_MODEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include +#include "lr1121_modem_common.h" +#include "lr1121_modem_modem_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Reset non-volatile settings to their default value + * + * Settings that are reset are: + * - region + * - devnonce + * - joinnonce + * - LoRaWAN certification state + * - low frequency clock source + * - reset counter + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_factory_reset( const void* context ); + +/*! + * @brief Return the modem version information + * + * @param [in] context Chip implementation context + * @param [out] modem_version Version of the LR1121 modem + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_modem_version( const void* context, + lr1121_modem_version_t* modem_version ); + +/*! + * @brief Return the modem status + * + * @param [in] context Chip implementation context + * @param [out] status LR1121 mode status bit mask @ref lr1121_modem_lorawan_status_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_status( const void* context, + lr1121_modem_lorawan_status_bitmask_t* status ); + +/*! + * @brief Return and reset the consumption statistics of the modem + * + * @param [in] context Chip implementation context + * @param [out] charge Charge counter in mAh + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_charge( const void* context, lr1121_modem_charge_t* charge ); + +/*! + * @brief Get pending events from the modem + * + * Pending events are indicated by the EVENT line. The EVENT line will be de-asserted after all events have been + * retrieved and no further events are available. + * When no event is available this command returns with empty response payload. + * + * @param [in] context Chip implementation context + * @param [out] event_fields \see lr1121_modem_event_fields_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_event( const void* context, lr1121_modem_event_fields_t* event_fields ); + +/*! + * @brief Suspend or resume the modem’s radio operations + * + * It can be used to prevent extra power consumption by the modem in case the application MCU temporarily needs more + * power itself and wants to prevent exceeding limits. + * + * @param [in] context Chip implementation context + * @param [in] suspend Operations are suspended with parameter value 0x01 and resumed with parameter value 0x00 @ref + * lr1121_modem_suspend_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_suspend( const void* context, const lr1121_modem_suspend_t suspend ); + +/*! + * @brief Get suspend or resume state of the the modem’s radio operations. + * + * @param [in] context Chip implementation context + * @param [out] suspend Operations are suspended with parameter value 0x01 and resumed with parameter value 0x00 @ref + * lr1121_modem_suspend_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_suspend( const void* context, lr1121_modem_suspend_t* suspend ); + +/*! + * @brief Set an application alarm timer in seconds + * + * When the timer has expired the event @ref LR1121_MODEM_LORAWAN_EVENT_ALARM is generated. + * If this command is applied again before the timer has expired, the timer will be started again with the new period. + * Value 0 immediately generates event @ref LR1121_MODEM_LORAWAN_EVENT_ALARM. + * + * @param [in] context Chip implementation context + * @param [in] seconds Seconds + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_set_alarm_timer( const void* context, uint32_t seconds ); + +/*! + * @brief Clear the alarm timer + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_clear_alarm_timer( const void* context ); + +/*! + * @brief Get the application alarm timer remaining time + * + * @param [in] context Chip implementation context + * @param [out] remaining_time Remaining time in seconds + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_alarm_remaining_time( const void* context, uint32_t* remaining_time ); + +/*! + * @brief Get the crashlog status + * + * @param [in] context Chip implementation context + * @param [out] status Crashlog status, see \ref lr1121_modem_crashlog_status_t + * @param [out] crashlog Function name in ASCII. It is up to the caller to ensure this pointer is at least 242 bytes + * long + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_get_crashlog( const void* context, lr1121_modem_crashlog_status_t* status, + uint8_t* crashlog ); + +/*! + * @brief Enable test functions + * + * With the exception of the @ref lr1121_modem_test_mode_start command, test commands are only available if test mode is + * active. Test mode can only be activated if the modem has not yet received a command that results in a radio + * operation. Once test mode is active, all other modem commands are disabled. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_test_mode_start( const void* context ); + +/*! + * @brief No operation. This command may be used to terminate an ongoing continuous TX operation. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_test_nop( const void* context ); + +/*! + * @brief Transmit LoRa packets + * + * @param [in] context Chip implementation context + * @param [in] frequency Frequency in Hz + * @param [in] tx_power Tx power in dBm + * @param [in] payload_length Payload length + * @param [in] sf spreading factor @ref lr1121_modem_tst_mode_lora_sf_t + * @param [in] bw bandwidth @ref lr1121_modem_tst_mode_lora_bw_t + * @param [in] cr Coding Rate @ref lr1121_modem_tst_mode_lora_cr_t + * @param [in] is_iq_inverted Enable IQ inverted + * @param [in] is_crc_enabled Enable CRC field + * @param [in] header_mode Configure header implicit or explicit + * @param [in] preamble_length Number of preamble symbols + * @param [in] number_of_tx Number of packet to send. 0 makes the chip to send packet infinitely until call to @ref + * lr1121_modem_test_nop + * @param [in] delay_ms Delay between two consecutive transmit + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_test_tx_lora( + const void* context, uint32_t frequency, int8_t tx_power, uint8_t payload_length, + lr1121_modem_tst_mode_lora_sf_t sf, lr1121_modem_tst_mode_lora_bw_t bw, lr1121_modem_tst_mode_lora_cr_t cr, + bool is_iq_inverted, bool is_crc_enabled, lr1121_modem_tst_mode_lora_packet_header_mode_t header_mode, + uint32_t preamble_length, uint32_t number_of_tx, uint32_t delay_ms ); + +/*! + * @brief Transmit FSK packets + * + * @param [in] context Chip implementation context + * @param [in] frequency Frequency in Hz + * @param [in] tx_power Tx power in dBm + * @param [in] payload_length Payload length + * @param [in] number_of_tx Number of packet to send. 0 makes the chip to send packet infinitely until call to @ref + * lr1121_modem_test_nop + * @param [in] delay_ms Delay between two consecutive transmit + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_test_tx_fsk( const void* context, uint32_t frequency, int8_t tx_power, + uint8_t payload_length, uint32_t number_of_tx, + uint32_t delay_ms ); + +/*! + * @brief Transmit LR-FHSS packets + * + * @param [in] context Chip implementation context + * @param [in] frequency Frequency in Hz + * @param [in] tx_power Tx power in dBm + * @param [in] payload_length Payload length + * @param [in] grid LR-FHSS grid + * @param [in] bw LR-FHSS bandwidth + * @param [in] cr LR-FHSS coding rate + * @param [in] number_of_tx Number of packet to send. 0 makes the chip to send packet infinitely until call to @ref + * lr1121_modem_test_nop + * @param [in] delay_ms Delay between two consecutive transmit + * @param [in] is_hopping_enabled Enable LR-FHSS frequency hopping + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_test_tx_lr_fhss( const void* context, uint32_t frequency, int8_t tx_power, + uint8_t payload_length, + lr1121_modem_tst_mode_lr_fhss_grid_t grid, + lr1121_modem_tst_mode_lr_fhss_bw_t bw, + lr1121_modem_tst_mode_lr_fhss_cr_t cr, uint32_t number_of_tx, + uint32_t delay_ms, bool is_hopping_enabled ); + +/*! + * @brief Transmit a continuous wave. + * + * @param [in] context Chip implementation context + * @param [in] frequency Frequency in Hz + * @param [in] tx_power Tx power in dBm + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_test_tx_cw( const void* context, uint32_t frequency, int8_t tx_power ); + +/*! + * @brief Continuously receive LoRa packets + * + * @param [in] context Chip implementation context + * @param [in] frequency Frequency in Hz + * @param [in] sf spreading factor @ref lr1121_modem_tst_mode_lora_sf_t + * @param [in] bw bandwidth @ref lr1121_modem_tst_mode_lora_bw_t + * @param [in] cr Coding Rate @ref lr1121_modem_tst_mode_lora_cr_t + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_test_rx_lora_cont( const void* context, uint32_t frequency, + lr1121_modem_tst_mode_lora_sf_t sf, + lr1121_modem_tst_mode_lora_bw_t bw, + lr1121_modem_tst_mode_lora_cr_t cr ); + +/*! + * @brief Continuously receive FSK packets + * + * @param [in] context Chip implementation context + * @param [in] frequency Frequency in Hz + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_test_rx_fsk_cont( const void* context, uint32_t frequency ); + +/*! + * @brief Read the packet counter received during continuously receive packets test. + * + * @param [in] context Chip implementation context + * @param [in] rx_packet_counter The counter of packet received during RX continuous packet test + * + * @returns Operation status + * + * @see lr1121_modem_test_rx_lora_cont + */ +lr1121_modem_response_code_t lr1121_modem_test_read_packet_counter_rx_cont( const void* context, + uint32_t* rx_packet_counter ); + +/*! + * @brief Continuously receive packets on Sub-GHz radio path. + * + * @param [in] context Chip implementation context + * @param [in] frequency Frequency in Hz + * @param [in] time_ms time in millisecond of the radio acquisition + * @param [in] bw_hz bandwidth in Hz + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_test_rssi_subghz( const void* context, uint32_t frequency, uint16_t time_ms, + uint32_t bw_hz ); + +/*! + * @brief Reset the LR1121 radio. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_test_radio_rst( const void* context ); + +/*! + * @brief Exit test mode and reset LR1121 modem. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_test_exit( const void* context ); + +/*! + * @brief Read RSSI after a Sub Gig / 2.4 Ghz test rssi command. + * + * @param [in] context Chip implementation context + * @param [out] rssi RSSI in dBm + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_test_read_rssi( const void* context, int16_t* rssi ); + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_MODEM_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_modem_types.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_modem_types.h new file mode 100755 index 0000000..7f20524 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_modem_types.h @@ -0,0 +1,296 @@ +/*! + * @file lr1121_modem_modem_types.h + * + * @brief Modem driver types for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_MODEM_TYPES_H +#define LR1121_MODEM_MODEM_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include +#include "lr1121_modem_common.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Modem status bits + */ +typedef enum +{ + LR1121_LORAWAN_CRASH = 0x02, + LR1121_LORAWAN_JOINED = 0x08, + LR1121_LORAWAN_SUSPEND = 0x10, + LR1121_LORAWAN_JOINING = 0x40, +} lr1121_modem_lorawan_status_t; + +/** + * @brief Bit mask for lr1121_modem_lorawan_status_t + * + * @see lr1121_modem_lorawan_status_t + */ +typedef uint8_t lr1121_modem_lorawan_status_bitmask_t; + +/*! + * @brief LoRa spreading factor for test mode + */ +typedef enum +{ + LR1121_MODEM_TST_MODE_LORA_SF5 = 0x05, + LR1121_MODEM_TST_MODE_LORA_SF6 = 0x06, + LR1121_MODEM_TST_MODE_LORA_SF7 = 0x07, + LR1121_MODEM_TST_MODE_LORA_SF8 = 0x08, + LR1121_MODEM_TST_MODE_LORA_SF9 = 0x09, + LR1121_MODEM_TST_MODE_LORA_SF10 = 0x0A, + LR1121_MODEM_TST_MODE_LORA_SF11 = 0x0B, + LR1121_MODEM_TST_MODE_LORA_SF12 = 0x0C, +} lr1121_modem_tst_mode_lora_sf_t; + +/*! + * @brief LoRa bandwidth for test mode + */ +typedef enum +{ + LR1121_MODEM_TST_MODE_LORA_10_KHZ = 0x01, + LR1121_MODEM_TST_MODE_LORA_15_KHZ = 0x02, + LR1121_MODEM_TST_MODE_LORA_20_KHZ = 0x03, + LR1121_MODEM_TST_MODE_LORA_31_KHZ = 0x04, + LR1121_MODEM_TST_MODE_LORA_41_KHZ = 0x05, + LR1121_MODEM_TST_MODE_LORA_62_KHZ = 0x06, + LR1121_MODEM_TST_MODE_LORA_125_KHZ = 0x07, + LR1121_MODEM_TST_MODE_LORA_200_KHZ = 0x08, + LR1121_MODEM_TST_MODE_LORA_250_KHZ = 0x09, + LR1121_MODEM_TST_MODE_LORA_400_KHZ = 0x0A, + LR1121_MODEM_TST_MODE_LORA_500_KHZ = 0x0B, + LR1121_MODEM_TST_MODE_LORA_800_KHZ = 0x0C, +} lr1121_modem_tst_mode_lora_bw_t; + +/*! + * @brief LoRa coding rate for test mode + */ +typedef enum +{ + LR1121_MODEM_TST_MODE_LORA_CR_4_5 = 0x01, + LR1121_MODEM_TST_MODE_LORA_CR_4_6 = 0x02, + LR1121_MODEM_TST_MODE_LORA_CR_4_7 = 0x03, + LR1121_MODEM_TST_MODE_LORA_CR_4_8 = 0x04, + LR1121_MODEM_TST_MODE_LORA_CR_4_5_LONG_INTERLEAVING = 0x05, + LR1121_MODEM_TST_MODE_LORA_CR_4_6_LONG_INTERLEAVING = 0x06, + LR1121_MODEM_TST_MODE_LORA_CR_4_8_LONG_INTERLEAVING = 0x07, +} lr1121_modem_tst_mode_lora_cr_t; + +/** + * @brief LR_FHSS grid for test mode + */ +typedef enum +{ + LR1121_MODEM_TST_MODE_LR_FHSS_GRID_V1_25391_HZ = 0x00, + LR1121_MODEM_TST_MODE_LR_FHSS_GRID_V1_3906_HZ = 0x01, +} lr1121_modem_tst_mode_lr_fhss_grid_t; + +/** + * @brief LR-FHSS bandwidth for test mode + */ +typedef enum +{ + LR1121_MODEM_TST_MODE_LR_FHSS_BW_V1_39063_HZ = 0x00, + LR1121_MODEM_TST_MODE_LR_FHSS_BW_V1_85938_HZ = 0x01, + LR1121_MODEM_TST_MODE_LR_FHSS_BW_V1_136719_HZ = 0x02, + LR1121_MODEM_TST_MODE_LR_FHSS_BW_V1_183594_HZ = 0x03, + LR1121_MODEM_TST_MODE_LR_FHSS_BW_V1_335938_HZ = 0x04, + LR1121_MODEM_TST_MODE_LR_FHSS_BW_V1_386719_HZ = 0x05, + LR1121_MODEM_TST_MODE_LR_FHSS_BW_V1_722656_HZ = 0x06, + LR1121_MODEM_TST_MODE_LR_FHSS_BW_V1_773438_HZ = 0x07, + LR1121_MODEM_TST_MODE_LR_FHSS_BW_V1_1523438_HZ = 0x08, + LR1121_MODEM_TST_MODE_LR_FHSS_BW_V1_1574219_HZ = 0x09, +} lr1121_modem_tst_mode_lr_fhss_bw_t; + +/** + * @brief LR-FHSS coding rate for test mode + */ +typedef enum +{ + LR1121_MODEM_TST_MODE_LR_FHSS_CR_V1_2_3 = 0x01, + LR1121_MODEM_TST_MODE_LR_FHSS_CR_V1_1_3 = 0x03, +} lr1121_modem_tst_mode_lr_fhss_cr_t; + +/*! + * @brief Packet header mode + */ +typedef enum +{ + LR1121_MODEM_TST_MODE_LORA_PACKET_HEADER_EXPLICIT = 0x00, + LR1121_MODEM_TST_MODE_LORA_PACKET_HEADER_IMPLICIT = 0x01, +} lr1121_modem_tst_mode_lora_packet_header_mode_t; + +/*! + * @brief Modem suspend type + */ +typedef enum +{ + LR1121_MODEM_RESUMED = 0x00, + LR1121_MODEM_SUSPEND = 0x01, +} lr1121_modem_suspend_t; + +/*! + * @brief Modem crashlog status type + */ +typedef enum +{ + LR1121_NO_NEW_CRASHLOG = 0x00, + LR1121_NEW_CRASHLOG = 0x01, +} lr1121_modem_crashlog_status_t; + +/*! + * @brief LR1121 modem consumption details + */ +typedef struct +{ + uint32_t tx_last_toa_ms; //!< Last Tx time-on-air (ms) + uint32_t rx_last_toa_ms; //!< Last Rx time-on-air (ms) + uint32_t tx_cumulated_toa_ms; //!< Cumulated Tx time-on-air (ms) + uint32_t rx_cumulated_toa_ms; //!< Cumulated Rx time-on-air (ms) + uint32_t none_consumption_ms; //!< Cumulated time not in Tx nor in Rx (ms) + uint32_t tx_consumption_ma; //!< Cumulated Tx power consumption (1/1000*uA*ms) + uint32_t rx_consumption_ma; //!< Cumulated Rx power consumption (1/1000*uA*ms) + uint32_t none_consumption_ma; //!< Cumulated power consumption not in Tx nor in Rx (1/1000*uA*ms) +} lr1121_modem_consumption_details_t; + +/*! + * @brief LR1121 modem consumption per stack action + */ +typedef struct +{ + lr1121_modem_consumption_details_t + suspend; //!< Consumptions when modem is in suspend state. These will always read 0, + //!< even if transceiver commands where called during this state + lr1121_modem_consumption_details_t class_b_beacon; //!< Consumptions of modem related to class B beacon + lr1121_modem_consumption_details_t lr1mac_stack; //!< Consumption of modem corresponding to LoRaWAN stack + lr1121_modem_consumption_details_t lbt; //!< Consumptions of modem related to Listen Before Talk + lr1121_modem_consumption_details_t cad; //!< Consumption of modem related to Channel Activity Detection + lr1121_modem_consumption_details_t class_b_ping_slot; //!< Consumptions of modem related to class B ping slots + lr1121_modem_consumption_details_t test_mode; //!< Consumptions of modem related to test mode + lr1121_modem_consumption_details_t + direct_rp_access; //!< Consumptions when modem is in direct radio planner access state. These will always read + //!< 0, even if transceiver commands where called during this state + lr1121_modem_consumption_details_t relay_tx; //!< Consumption of modem related to relay Tx + lr1121_modem_consumption_details_t class_c; //!< Consumptions of modem related to Class C +} lr1121_modem_charge_t; + +/*! + * @brief LR1121 modem version structure + */ +typedef struct +{ + uint8_t use_case; //!< Will always read 5 for LR1121 Modem-E + uint8_t modem_major; //!< Major number of Modem-E + uint8_t modem_minor; //!< Minor number of Modem-E + uint8_t modem_patch; //!< Patch number of Modem-E + uint8_t lbm_major; //!< Major number of built-in LoRa Basics Modem + uint8_t lbm_minor; //!< Minor number of built-in LoRa Basics Modem + uint8_t lbm_patch; //!< Patch number of built-in LoRa Basics Modem +} lr1121_modem_version_t; + +/*! + * @brief Event type for modem operation + */ +typedef enum +{ + LR1121_MODEM_LORAWAN_EVENT_RESET = 0x00, //!< Modem has reset + LR1121_MODEM_LORAWAN_EVENT_ALARM = 0x01, //!< Alarm time expired + LR1121_MODEM_LORAWAN_EVENT_JOINED = 0x02, //!< Network successfully joined + LR1121_MODEM_LORAWAN_EVENT_JOIN_FAIL = 0x03, //!< Attempt to join network failed + LR1121_MODEM_LORAWAN_EVENT_TX_DONE = 0x04, //!< Frame transmitted + LR1121_MODEM_LORAWAN_EVENT_DOWN_DATA = 0x05, //!< Downlink data received + LR1121_MODEM_LORAWAN_EVENT_LINK_CHECK = 0x06, //!< Modem received a LinkADR request + LR1121_MODEM_LORAWAN_EVENT_LORAWAN_MAC_TIME = 0x07, //!< Modem received a LinkADR request + LR1121_MODEM_LORAWAN_EVENT_CLASS_B_PING_SLOT_INFO = 0x08, //!< Ping Slot Info answered or not by network + LR1121_MODEM_LORAWAN_EVENT_CLASS_B_STATUS = 0x09, //!< Downlink class B is ready or not + LR1121_MODEM_LORAWAN_EVENT_NEW_MULTICAST_SESSION_CLASS_C = 0x0A, //!< New active multicast session in class C + LR1121_MODEM_LORAWAN_EVENT_NEW_MULTICAST_SESSION_CLASS_B = 0x0B, //!< New active multicast session in class B + LR1121_MODEM_LORAWAN_EVENT_NO_MORE_MULTICAST_SESSION_CLASS_C = 0x0C, //!< End of multicast session in class C + LR1121_MODEM_LORAWAN_EVENT_NO_MORE_MULTICAST_SESSION_CLASS_B = 0x0D, //!< End of multicast session in class B + LR1121_MODEM_LORAWAN_EVENT_RELAY_TX_DYNAMIC = + 0x0E, //!< Relay Tx dynamic mode has enabled or disabled the Wake On Radio + LR1121_MODEM_LORAWAN_EVENT_RELAY_TX_MODE = 0x0F, //!< Relay Tx activation has been updated + LR1121_MODEM_LORAWAN_EVENT_RELAY_TX_SYNC = 0x10, //!< Relay Tx synchronization has changed + LR1121_MODEM_LORAWAN_EVENT_ALC_SYNC_TIME = 0x11, //!< Applicative Layer Clock is synchronized + LR1121_MODEM_LORAWAN_EVENT_FUOTA_DONE = 0x12, //!< FUOTA file download terminated + LR1121_MODEM_LORAWAN_EVENT_TEST_MODE = 0x13, //!< Test mode event + LR1121_MODEM_LORAWAN_EVENT_REGIONAL_DUTY_CYCLE = + 0x14, //!< Transmissions are blocked or allowed per regional duty cycle limitation +} lr1121_modem_lorawan_event_type_t; + +/*! + * @brief modem event fields structure + */ +typedef struct +{ + lr1121_modem_lorawan_event_type_t event_type; //!< Event type + uint8_t missed_events_count; //!< Counter of missed events of type @p event_type + uint16_t data; //!< Event data +} lr1121_modem_event_fields_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_MODEM_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_radio.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_radio.h new file mode 100755 index 0000000..ab5c6ce --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_radio.h @@ -0,0 +1,1052 @@ +/*! + * @file lr1121_modem_radio.h + * + * @brief Radio driver definition for LR1121 + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_RADIO_H +#define LR1121_MODEM_RADIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include +#include "lr1121_modem_common.h" +#include "lr1121_modem_radio_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/*! + * @brief Length in byte of the GFSK sync word + */ +#define LR1121_MODEM_RADIO_GFSK_SYNC_WORD_LENGTH 8 + +/*! + * @brief Default GFSK sync word value + */ +#define LR1121_MODEM_RADIO_GFSK_SYNC_WORD_DEFAULT \ + { \ + 0x97, 0x23, 0x52, 0x25, 0x56, 0x53, 0x65, 0x64 \ + } + +/*! + * @brief Length in byte of the LR-FHSS sync word + */ +#define LR1121_MODEM_RADIO_LR_FHSS_SYNC_WORD_LENGTH ( 4 ) + +/*! + * @brief Ramp-up delay for the power amplifier in Sigfox context + * + * This parameter configures the delay to fine tune the ramp-up time of the power amplifier for BPSK operation. + */ +enum +{ + LR1121_MODEM_RADIO_SIGFOX_DBPSK_RAMP_UP_TIME_DEFAULT = 0x0000, //!< No optimization + LR1121_MODEM_RADIO_SIGFOX_DBPSK_RAMP_UP_TIME_100_BPS = 0x1306, //!< Ramp-up optimization for 100bps + LR1121_MODEM_RADIO_SIGFOX_DBPSK_RAMP_UP_TIME_600_BPS = 0x0325, //!< Ramp-up optimization for 600bps +}; + +/*! + * @brief Ramp-down delay for the power amplifier in Sigfox context + * + * This parameter configures the delay to fine tune the ramp-down time of the power amplifier for BPSK operation. + */ +enum +{ + LR1121_MODEM_RADIO_SIGFOX_DBPSK_RAMP_DOWN_TIME_DEFAULT = 0x0000, //!< No optimization + LR1121_MODEM_RADIO_SIGFOX_DBPSK_RAMP_DOWN_TIME_100_BPS = 0x1D70, //!< Ramp-down optimization for 100bps + LR1121_MODEM_RADIO_SIGFOX_DBPSK_RAMP_DOWN_TIME_600_BPS = 0x04E1, //!< Ramp-down optimization for 600bps +}; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Reset internal statistics of the received packets + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr1121_modem_radio_get_gfsk_stats, lr1121_modem_radio_get_lora_stats + */ +lr1121_modem_response_code_t lr1121_modem_radio_reset_stats( const void* context ); + +/*! + * @brief Get the internal statistics of the GFSK received packets + * + * Internal statistics are reset on Power on Reset, by entering sleep mode without memory retention, or by calling @ref + * lr1121_modem_radio_reset_stats. + * + * @param [in] context Chip implementation context + * @param [out] stats The statistics structure of the received packets + * + * @returns Operation status + * + * @see lr1121_modem_radio_reset_stats + */ +lr1121_modem_response_code_t lr1121_modem_radio_get_gfsk_stats( const void* context, + lr1121_modem_radio_stats_gfsk_t* stats ); + +/*! + * @brief Get the internal statistics of the LoRa received packets + * + * Internal statistics are reset on Power on Reset, by entering sleep mode without memory retention, or by calling @ref + * lr1121_modem_radio_reset_stats. + * + * @param [in] context Chip implementation context + * @param [out] stats The statistics structure of the received packets + * + * @returns Operation status + * + * @see lr1121_modem_radio_reset_stats + */ +lr1121_modem_response_code_t lr1121_modem_radio_get_lora_stats( const void* context, + lr1121_modem_radio_stats_lora_t* stats ); + +/*! + * @brief Get the packet type currently configured + * + * @param [in] context Chip implementation context + * @param [out] pkt_type The packet type currently configured + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type + */ +lr1121_modem_response_code_t lr1121_modem_radio_get_pkt_type( const void* context, + lr1121_modem_radio_pkt_type_t* pkt_type ); + +/*! + * @brief Get the length of last received packet, and the offset in the RX internal buffer of the first byte of the + * received payload + * + * @param [in] context Chip implementation context + * @param [out] rx_buffer_status The structure of RX buffer status + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_get_rx_buffer_status( + const void* context, lr1121_modem_radio_rx_buffer_status_t* rx_buffer_status ); + +/*! + * @brief Get the status of last GFSK received packet + * + * The value depends on the received packet type + * + * @param [in] context Chip implementation context + * @param [out] pkt_status The last received packet status + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_get_gfsk_pkt_status( const void* context, + lr1121_modem_radio_pkt_status_gfsk_t* pkt_status ); + +/*! + * @brief Get the status of last LoRa received packet + * + * The value depends on the received packet type + * + * @param [in] context Chip implementation context + * @param [out] pkt_status The last received packet status + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_get_lora_pkt_status( const void* context, + lr1121_modem_radio_pkt_status_lora_t* pkt_status ); + +/*! + * @brief Get the instantaneous RSSI. + * + * This command can be used during reception of a packet + * + * @param [in] context Chip implementation context + * @param [out] rssi_in_dbm Instantaneous RSSI. + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_get_rssi_inst( const void* context, int8_t* rssi_in_dbm ); + +/*! + * @brief Set the GFSK modem sync word + * + * This command is used to set the GFSK nodem sync word. This command expects a 8-byte long array to be passed as sync + * word parameter. By default, the value is 0x9723522556536564. + * + * @param [in] context Chip implementation context + * @param [in] gfsk_sync_word The sync word to be configured + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_gfsk_sync_word( + const void* context, const uint8_t gfsk_sync_word[LR1121_MODEM_RADIO_GFSK_SYNC_WORD_LENGTH] ); + +/*! + * @brief Set the LoRa modem sync word + * + * @param [in] context Chip implementation context + * @param [in] sync_word The sync word to be configured + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_lora_sync_word( const void* context, const uint8_t sync_word ); + +/*! + * @brief Set the syncword for LR-FHSS + * + * Default value: 0x2C0F7995 + * + * @param [in] context Chip implementation context + * @param [in] sync_word The syncword to set. It is up to the caller to ensure this array is at least four bytes long + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_lr_fhss_sync_word( + const void* context, const uint8_t sync_word[LR1121_MODEM_RADIO_LR_FHSS_SYNC_WORD_LENGTH] ); + +/*! + * @brief Set the LoRa modem sync word to private / public + * + * This command is used to select which LoRa network is selected + * + * @param [in] context Chip implementation context + * @param [in] network_type The network type to be configured + * + * @returns Operation status + * + * @warning This function is deprecated. Use lr1121_modem_radio_set_lora_sync_word for chip firmware equal to or more + * recent than 0x303. + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_lora_public_network( + const void* context, const lr1121_modem_radio_lora_network_type_t network_type ); + +/*! + * @brief Start RX operations with a timeout in millisecond + * + * This command sets the LR1121 to RX mode. The radio must have been configured before using this command with @ref + * lr1121_modem_radio_set_pkt_type + * + * By default, the timeout parameter allows to return automatically to standby RC mode if no packets have been received + * after a certain amount of time. This behavior can be altered by @ref lr1121_modem_radio_set_rx_tx_fallback_mode and + * @ref lr1121_modem_radio_auto_tx_rx. + * + * @remark To set the radio in Rx continuous mode, the function @ref lr1121_modem_radio_set_rx_with_timeout_in_rtc_step + * has to be called with \p timeout_in_rtc_step set to 0xFFFFFF + * + * @param [in] context Chip implementation context + * @param [in] timeout_in_ms The timeout configuration for RX operation + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type, lr1121_modem_radio_set_rx_tx_fallback_mode + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_rx( const void* context, const uint32_t timeout_in_ms ); + +/*! + * @brief Start RX operations with a timeout in millisecond and configure the LNA LF0 mode + * + * This command sets the LR1121 to RX mode and configures the LNA LF0 mode. The radio must have been configured before + * using this command with @ref lr1121_modem_radio_set_pkt_type + * + * This command must be issued only for sub-GHz RX operations as it configures the LNA LF0, which is only used for + * sub-GHz Rx operations. + * + * By default, the timeout parameter allows to return automatically to standby RC mode if no packets have been received + * after a certain amount of time. This behavior can be altered by @ref lr1121_modem_radio_set_rx_tx_fallback_mode and + * @ref lr1121_modem_radio_auto_tx_rx. + * + * @remark To set the radio in Rx continuous mode, the function @ref lr1121_modem_radio_set_rx_with_timeout_in_rtc_step + * has to be called with \p timeout_in_rtc_step set to 0xFFFFFF + * + * @param [in] context Chip implementation context + * @param [in] timeout_in_ms The timeout configuration for RX operation + * @param [in] lna_mode Path to use for reception + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type, lr1121_modem_radio_set_rx_tx_fallback_mode, lr1121_modem_radio_set_lna_mode + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_rx_and_lna_mode( const void* context, const uint32_t timeout_in_ms, + lr1121_modem_radio_lna_mode_t lna_mode ); + +/*! + * @brief Start RX operations with a timeout in RTC step + * + * This command sets the LR1121 to RX mode. The radio must have been configured before using this command with @ref + * lr1121_modem_radio_set_pkt_type + * + * By default, the timeout parameter allows to return automatically to standby RC mode if no packets have been received + * after a certain amount of time. This behavior can be altered by @ref lr1121_modem_radio_set_rx_tx_fallback_mode and + * @ref lr1121_modem_radio_auto_tx_rx. + * + * The timeout duration is obtained by: + * \f$ timeout\_duration\_ms = timeout \times \frac{1}{32.768} \f$ + * + * Maximal timeout value is 0xFFFFFF, which gives a maximal timeout of 511 seconds. + * + * The timeout argument can also have the following special values: + * + * + *
    Special values Meaning
    0x000000 RX single: LR1121 stays in RX mode until a + * packet is received, then switch to standby RC mode
    0xFFFFFF + * RX continuous: LR1121 stays in RX mode even after reception of a + * packet + *
    + * + * @param [in] context Chip implementation context + * @param [in] timeout_in_rtc_step The timeout configuration for RX operation + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type, lr1121_modem_radio_set_rx_tx_fallback_mode + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_rx_with_timeout_in_rtc_step( const void* context, + const uint32_t timeout_in_rtc_step ); + +/*! + * @brief Start RX operations with a timeout in RTC step and configure the LNA LF0 mode + * + * This command sets the LR1121 to RX mode and configures the LNA LF0 mode. The radio must have been configured before + * using this command with @ref lr1121_modem_radio_set_pkt_type It internally calls lr1121_modem_radio_set_lna_mode. + * This command must be issued only for sub-GHz RX operations as it configures the LNA LF0, which is only used for + * sub-GHz Rx operations. + * + * By default, the timeout parameter allows to return automatically to standby RC mode if no packets have been received + * after a certain amount of time. This behavior can be altered by @ref lr1121_modem_radio_set_rx_tx_fallback_mode and + * @ref lr1121_modem_radio_auto_tx_rx. + * + * The timeout duration is obtained by: + * \f$ timeout\_duration\_ms = timeout \times \frac{1}{32.768} \f$ + * + * Maximal timeout value is 0xFFFFFF, which gives a maximal timeout of 511 seconds. + * + * The timeout argument can also have the following special values: + * + * + *
    Special values Meaning
    0x000000 RX single: LR1121 stays in RX mode until a + * packet is received, then switch to standby RC mode
    0xFFFFFF + * RX continuous: LR1121 stays in RX mode even after reception of a + * packet + *
    + * + * @param [in] context Chip implementation context + * @param [in] timeout_in_rtc_step The timeout configuration for RX operation + * @param [in] lna_mode Path to use for reception + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type, lr1121_modem_radio_set_rx_tx_fallback_mode + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_rx_with_timeout_in_rtc_step_and_lna_mode( + const void* context, const uint32_t timeout_in_rtc_step, lr1121_modem_radio_lna_mode_t lna_mode ); + +/*! + * @brief Start TX operations + * + * This command sets the LR1121 to TX mode. The radio must have been configured before using this command with @ref + * lr1121_modem_radio_set_pkt_type + * + * By default, the timeout parameter allows to return automatically to standby RC mode if the packet has not been + * completely transmitted after a certain amount of time. This behavior can be altered by @ref + * lr1121_modem_radio_set_rx_tx_fallback_mode and @ref lr1121_modem_radio_auto_tx_rx. + * + * @param [in] context Chip implementation context + * @param [in] timeout_in_ms The timeout configuration for TX operation + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type, lr1121_modem_radio_set_rx_tx_fallback_mode + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_tx( const void* context, const uint32_t timeout_in_ms ); + +/*! + * @brief Start TX operations + * + * This command sets the LR1121 to TX mode. The radio must have been configured before using this command with @ref + * lr1121_modem_radio_set_pkt_type + * + * By default, the timeout parameter allows to return automatically to standby RC mode if the packet has not been + * completely transmitted after a certain amount of time. This behavior can be altered by @ref + * lr1121_modem_radio_set_rx_tx_fallback_mode and @ref lr1121_modem_radio_auto_tx_rx. + * + * The timeout duration is obtained by: + * \f$ timeout\_duration\_ms = timeout \times \frac{1}{32.768} \f$ + * + * Maximal value is 0xFFFFFF. + * + * If the timeout argument is 0, then no timeout is used. + * + * @param [in] context Chip implementation context + * @param [in] timeout_in_rtc_step The timeout configuration for TX operation + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type, lr1121_modem_radio_set_rx_tx_fallback_mode + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_tx_with_timeout_in_rtc_step( const void* context, + const uint32_t timeout_in_rtc_step ); + +/*! + * @brief Set the frequency for future radio operations. + * + * @param [in] context Chip implementation context + * @param [in] freq_in_hz The frequency in Hz to set for radio operations + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_rf_freq( const void* context, const uint32_t freq_in_hz ); + +/*! + * @brief Configure automatic TX after RX or automatic RX after TX + * + * After issuing this command, using the command @ref lr1121_modem_radio_set_tx_with_timeout_in_rtc_step or @ref + * lr1121_modem_radio_set_tx will make the LR1121 doing the following: + * - Enter TX mode as usual + * - Enter configurable Intermediary mode during configurable delay + * - Enter RX mode + * + * Similarly, after a @ref lr1121_modem_radio_set_rx_with_timeout_in_rtc_step_and_lna_mode, @ref + * lr1121_modem_radio_set_rx_with_timeout_in_rtc_step, @ref lr1121_modem_radio_set_rx_and_lna_mode, or @ref + * lr1121_modem_radio_set_rx command, the LR1121 will do the following: + * - Enter RX mode as usual + * - Enter configurable Intermediary mode during configurable delay + * - Enter TX mode + * + * In case delay is 0, the LR1121 does not enter Intermediary mode and directly enter the following mode. + * + * To disable this behavior, use this function with delay set to 0xFFFFFFFF. + * + * @param [in] context Chip implementation context + * @param [in] delay Time to spend in Intermediary mode expressed as steps of \f$\frac{1}{32.768 KHz}\f$ steps. + * @param [in] intermediary_mode The mode the LR1121 enters after first mode completion during delay time + * @param [in] timeout The timeout duration of the automatic RX or TX, expressed as steps of \f$ \frac{1}{32.768KHz} \f$ + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_auto_tx_rx( + const void* context, const uint32_t delay, const lr1121_modem_radio_intermediary_mode_t intermediary_mode, + const uint32_t timeout ); + +/*! + * @brief Set Channel Activity Detection configuration + * + * @param [in] context Chip implementation context + * @param [in] cad_params The structure defining CAD configuration + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_cad_params( const void* context, + const lr1121_modem_radio_cad_params_t* cad_params ); + +/*! + * @brief Set the packet type + * + * @param [in] context Chip implementation context + * @param [in] pkt_type Packet type to set + * + * @returns Operation status + * + * @see lr1121_modem_radio_get_pkt_type + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_pkt_type( const void* context, + const lr1121_modem_radio_pkt_type_t pkt_type ); + +/*! + * @brief Set the modulation parameters for GFSK packets + * + * The command @ref lr1121_modem_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] mod_params The structure of modulation configuration + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_gfsk_mod_params( + const void* context, const lr1121_modem_radio_mod_params_gfsk_t* mod_params ); + +/*! + * @brief Set the modulation parameters for BPSK packets + * + * The command @ref lr1121_modem_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] mod_params The structure of modulation configuration + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_bpsk_mod_params( + const void* context, const lr1121_modem_radio_mod_params_bpsk_t* mod_params ); + +/*! + * @brief Set the modulation parameters for LoRa packets + * + * The command @ref lr1121_modem_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] mod_params The structure of modulation configuration + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_lora_mod_params( + const void* context, const lr1121_modem_radio_mod_params_lora_t* mod_params ); + +/*! + * @brief Set the modulation parameters for LR-FHSS + * + * The command @ref lr1121_modem_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] mod_params The structure of modulation configuration + * + * @returns Operation status + * + * @see lr1121_modem_lr_fhss_set_pkt_type + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_lr_fhss_mod_params( + const void* context, const lr1121_modem_radio_mod_params_lr_fhss_t* mod_params ); + +/*! + * @brief Set the packet parameters for GFSK packets + * + * The command @ref lr1121_modem_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] pkt_params The structure of packet configuration + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type, lr1121_modem_radio_set_gfsk_mod_params + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_gfsk_pkt_params( + const void* context, const lr1121_modem_radio_pkt_params_gfsk_t* pkt_params ); + +/*! + * @brief Set the packet parameters for BPSK packets + * + * The command @ref lr1121_modem_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] pkt_params The structure of packet configuration + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type, lr1121_modem_radio_set_bpsk_mod_params + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_bpsk_pkt_params( + const void* context, const lr1121_modem_radio_pkt_params_bpsk_t* pkt_params ); + +/*! + * @brief Set the packet parameters for LoRa packets + * + * The command @ref lr1121_modem_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] pkt_params The structure of packet configuration + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type, lr1121_modem_radio_set_lora_mod_params + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_lora_pkt_params( + const void* context, const lr1121_modem_radio_pkt_params_lora_t* pkt_params ); + +/*! + * @brief Set the parameters for TX power and power amplifier ramp time + * + * The command @ref lr1121_modem_radio_set_pa_cfg must be called prior calling + * lr1121_modem_radio_set_tx_params. + * + * The range of possible TX output power values depends on PA selected with @ref lr1121_modem_radio_set_pa_cfg : + * - for LPA: power value goes from -17dBm to +14dBm (ie. from 0xEF to 0x0E) + * - for HPA: power value goes from -9dBm to +22dBm (ie. from 0xF7 to 0x16) + * + * Moreover, to use TX output power value higher than +10dBm, the @ref LR1121_MODEM_RADIO_PA_REG_SUPPLY_VBAT supply must + * have been selected with @ref lr1121_modem_radio_set_pa_cfg. + * + * @param [in] context Chip implementation context + * @param [in] pwr_in_dbm The TX output power in dBm + * @param [in] ramp_time The ramping time configuration for the PA + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_tx_params( const void* context, const int8_t pwr_in_dbm, + const lr1121_modem_radio_ramp_time_t ramp_time ); + +/*! + * @brief Sets the Node and Broadcast address used for GFSK + * + * This setting is used only when filtering is enabled. + * + * @param [in] context Chip implementation context + * @param [in] node_address The node address used as filter + * @param [in] broadcast_address The broadcast address used as filter + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_pkt_address( const void* context, const uint8_t node_address, + const uint8_t broadcast_address ); + +/*! + * @brief Alter the chip mode after successfull transmission or reception operation + * + * This setting is not used during Rx Duty Cycle mode or Auto Tx Rx. + * + * @param [in] context Chip implementation context + * @param [in] fallback_mode The chip mode to enter after successfull transmission or reception. + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_rx_tx_fallback_mode( + const void* context, const lr1121_modem_radio_fallback_modes_t fallback_mode ); + +/*! + * @brief Configure and start a Rx Duty Cycle operation + * + * It executes the following steps: + * 1. Reception: enters reception state for duration defined by rx_period + * - If mode is LR1121_MODEM_RADIO_RX_DUTY_CYCLE_MODE_RX: it is standard RX mode + * - If mode is LR1121_MODEM_RADIO_RX_DUTY_CYCLE_MODE_CAD (only in LoRa) : it is CAD operation + * 2. Depending on the over-the-air activity detection: + * - In case of positive over-the-air detection, the rx_period timeout is recomputed to the value + * \f$2 \times rx\_period + sleep\_period\f$ + * - If no air activity is detected, the LR1121 goes back to sleep mode with retention for a duration defined by + * sleep_period + * 3. On wake-up, the LR1121 restarts the process with the reception state. + * + * @remark If mode is configured to @ref LR1121_MODEM_RADIO_RX_DUTY_CYCLE_MODE_CAD, then the CAD configuration used in + * step 1. is the one set from the last call to @ref lr1121_modem_radio_set_cad_params. + * + * @param [in] context Chip implementation context + * @param [in] rx_period_in_ms The length of Rx period + * @param [in] sleep_period_in_ms The length of sleep period + * @param [in] mode The operation mode during Rx phase + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_cad_params + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_rx_duty_cycle( const void* context, const uint32_t rx_period_in_ms, + const uint32_t sleep_period_in_ms, + const lr1121_modem_radio_rx_duty_cycle_mode_t mode ); + +/*! + * @brief Configure and start a Rx Duty Cycle operation + * + * It executes the following steps: + * 1. Reception: enters reception state for duration defined by rx_period + * - If mode is LR1121_MODEM_RADIO_RX_DUTY_CYCLE_MODE_RX: it is standard RX mode + * - If mode is LR1121_MODEM_RADIO_RX_DUTY_CYCLE_MODE_CAD (only in LoRa) : it is CAD operation + * 2. Depending on the over-the-air activity detection: + * - In case of positive over-the-air detection, the rx_period timeout is recomputed to the value + * \f$2 \times rx\_period + sleep\_period\f$ + * - If no air activity is detected, the LR1121 goes back to sleep mode with retention for a duration defined by + * sleep_period + * 3. On wake-up, the LR1121 restarts the process with the reception state. + * + * @remark If mode is configured to @ref LR1121_MODEM_RADIO_RX_DUTY_CYCLE_MODE_CAD, then the CAD configuration used in + * step 1. is the one set from the last call to @ref lr1121_modem_radio_set_cad_params. + * + * @param [in] context Chip implementation context + * @param [in] rx_period_in_rtc_step The length of Rx period + * @param [in] sleep_period_in_rtc_step The length of sleep period + * @param [in] mode The operation mode during Rx phase + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_cad_params + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_rx_duty_cycle_with_timings_in_rtc_step( + const void* context, const uint32_t rx_period_in_rtc_step, const uint32_t sleep_period_in_rtc_step, + const lr1121_modem_radio_rx_duty_cycle_mode_t mode ); + +/*! + * @brief Set the Power Amplifier configuration + * + * It must be called prior using @ref lr1121_modem_radio_set_tx_params. + * + * @param [in] context Chip implementation context + * @param [in] pa_cfg The structure for PA configuration + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_pa_cfg( const void* context, + const lr1121_modem_radio_pa_cfg_t* pa_cfg ); + +/*! + * @brief Define on which event the Rx timeout shall be stopped + * + * The two options are: + * - Syncword / Header detection + * - Preamble detection + * + * @param [in] context Chip implementation context + * @param [in] stop_timeout_on_preamble The choice of the event to be taken into account + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_stop_timeout_on_preamble( const void* context, + const bool stop_timeout_on_preamble ); + +/*! + * @brief Start the CAD mode + * + * The LoRa packet type shall be selected before this function is called. The fallback mode is configured with + * lr1121_modem_radio_set_cad_params. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_cad_params, lr1121_modem_radio_set_pkt_type + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_cad( const void* context ); + +/*! + * @brief Set the device into Tx continuous wave (RF tone). + * + * A packet type shall be selected before this function is called. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_pkt_type + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_tx_cw( const void* context ); + +/*! + * @brief Set the device into Tx continuous preamble (modulated signal). + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_tx_infinite_preamble( const void* context ); + +/*! + * @brief Configure the LoRa modem to issue a RX timeout after an exact number of symbols given in parameter if no LoRa + * modulation is detected + * + * @warning Values of nb_symbol higher than 255 are only valid for chip firmware equal to or more recent than 0x308. + * + * @param [in] context Chip implementation context + * @param [in] nb_symbol number of symbols to compute the timeout + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_lora_sync_timeout( const void* context, const uint16_t nb_symbol ); + +/*! + * @brief Configure the LoRa modem to issue a RX timeout after an exact number of symbols given in parameter if no LoRa + * modulation is detected + * + * @warning This command has been introduced in chip firware 0x0308 and is not available in earlier version + * + * @remark The number of symbol is computed as mantissa ^ (2*exponent + 1) + * + * @param [in] context Chip implementation context + * @param [in] mantissa Mantissa - from 0 to 31 - to compute the number of symbols of the timeout + * @param [in] exponent Exponent - from 0 to 7 - to compute the number of symbols of the timeout + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_lora_sync_timeout_with_mantissa_exponent( const void* context, + const uint8_t mantissa, + const uint8_t exponent ); + +/*! + * @brief Configure the seed and the polynomial used to compute CRC in GFSK packet + * + * @param [in] context Chip implementation context + * @param [in] seed Seed used to compute the CRC value + * @param [in] polynomial Polynomial used to compute the CRC value + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_gfsk_crc_params( const void* context, const uint32_t seed, + const uint32_t polynomial ); + +/*! + * @brief Configure the whitening seed used in GFSK packet + * + * @param [in] context Chip implementation context + * @param [in] seed Whitening seed value + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_gfsk_whitening_seed( const void* context, const uint16_t seed ); + +/*! + * @brief Configure the boost mode in reception + * + * @param [in] context Chip implementation context + * @param [in] enable_boost_mode Boost mode activation + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_cfg_rx_boosted( const void* context, const bool enable_boost_mode ); + +/*! + * @brief Set RSSI calibration table + * + * @param [in] context Chip implementation context + * @param [in] rssi_cal_table RSSI calibration table + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_rssi_calibration( + const void* context, const lr1121_modem_radio_rssi_calibration_table_t* rssi_cal_table ); + +/*! + * @brief Gets the radio bw parameter for a given bandwidth in Hz + * + * @param [in] bw_in_hz Requested GFSK Rx bandwidth + * @param [out] bw_parameter Radio parameter immediately above requested bw_in_hz + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_get_gfsk_rx_bandwidth( uint32_t bw_in_hz, + lr1121_modem_radio_gfsk_bw_t* bw_parameter ); + +/** + * @brief Compute the numerator for LoRa time-on-air computation. + * + * @remark To get the actual time-on-air in seconds, this value has to be divided by the LoRa bandwidth in Hertz. + * + * @param [in] pkt_p Pointer to the structure holding the LoRa packet parameters + * @param [in] mod_p Pointer to the structure holding the LoRa modulation parameters + * + * @returns LoRa time-on-air numerator + */ +uint32_t lr1121_modem_radio_get_lora_time_on_air_numerator( const lr1121_modem_radio_pkt_params_lora_t* pkt_p, + const lr1121_modem_radio_mod_params_lora_t* mod_p ); + +/** + * @brief Get the actual value in Hertz of a given LoRa bandwidth + * + * @param [in] bw LoRa bandwidth parameter + * + * @returns Actual LoRa bandwidth in Hertz + */ +uint32_t lr1121_modem_radio_get_lora_bw_in_hz( lr1121_modem_radio_lora_bw_t bw ); + +/*! + * @brief Get the time on air in ms for LoRa transmission + * + * @param [in] pkt_p Pointer to a structure holding the LoRa packet parameters + * @param [in] mod_p Pointer to a structure holding the LoRa modulation parameters + * + * @returns Time-on-air value in ms for LoRa transmission + */ +uint32_t lr1121_modem_radio_get_lora_time_on_air_in_ms( const lr1121_modem_radio_pkt_params_lora_t* pkt_p, + const lr1121_modem_radio_mod_params_lora_t* mod_p ); + +/** + * @brief Compute the numerator for GFSK time-on-air computation. + * + * @remark To get the actual time-on-air in seconds, this value has to be divided by the GFSK bitrate in bits per + * second. + * + * @param [in] pkt_p Pointer to the structure holding the GFSK packet parameters + * + * @returns GFSK time-on-air numerator + */ +uint32_t lr1121_modem_radio_get_gfsk_time_on_air_numerator( const lr1121_modem_radio_pkt_params_gfsk_t* pkt_p ); + +/** + * @brief Get the time on air in ms for GFSK transmission + * + * @param [in] pkt_p Pointer to a structure holding the GFSK packet parameters + * @param [in] mod_p Pointer to a structure holding the GFSK modulation parameters + * + * @returns Time-on-air value in ms for GFSK transmission + */ +uint32_t lr1121_modem_radio_get_gfsk_time_on_air_in_ms( const lr1121_modem_radio_pkt_params_gfsk_t* pkt_p, + const lr1121_modem_radio_mod_params_gfsk_t* mod_p ); + +/** + * @brief Get the number of RTC steps for a given time in millisecond + * + * @param [in] time_in_ms Timeout in millisecond + * + * @returns Number of RTC steps + */ +uint32_t lr1121_modem_radio_convert_time_in_ms_to_rtc_step( uint32_t time_in_ms ); + +/*! + * @brief Configure the radio for Bluetooth® Low Energy Beaconing Compatibility. + * + * The caller shall ensure that the payload, if provided, follows the format of the Advertising physical channel PDU as + * defined in the Bluetooth® core specification. + * + * This automatically configures the syncword to 0x8e89bed6 and the 3-byte CRC (polynomial set to 0x100065b, seed set to + * 0x555555). + * + * @param [in] context Chip implementation context + * @param [in] channel_id BLE channel - allowed channels are 37, 38, 39 + * @param [in] buffer Array of bytes to be used as beacon payload (optional) + * @param [in] length Number of bytes in the @p buffer array + * + * @returns Operation status + * + * @note As opposed to the function @ref lr1121_modem_radio_cfg_and_send_bluetooth_low_energy_beaconning_compatibility, + * this function only configures the radio interface for Bluetooth® Low Energy Beaconing Compatibility advertising. To + * actually start the transmission, the function @ref lr1121_modem_radio_set_tx must be called. + * @note The previously configured payload with @ref + * lr1121_modem_radio_cfg_bluetooth_low_energy_beaconning_compatibility or + * @ref lr1121_modem_radio_cfg_and_send_bluetooth_low_energy_beaconning_compatibility is sent if @p length is set to 0, + * except if a call to @ref lr1121_modem_regmem_write_buffer8, @ref lr1121_modem_lr_fhss_build_frame is done in between + * or @ref lr1121_modem_system_set_sleep with warm start disabled + * + * @sa lr1121_modem_radio_cfg_and_send_bluetooth_low_energy_beaconning_compatibility + * @sa lr1121_modem_radio_set_tx + */ +lr1121_modem_response_code_t lr1121_modem_radio_cfg_bluetooth_low_energy_beaconning_compatibility( + const void* context, const uint8_t channel_id, const uint8_t* buffer, const uint8_t length ); + +/** + * @brief Get the information from the last received LoRa packet header (if @ref LR1121_MODEM_RADIO_LORA_PKT_EXPLICIT) + * or the locally configured settings (if @ref LR1121_MODEM_RADIO_LORA_PKT_IMPLICIT) + * + * @remark This function can be called only if @ref LR1121_MODEM_RADIO_PKT_TYPE_LORA is selected with @ref + * lr1121_modem_radio_set_pkt_type + * + * @param [in] context Chip implementation context + * @param [out] is_crc_present CRC configuration + * @param [out] cr LoRa coding rate + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_radio_get_lora_rx_info( const void* context, bool* is_crc_present, + lr1121_modem_radio_lora_cr_t* cr ); + +/*! + * @brief Configure the radio for Bluetooth® Low Energy Beaconing Compatibility and send the given beacon on the desired + * channel. + * + * The caller shall ensure that the payload, if provided, follows the format of the Advertising physical channel PDU as + * defined in the Bluetooth® core specification. + * + * This automatically configures the syncword to 0x8e89bed6 and the 3-byte CRC (polynomial set to 0x100065b, seed set to + * 0x555555). + * + * @param [in] context Chip implementation context + * @param [in] channel_id BLE channel - allowed channels are 37, 38, 39 + * @param [in] buffer Array of bytes to be used as beacon payload (optional) + * @param [in] length Number of bytes in the @p buffer array + * + * @returns Operation status + * + * @note This function combines the configuration for Bluetooth® Low Energy Beaconing Compatibility - done with @ref + * lr1121_modem_radio_cfg_bluetooth_low_energy_beaconning_compatibility) - and the actual transmission - done with @ref + * lr1121_modem_radio_set_tx. + * @note The previously configured payload with @ref + * lr1121_modem_radio_cfg_bluetooth_low_energy_beaconning_compatibility or + * @ref lr1121_modem_radio_cfg_and_send_bluetooth_low_energy_beaconning_compatibility is sent if @p length is set to 0, + * except if a call to @ref lr1121_modem_regmem_write_buffer8, @ref lr1121_modem_lr_fhss_build_frame is done in between + * or @ref lr1121_modem_system_set_sleep with warm start disabled + * + * @sa lr1121_modem_radio_cfg_bluetooth_low_energy_beaconning_compatibility + */ +lr1121_modem_response_code_t lr1121_modem_radio_cfg_and_send_bluetooth_low_energy_beaconning_compatibility( + const void* context, const uint8_t channel_id, const uint8_t* buffer, const uint8_t length ); + +/** + * @brief Get the mantissa and exponent for a given number of symbol + * + * @remark This function computes the [mantissa, exponent] duple which corresponds to nb_of_symb: the smallest + * value verifying both following conditions: + * - nb_of_symb >= nb_symbol; and + * - nb_of_symb = mant * 2 ^ (2 * exp + 1) + * + * @param [in] nb_symbol Number of symbols + * @param [out] mant Mantissa computed from nb_symb + * @param [out] exp Exponent computed from nb_symb + * + * @returns Number of symbols corresponding to the [mantissa, exponent] duple computed with the following formula: + * nb_of_symb = mant * 2 ^ (2 * exp + 1) + */ +uint16_t lr1121_modem_radio_convert_nb_symb_to_mant_exp( const uint16_t nb_symbol, uint8_t* mant, uint8_t* exp ); + +/** + * @brief Configure LNA LF0 mode + * + * @remark This function shall be called after each call to lr1121_modem_radio_set_rx or + * lr1121_modem_radio_set_rx_with_timeout_in_rtc_step to be taken into account. Helper functions + * lr1121_modem_radio_set_rx_on_lna_path or lr1121_modem_radio_set_rx_with_timeout_in_rtc_step_on_lna_path can be used + * instead. + * + * @param [in] context Chip implementation context + * @param [in] lna_mode Value to put in the register, enum type + * + * @returns Operation status + * + * @see lr1121_modem_radio_set_rx_on_lna_path, lr1121_modem_radio_set_rx_with_timeout_in_rtc_step_on_lna_path + */ +lr1121_modem_response_code_t lr1121_modem_radio_set_lna_mode( const void* context, + lr1121_modem_radio_lna_mode_t lna_mode ); + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_RADIO_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_radio_timings.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_radio_timings.h new file mode 100755 index 0000000..21fb698 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_radio_timings.h @@ -0,0 +1,97 @@ +/** + * @file lr1121_modem_radio_timings.h + * + * @brief LR1121 timing helper functions definition + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_RADIO_TIMINGS_H +#define LR1121_MODEM_RADIO_TIMINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr1121_modem_radio_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/** + * @brief Get the time between the last bit sent (on Tx side) and the Rx done event (on Rx side) + * + * @param [in] mod_params Pointer to a structure holding the LoRa modulation parameters used for the computation + * + * @returns Delay in microsecond + */ +uint32_t lr1121_modem_radio_timings_get_delay_between_last_bit_sent_and_rx_done_in_us( + const lr1121_modem_radio_mod_params_lora_t* mod_params ); + +/** + * @brief Get the time between the last bit sent and the Tx done event + * + * @param [in] ramp_time Power amplifier ramp time + * + * @returns Delay in microsecond + */ +uint32_t lr1121_modem_radio_timings_get_delay_between_last_bit_sent_and_tx_done_in_us( + const lr1121_modem_radio_ramp_time_t ramp_time ); + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_RADIO_TIMINGS_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_radio_types.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_radio_types.h new file mode 100755 index 0000000..b353b62 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_radio_types.h @@ -0,0 +1,653 @@ +/*! + * @file lr1121_modem_radio_types.h + * + * @brief Radio driver types for LR1121 + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_RADIO_TYPES_H +#define LR1121_MODEM_RADIO_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/*! + * @brief Bit mask to set to indicate a LR-FHSS bitrate is defined in steps of 1/256 bit per seconds + */ +#define LR1121_MODEM_RADIO_LR_FHSS_BITRATE_DIVIDE_BY_256 ( 0x80000000 ) + +/*! + * @brief LR-FHSS bitrate value at 488.28125 bps defined as steps of 1/256 bitrate per seconds + */ +#define LR1121_MODEM_RADIO_LR_FHSS_BITRATE_IN_256_BPS_STEPS ( 125000 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Power Amplifier Selection values + * + * - Low-power Power Amplifier can reach up to 14dBm + * - High-power Power Amplifier can reach up to 22 dBm + */ +typedef enum +{ + LR1121_MODEM_RADIO_PA_SEL_LP = 0x00, //!< Low-power Power Amplifier + LR1121_MODEM_RADIO_PA_SEL_HP = 0x01, //!< High-power Power Amplifier + LR1121_MODEM_RADIO_PA_SEL_HF = 0x02, //!< High-frequency Power Amplifier +} lr1121_modem_radio_pa_selection_t; + +/*! + * @brief GFSK Address Filtering configurations + * + * If Address Filtering is enabled but a wrong address is received, therefore the reception is aborted and the address + * error flag of packet status is set. + */ +typedef enum +{ + LR1121_MODEM_RADIO_GFSK_ADDRESS_FILTERING_DISABLE = 0x00, //!< Filter deactivated + LR1121_MODEM_RADIO_GFSK_ADDRESS_FILTERING_NODE_ADDRESS = 0x01, //!< Filter on Node Address + LR1121_MODEM_RADIO_GFSK_ADDRESS_FILTERING_NODE_AND_BROADCAST_ADDRESSES = + 0x02, //!< Filtering on Node and Broadcast addresses +} lr1121_modem_radio_gfsk_address_filtering_t; + +/*! + * @brief Chip mode after successfull transmission or reception + * + * Unused for RX duty cycle and AutoTxRx operations + */ +typedef enum +{ + LR1121_MODEM_RADIO_FALLBACK_STDBY_RC = 0x01, //!< Standby RC (Default) + LR1121_MODEM_RADIO_FALLBACK_STDBY_XOSC = 0x02, //!< Standby XOSC + LR1121_MODEM_RADIO_FALLBACK_FS = 0x03 //!< FS +} lr1121_modem_radio_fallback_modes_t; + +/*! + * @brief Ramping time for PA + * + * This parameter is the ramping time of the PA. A high value improves spectral quality. + */ +typedef enum +{ + LR1121_MODEM_RADIO_RAMP_16_US = 0x00, //!< 16 us Ramp Time + LR1121_MODEM_RADIO_RAMP_32_US = 0x01, //!< 32 us Ramp Time + LR1121_MODEM_RADIO_RAMP_48_US = 0x02, //!< 48 us Ramp Time (Default) + LR1121_MODEM_RADIO_RAMP_64_US = 0x03, //!< 64 us Ramp Time + LR1121_MODEM_RADIO_RAMP_80_US = 0x04, //!< 80 us Ramp Time + LR1121_MODEM_RADIO_RAMP_96_US = 0x05, //!< 96 us Ramp Time + LR1121_MODEM_RADIO_RAMP_112_US = 0x06, //!< 112 us Ramp Time + LR1121_MODEM_RADIO_RAMP_128_US = 0x07, //!< 128 us Ramp Time + LR1121_MODEM_RADIO_RAMP_144_US = 0x08, //!< 144 us Ramp Time + LR1121_MODEM_RADIO_RAMP_160_US = 0x09, //!< 160 us Ramp Time + LR1121_MODEM_RADIO_RAMP_176_US = 0x0A, //!< 176 us Ramp Time + LR1121_MODEM_RADIO_RAMP_192_US = 0x0B, //!< 192 us Ramp Time + LR1121_MODEM_RADIO_RAMP_208_US = 0x0C, //!< 208 us Ramp Time + LR1121_MODEM_RADIO_RAMP_240_US = 0x0D, //!< 240 us Ramp Time + LR1121_MODEM_RADIO_RAMP_272_US = 0x0E, //!< 272 us Ramp Time + LR1121_MODEM_RADIO_RAMP_304_US = 0x0F, //!< 304 us Ramp Time +} lr1121_modem_radio_ramp_time_t; + +/*! + * @brief LoRa network type configuration + */ +typedef enum +{ + LR1121_MODEM_RADIO_LORA_NETWORK_PRIVATE = 0x00, //!< LoRa private network + LR1121_MODEM_RADIO_LORA_NETWORK_PUBLIC = 0x01, //!< LoRa public network +} lr1121_modem_radio_lora_network_type_t; + +/*! + * @brief LoRa Spreading Factor configurations + */ +typedef enum +{ + LR1121_MODEM_RADIO_LORA_SF5 = 0x05, //!< Spreading Factor 5 + LR1121_MODEM_RADIO_LORA_SF6 = 0x06, //!< Spreading Factor 6 + LR1121_MODEM_RADIO_LORA_SF7 = 0x07, //!< Spreading Factor 7 + LR1121_MODEM_RADIO_LORA_SF8 = 0x08, //!< Spreading Factor 8 + LR1121_MODEM_RADIO_LORA_SF9 = 0x09, //!< Spreading Factor 9 + LR1121_MODEM_RADIO_LORA_SF10 = 0x0A, //!< Spreading Factor 10 + LR1121_MODEM_RADIO_LORA_SF11 = 0x0B, //!< Spreading Factor 11 + LR1121_MODEM_RADIO_LORA_SF12 = 0x0C, //!< Spreading Factor 12 +} lr1121_modem_radio_lora_sf_t; + +/*! + * @brief LoRa Bandwidth configurations + */ +typedef enum +{ + LR1121_MODEM_RADIO_LORA_BW_10 = 0x08, //!< Bandwidth 10.42 kHz + LR1121_MODEM_RADIO_LORA_BW_15 = 0x01, //!< Bandwidth 15.63 kHz + LR1121_MODEM_RADIO_LORA_BW_20 = 0x09, //!< Bandwidth 20.83 kHz + LR1121_MODEM_RADIO_LORA_BW_31 = 0x02, //!< Bandwidth 31.25 kHz + LR1121_MODEM_RADIO_LORA_BW_41 = 0x0A, //!< Bandwidth 41.67 kHz + LR1121_MODEM_RADIO_LORA_BW_62 = 0x03, //!< Bandwidth 62.50 kHz + LR1121_MODEM_RADIO_LORA_BW_125 = 0x04, //!< Bandwidth 125.00 kHz + LR1121_MODEM_RADIO_LORA_BW_250 = 0x05, //!< Bandwidth 250.00 kHz + LR1121_MODEM_RADIO_LORA_BW_500 = 0x06, //!< Bandwidth 500.00 kHz + LR1121_MODEM_RADIO_LORA_BW_200 = 0x0D, //!< Bandwidth 203.00 kHz, 2G4 and compatible with LR112x chips only + LR1121_MODEM_RADIO_LORA_BW_400 = 0x0E, //!< Bandwidth 406.00 kHz, 2G4 and compatible with LR112x chips only + LR1121_MODEM_RADIO_LORA_BW_800 = 0x0F, //!< Bandwidth 812.00 kHz, 2G4 and compatible with LR112x chips only +} lr1121_modem_radio_lora_bw_t; + +/*! + * @brief LoRa Coding Rate configurations + */ +typedef enum +{ + LR1121_MODEM_RADIO_LORA_NO_CR = 0x00, //!< No Coding Rate + LR1121_MODEM_RADIO_LORA_CR_4_5 = 0x01, //!< Coding Rate 4/5 Short Interleaver + LR1121_MODEM_RADIO_LORA_CR_4_6 = 0x02, //!< Coding Rate 4/6 Short Interleaver + LR1121_MODEM_RADIO_LORA_CR_4_7 = 0x03, //!< Coding Rate 4/7 Short Interleaver + LR1121_MODEM_RADIO_LORA_CR_4_8 = 0x04, //!< Coding Rate 4/8 Short Interleaver + LR1121_MODEM_RADIO_LORA_CR_LI_4_5 = 0x05, //!< Coding Rate 4/5 Long Interleaver + LR1121_MODEM_RADIO_LORA_CR_LI_4_6 = 0x06, //!< Coding Rate 4/6 Long Interleaver + LR1121_MODEM_RADIO_LORA_CR_LI_4_8 = 0x07, //!< Coding Rate 4/8 Long Interleaver +} lr1121_modem_radio_lora_cr_t; + +/*! + * @brief Values for intermediary mode + */ +typedef enum +{ + LR1121_MODEM_RADIO_MODE_SLEEP = 0x00, //!< Sleep + LR1121_MODEM_RADIO_MODE_STANDBY_RC = 0x01, //!< Standby RC + LR1121_MODEM_RADIO_MODE_STANDBY_XOSC = 0x02, //!< Standby XOSC + LR1121_MODEM_RADIO_MODE_FS = 0x03 //!< Frequency Synthesis +} lr1121_modem_radio_intermediary_mode_t; + +/*! + * @brief GFSK Cyclic Redundancy Check configurations + * + * If this value is set to something other than CRC_OFF, a CRC is automatically computed and added after the end of the + * payload on transmitter side. On receiver side, the CRC check is automatically processed. + */ +typedef enum +{ + LR1121_MODEM_RADIO_GFSK_CRC_OFF = 0x01, //!< CRC check deactivated + LR1121_MODEM_RADIO_GFSK_CRC_1_BYTE = 0x00, + LR1121_MODEM_RADIO_GFSK_CRC_2_BYTES = 0x02, + LR1121_MODEM_RADIO_GFSK_CRC_1_BYTE_INV = 0x04, + LR1121_MODEM_RADIO_GFSK_CRC_2_BYTES_INV = 0x06, +} lr1121_modem_radio_gfsk_crc_type_t; + +/*! + * @brief GFSK data whitening configurations + */ +typedef enum +{ + LR1121_MODEM_RADIO_GFSK_DC_FREE_OFF = 0x00, //!< Whitening deactivated + LR1121_MODEM_RADIO_GFSK_DC_FREE_WHITENING = 0x01, //!< Whitening enabled + LR1121_MODEM_RADIO_GFSK_DC_FREE_WHITENING_SX128X_COMP = 0x03, //!< Whitening enabled - SX128x compatibility +} lr1121_modem_radio_gfsk_dc_free_t; + +/*! + * @brief GFSK Header Type configurations + * + * This parameter indicates whether or not the payload length is sent and read over the air. + * + * If the payload length is known beforehand by both transmitter and receiver, therefore there is no need to send it + * over the air. Otherwise, setting this parameter to LR1121_MODEM_RADIO_GFSK_PKT_VAR_LEN will make the modem to + * automatically prepand a byte containing the payload length to the the payload on transmitter side. On receiver side, + * this first byte is read to set the payload length to read. + * + * This configuration is only available for GFSK packet types. + */ +typedef enum +{ + LR1121_MODEM_RADIO_GFSK_PKT_FIX_LEN = 0x00, //!< Payload length is not sent/read over the air + LR1121_MODEM_RADIO_GFSK_PKT_VAR_LEN = 0x01, //!< Payload length is sent/read over the air + LR1121_MODEM_RADIO_GFSK_PKT_VAR_LEN_SX128X_COMP = + 0x02, //!< Payload length is sent/read over the air - SX128x compatibility +} lr1121_modem_radio_gfsk_pkt_len_modes_t; + +/*! + * @brief GFSK Preamble Detector Length configurations + * + * This parameter sets the minimum length of preamble bits to be received to continue reception of incoming packet. If a + * packet with preamble length lower than this value is being received, the reception stops without generating IRQ. + * + * This parameter has no impact on TX operations. + */ +typedef enum +{ + LR1121_MODEM_RADIO_GFSK_PREAMBLE_DETECTOR_OFF = 0x00, + LR1121_MODEM_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_8BITS = 0x04, + LR1121_MODEM_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_16BITS = 0x05, + LR1121_MODEM_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_24BITS = 0x06, + LR1121_MODEM_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_32BITS = 0x07 +} lr1121_modem_radio_gfsk_preamble_detector_t; + +/*! + * @brief LoRa Cyclic Redundancy Check configurations + */ +typedef enum +{ + LR1121_MODEM_RADIO_LORA_CRC_OFF = 0x00, //!< CRC deactivated + LR1121_MODEM_RADIO_LORA_CRC_ON = 0x01, //!< CRC activated +} lr1121_modem_radio_lora_crc_t; + +/*! + * @brief LoRa Header type configurations + */ +typedef enum +{ + LR1121_MODEM_RADIO_LORA_PKT_EXPLICIT = 0x00, //!< Explicit header: transmitted over the air + LR1121_MODEM_RADIO_LORA_PKT_IMPLICIT = 0x01, //!< Implicit header: not transmitted over the air +} lr1121_modem_radio_lora_pkt_len_modes_t; + +/*! + * @brief LoRa IQ mode configurations + * + * LoRa IQ modes are mutually exclusives: a physical packet sent with standard IQ will not be received by a receiver + * configured with inverted IQ. + */ +typedef enum +{ + LR1121_MODEM_RADIO_LORA_IQ_STANDARD = 0x00, //!< IQ standard + LR1121_MODEM_RADIO_LORA_IQ_INVERTED = 0x01, //!< IQ inverted +} lr1121_modem_radio_lora_iq_t; + +/*! + * @brief Packet type values + */ +typedef enum +{ + LR1121_MODEM_RADIO_PKT_NONE = 0x00, //!< State after cold start + LR1121_MODEM_RADIO_PKT_TYPE_GFSK = 0x01, //!< GFSK modulation + LR1121_MODEM_RADIO_PKT_TYPE_LORA = 0x02, //!< LoRa modulation + LR1121_MODEM_RADIO_PKT_TYPE_BPSK = 0x03, //!< BPSK modulation + LR1121_MODEM_RADIO_PKT_TYPE_LR_FHSS = 0x04, //!< LR-FHSS modulation +} lr1121_modem_radio_pkt_type_t; + +/*! + * @brief Select power amplifier supply source + */ +typedef enum +{ + LR1121_MODEM_RADIO_PA_REG_SUPPLY_VREG = 0x00, //!< Power amplifier supplied by the main regulator + LR1121_MODEM_RADIO_PA_REG_SUPPLY_VBAT = 0x01 //!< Power amplifier supplied by the battery +} lr1121_modem_radio_pa_reg_supply_t; + +/*! + * @brief RX Duty Cycle Modes + */ +typedef enum +{ + LR1121_MODEM_RADIO_RX_DUTY_CYCLE_MODE_RX = 0x00, //!< LoRa/GFSK: Uses Rx for listening to packets + LR1121_MODEM_RADIO_RX_DUTY_CYCLE_MODE_CAD = 0x01, //!< Only in LoRa: Uses CAD to listen for over-the-air activity +} lr1121_modem_radio_rx_duty_cycle_mode_t; + +/*! + * @brief GFSK Bandwidth configurations + */ +typedef enum +{ + LR1121_MODEM_RADIO_GFSK_BW_4800 = 0x1F, //!< Bandwidth 4.8 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_5800 = 0x17, //!< Bandwidth 5.8 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_7300 = 0x0F, //!< Bandwidth 7.3 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_9700 = 0x1E, //!< Bandwidth 9.7 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_11700 = 0x16, //!< Bandwidth 11.7 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_14600 = 0x0E, //!< Bandwidth 14.6 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_19500 = 0x1D, //!< Bandwidth 19.5 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_23400 = 0x15, //!< Bandwidth 23.4 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_29300 = 0x0D, //!< Bandwidth 29.3 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_39000 = 0x1C, //!< Bandwidth 39.0 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_46900 = 0x14, //!< Bandwidth 46.9 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_58600 = 0x0C, //!< Bandwidth 58.6 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_78200 = 0x1B, //!< Bandwidth 78.2 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_93800 = 0x13, //!< Bandwidth 93.8 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_117300 = 0x0B, //!< Bandwidth 117.3 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_156200 = 0x1A, //!< Bandwidth 156.2 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_187200 = 0x12, //!< Bandwidth 187.2 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_234300 = 0x0A, //!< Bandwidth 232.3 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_312000 = 0x19, //!< Bandwidth 312.0 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_373600 = 0x11, //!< Bandwidth 373.6 kHz DSB + LR1121_MODEM_RADIO_GFSK_BW_467000 = 0x09 //!< Bandwidth 467.0 kHz DSB +} lr1121_modem_radio_gfsk_bw_t; + +/*! + * @brief Possible automatic actions when Channel Activity Detection operations terminate + * + * For RADIO_EXIT_MODE_CAD_RX, LR1121 enters RX mode on activity detected. The timeout value for this RX operation is + * defined as: + * + * \f$ 31.25us \times timeout \f$ + * + * With \f$ timeout \f$ defined in RadioCadParams_t::timeout + * + * If the CAD operation is negative with RADIO_CAD_EXIT_MODE_RX or if CAD operation is positive with + * RADIO_CAD_EXIT_MODE_TX, therefore the LR1121 enters Standby RC mode. + */ +typedef enum +{ + LR1121_MODEM_RADIO_CAD_EXIT_MODE_STANDBYRC = 0x00, //!< Enter standby RC mode after CAD operation + LR1121_MODEM_RADIO_CAD_EXIT_MODE_RX = 0x01, //!< Enter in RX mode if an activity is detected + LR1121_MODEM_RADIO_CAD_EXIT_MODE_TX = 0x10, //!< Enter in TX mode if no activity is detected +} lr1121_modem_radio_cad_exit_mode_t; + +/*! + * @brief Pulse shape configurations + */ +typedef enum +{ + LR1121_MODEM_RADIO_GFSK_PULSE_SHAPE_OFF = 0x00, //!< No filter applied + LR1121_MODEM_RADIO_GFSK_PULSE_SHAPE_BT_03 = 0x08, //!< Gaussian BT 0.3 + LR1121_MODEM_RADIO_GFSK_PULSE_SHAPE_BT_05 = 0x09, //!< Gaussian BT 0.5 + LR1121_MODEM_RADIO_GFSK_PULSE_SHAPE_BT_07 = 0x0A, //!< Gaussian BT 0.7 + LR1121_MODEM_RADIO_GFSK_PULSE_SHAPE_BT_1 = 0x0B //!< Gaussian BT 1.0 +} lr1121_modem_radio_gfsk_pulse_shape_t; + +/*! + * @brief BPSK pulse shape configurations + */ +typedef enum +{ + LR1121_MODEM_RADIO_DBPSK_PULSE_SHAPE = 0x16, //!< Double OSR / RRC / BT 0.7 +} lr1121_modem_radio_bpsk_pulse_shape_t; + +/*! + * @brief LR-FHSS bitrate configurations + */ +typedef enum +{ + LR1121_MODEM_RADIO_LR_FHSS_BITRATE_488_BPS = + ( int ) ( LR1121_MODEM_RADIO_LR_FHSS_BITRATE_DIVIDE_BY_256 + + LR1121_MODEM_RADIO_LR_FHSS_BITRATE_IN_256_BPS_STEPS ), //!< 488.28215 bps +} lr1121_modem_radio_lr_fhss_bitrate_t; + +/*! + * @brief LR-FHSS pulse shape configurations + */ +typedef enum +{ + LR1121_MODEM_RADIO_LR_FHSS_PULSE_SHAPE_BT_1 = 0x0B //!< Gaussian BT 1.0 +} lr1121_modem_radio_lr_fhss_pulse_shape_t; + +/*! + * @brief Channel Activity Detection parameters + * + * Parameters detPeak and detMin are to be used for tuning the sensitivity of Channel Activity Detection. It depends on + * Spreading Factor, Bandwidth and symbolNum. + * + * For detPeak, the 5 MSBits are encoding the integer part, the 3 LSBits are encoding 1/8 of the decimal part. For + * instance, \f$detPeak = 50\f$ (= 0x32) leads to a ratio being \f$6 + 2 * 1/8 = 6.25\f$. + * + * detMin is unit free and represents the ratio between the minimal power of a correlation peak and measurement gain + * that can be considered as a peak detection. It helps to avoid detection on noise. Authorized values a from 0 to 181. + */ +typedef struct lr1121_modem_radio_cad_params_s +{ + uint8_t cad_symb_nb; //!< Number of symbols used for CAD detection + uint8_t cad_detect_peak; //!< Ratio for CAD between correlator peak and average + //!< (Default 0x32) + uint8_t cad_detect_min; //!< Minimum power of the correlation peak to be + //!< considered as a positive CAD (Default 0x0A) + lr1121_modem_radio_cad_exit_mode_t cad_exit_mode; //!< Automated action on CAD completion + uint32_t cad_timeout; //!< Value used to compute timeout +} lr1121_modem_radio_cad_params_t; + +/*! + * @brief Status of GFSK received packet + */ +typedef struct lr1121_modem_radio_pkt_status_gfsk_s +{ + int8_t rssi_sync_in_dbm; //!< RSSI value latched on detection of the last received packet Sync Address + int8_t rssi_avg_in_dbm; //!< RSSI averaged over the payload of the last received packet + uint8_t rx_len_in_bytes; //!< Length of the last received packet [Bytes] + bool is_addr_err; //!< Address filtering status. Asserted if received packet address does not match node address + //!< nor broadcast address + bool is_crc_err; //!< CRC status of the current packet (applicable only in RX, with CRC enabled) + bool is_len_err; //!< Asserted when the length of last received packet is greater than the maximal length + //!< (applicable only in RX with variable length packet) + bool is_abort_err; //!< Asserted when the current packet has been aborted (applicable in RX and TX) + bool is_received; //!< Asserted when packet reception is done (applicable in RX) + bool is_sent; //!< Asserted when packet transmission is done (applicable in TX) +} lr1121_modem_radio_pkt_status_gfsk_t; + +/*! + * @brief Status of received packet + */ +typedef struct lr1121_modem_radio_pkt_status_lora_s +{ + int8_t rssi_pkt_in_dbm; //!< Average RSSI over last received packet. + int8_t snr_pkt_in_db; //!< SNR estimated on last received packet. + int8_t signal_rssi_pkt_in_dbm; //!< RSSI of last packet latched after +} lr1121_modem_radio_pkt_status_lora_t; + +/*! + * @brief Length and offset of received packet + */ +typedef struct lr1121_modem_radio_rx_buffer_status_s +{ + uint8_t pld_len_in_bytes; //!< Length of received packet [Bytes] + uint8_t buffer_start_pointer; //!< Offset in the reception buffer of + //!< first byte received [Bytes] +} lr1121_modem_radio_rx_buffer_status_t; + +/*! + * @brief GFSK packet statistic structure + */ +typedef struct lr1121_modem_radio_stats_gfsk_s +{ + uint16_t nb_pkt_received; //!< Total number of received packets + uint16_t nb_pkt_crc_error; //!< Total number of received packets with CRC error + uint16_t nb_pkt_len_error; //!< Total number of received packets with a length error +} lr1121_modem_radio_stats_gfsk_t; + +/*! + * @brief LoRa packet statistic structure + */ +typedef struct lr1121_modem_radio_stats_lora_s +{ + uint16_t nb_pkt_received; //!< Total number of received packets + uint16_t nb_pkt_crc_error; //!< Total number of received packets with CRC error + uint16_t nb_pkt_header_error; //!< Total number of packets with header error + uint16_t nb_pkt_falsesync; //!< Total number of false sync +} lr1121_modem_radio_stats_lora_t; + +/*! + * @brief Modulation configuration for GFSK packet + */ +typedef struct lr1121_modem_radio_mod_params_gfsk_s +{ + uint32_t br_in_bps; //!< GFSK bitrate [bit/s] + lr1121_modem_radio_gfsk_pulse_shape_t pulse_shape; //!< GFSK pulse shape + lr1121_modem_radio_gfsk_bw_t bw_dsb_param; //!< GFSK bandwidth + uint32_t fdev_in_hz; //!< GFSK frequency deviation [Hz] +} lr1121_modem_radio_mod_params_gfsk_t; + +/*! + * @brief Modulation configuration for BPSK packet + */ +typedef struct lr1121_modem_radio_mod_params_bpsk_s +{ + uint32_t br_in_bps; //!< BPSK bitrate [bit/s] + lr1121_modem_radio_bpsk_pulse_shape_t pulse_shape; //!< BPSK pulse shape +} lr1121_modem_radio_mod_params_bpsk_t; + +/*! + * @brief Modulation configuration for LoRa packet + */ +typedef struct lr1121_modem_radio_mod_params_lora_s +{ + lr1121_modem_radio_lora_sf_t sf; //!< LoRa spreading factor + lr1121_modem_radio_lora_bw_t bw; //!< LoRa bandwidth + lr1121_modem_radio_lora_cr_t cr; //!< LoRa coding rate + uint8_t ldro; //!< LoRa LDRO +} lr1121_modem_radio_mod_params_lora_t; + +/*! + * @brief Modulation configuration for LR-FHSS packets + */ +typedef struct lr1121_modem_radio_mod_params_lr_fhss_s +{ + lr1121_modem_radio_lr_fhss_bitrate_t br_in_bps; //!< LR-FHSS bitrate + lr1121_modem_radio_lr_fhss_pulse_shape_t pulse_shape; //!< LR-FHSS pulse shape +} lr1121_modem_radio_mod_params_lr_fhss_t; + +/*! + * @brief Packet parameter configuration for GFSK packets + */ +typedef struct lr1121_modem_radio_pkt_params_gfsk_s +{ + uint16_t preamble_len_in_bits; //!< GFSK Preamble length [bits] + lr1121_modem_radio_gfsk_preamble_detector_t preamble_detector; //!< GFSK Preamble detection configuration + uint8_t sync_word_len_in_bits; //!< GFSK Syncword length [bits] + lr1121_modem_radio_gfsk_address_filtering_t address_filtering; //!< GFSK Address filtering/comparison configuration + lr1121_modem_radio_gfsk_pkt_len_modes_t header_type; //!< GFSK Header type configuration + uint8_t pld_len_in_bytes; //!< GFSK Payload length [bytes] + lr1121_modem_radio_gfsk_crc_type_t crc_type; //!< GFSK CRC configuration + lr1121_modem_radio_gfsk_dc_free_t dc_free; //!< GFSK Whitening configuration +} lr1121_modem_radio_pkt_params_gfsk_t; + +/*! + * @brief Packet parameter configuration for BPSK packets + */ +typedef struct lr1121_modem_radio_pkt_params_bpsk_s +{ + uint8_t pld_len_in_bytes; //!< Payload length [bytes] + uint16_t ramp_up_delay; //!< Delay to fine tune ramp-up time, if non-zero + uint16_t ramp_down_delay; //!< Delay to fine tune ramp-down time, if non-zero + uint16_t pld_len_in_bits; //!< If non-zero, used to ramp down PA before end of a payload with length that is not a + //!< multiple of 8. pld_len_in_bits <= pld_len_in_bytes * 8 +} lr1121_modem_radio_pkt_params_bpsk_t; + +/*! + * @brief Packet parameter configuration for LoRa packets + */ +typedef struct lr1121_modem_radio_pkt_params_lora_s +{ + uint16_t preamble_len_in_symb; //!< LoRa Preamble length [symbols] + lr1121_modem_radio_lora_pkt_len_modes_t header_type; //!< LoRa Header type configuration + uint8_t pld_len_in_bytes; //!< LoRa Payload length [bytes] + lr1121_modem_radio_lora_crc_t crc; //!< LoRa CRC configuration + lr1121_modem_radio_lora_iq_t iq; //!< LoRa IQ configuration +} lr1121_modem_radio_pkt_params_lora_t; + +/*! + * @brief Configuration of Power Amplifier + * + * @p pa_duty_cycle controls the duty cycle of Power Amplifier according to: + * \f$ dutycycle = 0.2 + 0.04 \times pa\_duty\_cycle \f$ + * It can be used to adapt the TX multi-band operation using a single-matching network. + * + * The allowed duty cycle values for LPA are from 0.2 to 0.48 (by step of 0.04). Therefore possible values for + * pa_duty_cycle go from 0 to 7. + * + * The allowed duty cycle values for HPA go from 0.2 to 0.36 (by step of 0.04). Therefore in this case, the possible + * values for pa_duty_cycle go from 0 to 4. + * + * @p pa_hp_sel controls the number of slices for HPA according to: \f$ \#slices = pa\_hp\_sel + 1 \f$ + */ +typedef struct lr1121_modem_radio_pa_cfg_s +{ + lr1121_modem_radio_pa_selection_t pa_sel; //!< Power Amplifier selection + lr1121_modem_radio_pa_reg_supply_t pa_reg_supply; //!< Power Amplifier regulator supply source + uint8_t pa_duty_cycle; //!< Power Amplifier duty cycle (Default 0x04) + uint8_t pa_hp_sel; //!< Number of slices for HPA (Default 0x07) +} lr1121_modem_radio_pa_cfg_t; + +/*! + * @brief RSSI calibration table + */ +typedef struct lr1121_modem_radio_rssi_calibration_table_s +{ + struct + { + uint8_t g4; + uint8_t g5; + uint8_t g6; + uint8_t g7; + uint8_t g8; + uint8_t g9; + uint8_t g10; + uint8_t g11; + uint8_t g12; + uint8_t g13; + uint8_t g13hp1; + uint8_t g13hp2; + uint8_t g13hp3; + uint8_t g13hp4; + uint8_t g13hp5; + uint8_t g13hp6; + uint8_t g13hp7; + } gain_tune; //!< Used to set gain tune value for RSSI calibration + + int16_t gain_offset; //!< Used to set gain offset value for RSSI calibration +} lr1121_modem_radio_rssi_calibration_table_t; + +/*! + * @brief Values to use to setup LNA LF0 configuration + * + * LNA can be configured in either of the 3 modes: Single N, Single P or differential (which is default) + * + */ +typedef enum +{ + LR1121_MODEM_RADIO_LNA_MODE_SINGLE_RFI_N_LF0 = 1, //!< Use only RFI_N_LF0 antenna + LR1121_MODEM_RADIO_LNA_MODE_SINGLE_RFI_P_LF0 = 2, //!< Use only RFI_P_LF0 antenna + LR1121_MODEM_RADIO_LNA_MODE_DIFFERENTIAL_LF0 = 3 //!< Configure LNA LF0 in differential mode (default) +} lr1121_modem_radio_lna_mode_t; +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_RADIO_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_regmem.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_regmem.h new file mode 100755 index 0000000..ecbb611 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_regmem.h @@ -0,0 +1,209 @@ +/*! + * @file lr1121_modem_regmem.h + * + * @brief Register/memory driver definition for LR1121 + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_REGMEM_H +#define LR1121_MODEM_REGMEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr1121_modem_common.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/*! + * @brief Maximum number of words that can be written to / read from a LR1121 chip with regmem32 commands + */ +#define LR1121_MODEM_REGMEM_MAX_WRITE_READ_WORDS 64 + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Write up to 64 words into register memory space of LR1121. + * + * A word is 32-bit long. The writing operations write contiguously in register memory, starting at the address + * provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start writing operation + * @param [in] buffer The buffer of words to write into memory. Its size must be enough to contain length words. + * @param [in] length Number of words to write into memory + * + * @returns Operation status + * + * @see lr1121_modem_regmem_read_regmem32 + */ +lr1121_modem_response_code_t lr1121_modem_regmem_write_regmem32( const void* context, const uint32_t address, + const uint32_t* buffer, const uint8_t length ); + +/*! + * @brief Read up to 64 words into register memory space of LR1121. + * + * A word is 32-bit long. The reading operations read contiguously from register memory, starting at the address + * provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start reading operation + * @param [in] length Number of words to read from memory + * @param [out] buffer Pointer to a words array to be filled with content from memory. Its size must be enough to + * contain at least length words. + * + * @returns Operation status + * + * @see lr1121_modem_regmem_write_regmem32 + */ +lr1121_modem_response_code_t lr1121_modem_regmem_read_regmem32( const void* context, const uint32_t address, + uint32_t* buffer, const uint8_t length ); + +/*! + * @brief Write bytes into register memory space of LR1121. + * + * A byte is 8-bit long. The writing operations write contiguously in register memory, starting at the address provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start writing operation + * @param [in] buffer The buffer of bytes to write into memory. Its size must be enough to contain length bytes + * @param [in] length Number of bytes to write into memory + * + * @returns Operation status + * + * @see lr1121_modem_regmem_read_mem8 + */ +lr1121_modem_response_code_t lr1121_modem_regmem_write_mem8( const void* context, const uint32_t address, + const uint8_t* buffer, const uint8_t length ); + +/*! + * @brief Read bytes into register memory space of LR1121. + * + * A byte is 8-bit long. The reading operations read contiguously from register memory, starting at the address + * provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start reading operation + * @param [in] length Number of bytes to read from memory + * @param [in] buffer Pointer to a byte array to be filled with content from memory. Its size must be enough to contain + * at least length bytes + * + * @returns Operation status + * + * @see lr1121_modem_regmem_write_mem8 + */ +lr1121_modem_response_code_t lr1121_modem_regmem_read_mem8( const void* context, const uint32_t address, + uint8_t* buffer, const uint8_t length ); + +/*! + * @brief Write bytes into radio TX buffer memory space of LR1121. + * + * @param [in] context Chip implementation context + * @param [in] buffer The buffer of bytes to write into radio buffer. Its size must be enough to contain length bytes + * @param [in] length Number of bytes to write into radio buffer + * + * @returns Operation status + * + * @see lr1121_modem_regmem_read_buffer8 + */ +lr1121_modem_response_code_t lr1121_modem_regmem_write_buffer8( const void* context, const uint8_t* buffer, + const uint8_t length ); + +/*! + * @brief Read bytes from radio RX buffer memory space of LR1121. + * + * @param [in] context Chip implementation context + * @param [in] buffer Pointer to a byte array to be filled with content from radio buffer. Its size must be enough to + * contain at least length bytes + * @param [in] offset Memory offset to start reading + * @param [in] length Number of bytes to read from radio buffer + * + * @returns Operation status + * + * @see lr1121_modem_regmem_write_buffer8 + */ +lr1121_modem_response_code_t lr1121_modem_regmem_read_buffer8( const void* context, uint8_t* buffer, + const uint8_t offset, const uint8_t length ); + +/*! + * @brief Clear radio RX buffer + * + * Set to 0x00 all content of the radio RX buffer + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_regmem_clear_rxbuffer( const void* context ); + +/*! + * @brief Read-modify-write data at given register/memory address + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to be modified + * @param [in] mask The mask to be applied on read data + * @param [in] data The data to be written + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_regmem_write_regmem32_mask( const void* context, const uint32_t address, + const uint32_t mask, const uint32_t data ); + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_REGMEM_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_relay.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_relay.h new file mode 100755 index 0000000..a5132bc --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_relay.h @@ -0,0 +1,99 @@ +/*! + * @file lr1121_modem_relay.h + * + * @brief Relay driver for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_RELAY_H +#define LR1121_MODEM_RELAY_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr1121_modem_common.h" +#include "lr1121_modem_relay_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Get the Tx relay configuration + * + * @param [in] context Chip implementation context + * @param [out] configuration Tx relay configuration + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_relay_get_tx_config( const void* context, + lr1121_modem_relay_tx_configuration_t* configuration ); + +/*! + * @brief Set the Tx relay configuration + * + * @param [in] context Chip implementation context + * @param [out] configuration Tx relay configuration to set + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_relay_set_tx_config( + const void* context, const lr1121_modem_relay_tx_configuration_t* configuration ); + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_RELAY_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_relay_types.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_relay_types.h new file mode 100755 index 0000000..4031e20 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_relay_types.h @@ -0,0 +1,127 @@ +/*! + * @file lr1121_modem_relay_types.h + * + * @brief Relay driver types for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_RELAY_TYPES_H +#define LR1121_MODEM_RELAY_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief Relay activation + * + * Refer to LoRaWAN Relay specification document for details. + */ +typedef enum +{ + LR1121_MODEM_RELAY_ACTIVATION_DISABLE = 0x00, //!< The relay is disabled + LR1121_MODEM_RELAY_ACTIVATION_ENABLE = + 0x01, //!< The relay is enabled, even if no WOR ack nor downlink on RxR is received + LR1121_MODEM_RELAY_ACTIVATION_DYNAMIC = + 0x02, //!< The device automatically enable the relay if no downlink is received after several uplinks + LR1121_MODEM_RELAY_ACTIVATION_DEVICE_CONTROLLED = 0x03, //!< The device automatically enable or disable the relay +} lr1121_modem_relay_activation_t; + +/** + * @brief Smart level configuration + * + * Refer to LoRaWAN Relay specification document for details. + */ +typedef enum +{ + LR1121_MODEM_RELAY_SMART_LEVEL_8 = + 0x00, //!< The relay shall be enabled if no valid downlink is received during 8 consecutive uplinks + LR1121_MODEM_RELAY_SMART_LEVEL_16 = + 0x01, //!< The relay shall be enabled if no valid downlink is received during 16 consecutive uplinks + LR1121_MODEM_RELAY_SMART_LEVEL_32 = + 0x02, //!< The relay shall be enabled if no valid downlink is received during 32 consecutive uplinks + LR1121_MODEM_RELAY_SMART_LEVEL_64 = + 0x03, //!< The relay shall be enabled if no valid downlink is received during 64 consecutive uplinks +} lr1121_modem_relay_smart_level_t; + +/** + * @brief Configuration of relay Tx + */ +typedef struct +{ + uint32_t wor_second_channel_frequency_hz; //!< Frequency of the second Wake On Radio channel (Hz) + uint32_t + wor_ack_second_channel_frequency_hz; //!< Frequency of the second Wake On Radio acknowledgment channel (Hz) + uint8_t wor_second_channel_datarate; //!< Datarate of the second Wake On Radio chanel + uint8_t wor_second_channel_enable; //!< Wake On Radio second channel enable + uint8_t backoff_behavior; //!< Indicate number of missed ACK tolerated before sending the LoRaWAN uplink. Possible + //!< values in [0:63] (0 means the LoRaWAN uplink is always sent) + lr1121_modem_relay_activation_t activation; //!< Relay activation configuration + lr1121_modem_relay_smart_level_t smart_level; //!< Smart level configuration. Only valid if @ref activation is set + //!< to @ref LR1121_MODEM_RELAY_ACTIVATION_DYNAMIC + uint8_t missed_ack_to_unsynchronized_threshold; //!< Number of consecutively WOR ACK to miss before switching to + //!< unsynchronized state +} lr1121_modem_relay_tx_configuration_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_RELAY_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_system.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_system.h new file mode 100755 index 0000000..545b7a7 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_system.h @@ -0,0 +1,606 @@ +/*! + * @file lr1121_modem_system.h + * + * @brief System driver definition for LR1121 + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_SYSTEM_H +#define LR1121_MODEM_SYSTEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include +#include "lr1121_modem_common.h" +#include "lr1121_modem_system_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/*! + * @brief Frequency step in MHz used to compute the image calibration parameter + * + * @see lr1121_modem_system_calibrate_image_in_mhz + */ +#define LR1121_MODEM_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ 4 + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Reset the radio + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_system_reset( const void* context ); + +/** + * @brief Wake the radio up from sleep mode. + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_system_wakeup( const void* context ); + +/*! + * @brief Return stat1, stat2, and irq_status + * + * @param [in] context Chip implementation context + * @param [out] stat1 Pointer to a variable for holding stat1. Can be NULL. + * @param [out] stat2 Pointer to a variable for holding stat2. Can be NULL. + * @param [out] irq_status Pointer to a variable for holding irq_status. Can be NULL. + * + * @returns Operation status + * + * @remark To simplify system integration, this function does not actually execute the GetStatus command, which would + * require bidirectional SPI communication. It obtains the stat1, stat2, and irq_status values by performing an ordinary + * SPI read (which is required to send null/NOP bytes on the MOSI line). This is possible since the LR1121 returns these + * values automatically whenever a read that does not directly follow a response-carrying command is performed. + * Unlike with the GetStatus command, however, the reset status information is NOT cleared by this command. The function + * @ref lr1121_modem_system_clear_reset_status_info may be used for this purpose when necessary. + */ +lr1121_modem_response_code_t lr1121_modem_system_get_status( const void* context, lr1121_modem_system_stat1_t* stat1, + lr1121_modem_system_stat2_t* stat2, + lr1121_modem_system_irq_mask_t* irq_status ); + +/*! + * @brief Clear the reset status information stored in stat2 + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_system_clear_reset_status_info( const void* context ); + +/*! + * @brief Return irq_status + * + * @param [in] context Chip implementation context + * @param [out] irq_status irq_status status variable + * + * @returns Operation status + */ +static inline lr1121_modem_response_code_t lr1121_modem_system_get_irq_status( + const void* context, lr1121_modem_system_irq_mask_t* irq_status ) +{ + return lr1121_modem_system_get_status( context, 0, 0, irq_status ); +} + +/*! + * @brief Return the version of the system (hardware and software) + * + * @param [in] context Chip implementation context + * @param [out] version Pointer to the structure holding the system version + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_system_get_version( const void* context, + lr1121_modem_system_version_t* version ); + +/*! + * @brief Return the system errors + * + * Errors may be fixed following: + * - calibration error can be fixed by attempting another RC calibration; + * - XOsc related errors may be due to hardware problems, can be fixed by reset; + * - PLL lock related errors can be due to not-locked PLL, or by attempting to use an out-of-band frequency, can be + * fixed by executing a PLL calibration, or by using other frequencies. + * + * @param [in] context Chip implementation context + * @param [out] errors Pointer to a value holding error flags + * + * @returns Operation status + * + * @see lr1121_modem_system_calibrate, lr1121_modem_system_calibrate_image, lr1121_modem_system_clear_errors + */ +lr1121_modem_response_code_t lr1121_modem_system_get_errors( const void* context, uint16_t* errors ); + +/*! + * @brief Clear all error flags pending. + * + * This function cannot be used to clear flags individually. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr1121_modem_system_get_errors + */ +lr1121_modem_response_code_t lr1121_modem_system_clear_errors( const void* context ); + +/*! + * @brief lr1121_modem_system_calibrate the requested blocks + * + * This function can be called in any mode of the chip. + * + * The chip will return to standby RC mode on exit. Potential calibration issues can be read out with + * lr1121_modem_system_get_errors command. + * + * @param [in] context Chip implementation context + * @param [in] calib_param Structure holding the reference to blocks to be calibrated + * + * @returns Operation status + * + * @see lr1121_modem_system_get_errors + */ +lr1121_modem_response_code_t lr1121_modem_system_calibrate( const void* context, const uint8_t calib_param ); + +/*! + * @brief Configure the regulator mode to be used in specific modes + * + * This function shall only be called in standby RC mode. + * + * The reg_mode parameter defines if the DC-DC converter is switched on in the following modes: STANDBY XOSC, FS, RX, TX + * and RX_CAPTURE. + * + * @param [in] context Chip implementation context + * @param [in] reg_mode Regulator mode configuration + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_system_set_reg_mode( const void* context, + const lr1121_modem_system_reg_mode_t reg_mode ); + +/*! + * @brief Launch an image calibration valid for all frequencies inside an interval, in steps + * + * This function can be called in any mode of the chip. + * + * The chip will return to standby RC mode on exit. Potential calibration issues can be read out with + * lr1121_modem_system_get_errors command. + * + * The frequencies given in parameters are defined in 4MHz step (Eg. 900MHz corresponds to 0xE1). If freq1 = freq2, only + * one calibration is performed. + * + * @param [in] context Chip implementation context + * @param [in] freq1 Image calibration interval lower bound, in steps + * @param [in] freq2 Image calibration interval upper bound, in steps + * + * @remark freq1 must be less than or equal to freq2 + * + * @returns Operation status + * + * @see lr1121_modem_system_get_errors + */ +lr1121_modem_response_code_t lr1121_modem_system_calibrate_image( const void* context, const uint8_t freq1, + const uint8_t freq2 ); + +/*! + * @brief Launch an image calibration valid for all frequencies inside an interval, in MHz + * + * @remark This function relies on @ref lr1121_modem_system_calibrate_image + * + * @param [in] context Chip implementation context + * @param [in] freq1_in_mhz Image calibration interval lower bound, in MHz + * @param [in] freq2_in_mhz Image calibration interval upper bound, in MHz + * + * @remark freq1 must be less than or equal to freq2 + * + * @returns Operation status + * + * @see lr1121_modem_system_calibrate_image + */ +lr1121_modem_response_code_t lr1121_modem_system_calibrate_image_in_mhz( const void* context, + const uint16_t freq1_in_mhz, + const uint16_t freq2_in_mhz ); + +/*! + * @brief Set the RF switch configurations for each RF setup + * + * This function shall only be called in standby RC mode. + * + * By default, no DIO is used to control a RF switch. All DIOs are set in High-Z mode. + * + * @param [in] context Chip implementation context + * @param [in] rf_switch_cfg Pointer to a structure that holds the switches configuration + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_system_set_dio_as_rf_switch( + const void* context, const lr1121_modem_system_rfswitch_cfg_t* rf_switch_cfg ); + +/*! + * @brief Set which interrupt signals are redirected to the dedicated DIO pin + * + * By default, no interrupt signal is redirected. + * + * The dedicated DIO pin will remain asserted until all redirected interrupt signals are cleared with a call to + * lr1121_modem_system_clear_irq_status. + * + * @param [in] context Chip implementation context + * @param [in] irqs_to_enable_dio1 Variable that holds the interrupt mask for dio1 + * @param [in] irqs_to_enable_dio2 Variable that holds the interrupt mask for dio2 + * + * @returns Operation status + * + * @see lr1121_modem_system_clear_irq_status + */ +lr1121_modem_response_code_t lr1121_modem_system_set_dio_irq_params( + const void* context, const lr1121_modem_system_irq_mask_t irqs_to_enable_dio1, + const lr1121_modem_system_irq_mask_t irqs_to_enable_dio2 ); + +/*! + * @brief Clear requested bits in the internal pending interrupt register + * + * @param [in] context Chip implementation context + * @param [in] irqs_to_clear Variable that holds the interrupts to be cleared + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_system_clear_irq_status( const void* context, + const lr1121_modem_system_irq_mask_t irqs_to_clear ); + +/** + * @brief This helper function clears any radio irq status flags that are set and returns the flags that were cleared. + * + * @param [in] context Chip implementation context. + * @param [out] irq Pointer to a variable for holding the system interrupt status. Can be NULL. + * + * @returns Operation status + * + * @see lr1121_modem_system_get_irq_status, lr1121_modem_system_clear_irq_status + */ +lr1121_modem_response_code_t lr1121_modem_system_get_and_clear_irq_status( const void* context, + lr1121_modem_system_irq_mask_t* irq ); + +/*! + * @brief Defines which clock is used as Low Frequency (LF) clock + * + * @param [in] context Chip implementation context + * @param [in] lfclock_cfg Low frequency clock configuration + * @param [in] wait_for_32k_ready Tells the radio if it has to check if 32k source is ready before driving busy low + * + * @returns Operation status + * + * @see lr1121_modem_system_calibrate, lr1121_modem_system_calibrate_image + */ +lr1121_modem_response_code_t lr1121_modem_system_cfg_lfclk( const void* context, + const lr1121_modem_system_lfclk_cfg_t lfclock_cfg, + const bool wait_for_32k_ready ); + +/*! + * @brief Enable and configure TCXO supply voltage and detection timeout + * + * This function shall only be called in standby RC mode. + * + * The timeout parameter is the maximum time the firmware waits for the TCXO to be ready. The timeout duration is given + * by: \f$ timeout\_duration\_us = timeout \times 30.52 \f$ + * + * The TCXO mode can be disabled by setting timeout parameter to 0. + * + * @param [in] context Chip implementation context + * @param [in] tune Supply voltage value + * @param [in] timeout Gating time before which the radio starts its Rx / Tx operation + * + * @returns Operation status + * + * @see lr1121_modem_system_calibrate, lr1121_modem_system_calibrate_image + */ +lr1121_modem_response_code_t lr1121_modem_system_set_tcxo_mode( const void* context, + const lr1121_modem_system_tcxo_supply_voltage_t tune, + const uint32_t timeout ); + +/*! + * @brief Software reset of the chip. + * + * This function should be used to reboot the chip in a specified mode. Rebooting in flash mode presumes that the + * content in flash memory is not corrupted (i.e. the integrity check performed by the bootloader before executing the + * first instruction in flash is OK). + * + * @param [in] context Chip implementation context + * @param [in] stay_in_bootloader Selector to stay in bootloader or execute flash code after reboot. If true, the + * bootloader will not execute the flash code but activate SPI interface to allow firmware upgrade + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_system_reboot( const void* context, const bool stay_in_bootloader ); + +/*! + * @brief Returns the value of Vbat + * + * Vbat value (in V) is a function of Vana (typ. 1.35V) using the following + * formula: \f$ Vbat_{V} = (5 \times \frac{Vbat}{255} - 1) \times Vana \f$ + * + * @param [in] context Chip implementation context + * @param [out] vbat A pointer to the Vbat value + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_system_get_vbat( const void* context, uint8_t* vbat ); + +/*! + * @brief Returns the value of Temp + * + * The temperature (in °C) is a function of Vana (typ. 1.35V), Vbe25 (Vbe voltage @ 25°C, typ. 0.7295V) and VbeSlope + * (typ. -1.7mV/°C) using the following formula: + * \f$ Temperature_{°C} = (\frac{Temp(10:0)}{2047} \times Vana - Vbe25) \times \frac{1000}{VbeSlope} + 25 \f$ + * + * @remark If a TCXO is used, make sure to configure it with @ref lr1121_modem_system_set_tcxo_mode before calling this + * function + * + * @param [in] context Chip implementation context + * @param [out] temp A pointer to the Temp value + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_system_get_temp( const void* context, uint16_t* temp ); + +/*! + * @brief Set the device into Sleep or Deep Sleep Mode + * + * The \p sleep_cfg parameter defines in which sleep mode is to use. + * + * The \p sleep_time parameter sets the sleep duration in number of clock cycles: + * \f$ sleep\_time\_ms = sleep\_time \times \frac{1}{32.768} \f$ + * + * @param [in] context Chip implementation context + * @param [in] sleep_cfg Sleep mode configuration + * @param [in] sleep_time Value of the RTC timeout (if RtcTimeout = 1) + * + * @returns Operation status + * + * @see lr1121_modem_system_set_standby, lr1121_modem_system_set_fs + */ +lr1121_modem_response_code_t lr1121_modem_system_set_sleep( const void* context, + const lr1121_modem_system_sleep_cfg_t sleep_cfg, + const uint32_t sleep_time ); + +/*! + * @brief Set the device into the requested Standby mode + * + * @param [in] context Chip implementation context + * @param [in] standby_cfg Stand by mode configuration (RC or XOSC) + * + * @returns Operation status + * + * @see lr1121_modem_system_set_sleep, lr1121_modem_system_set_fs + */ +lr1121_modem_response_code_t lr1121_modem_system_set_standby( const void* context, + const lr1121_modem_system_standby_cfg_t standby_cfg ); + +/*! + * @brief Set the device into Frequency Synthesis (FS) mode + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr1121_modem_system_set_standby, lr1121_modem_system_set_sleep + */ +lr1121_modem_response_code_t lr1121_modem_system_set_fs( const void* context ); + +/*! + * @brief Erase an info page + * + * @param [in] context Chip implementation context + * @param [in] info_page_id Info page to be erased. Only LR1121_MODEM_SYSTEM_INFOPAGE_1 is allowed. + * + * @returns Operation status + * + * @see lr1121_modem_system_write_infopage, lr1121_modem_system_read_infopage + */ +lr1121_modem_response_code_t lr1121_modem_system_erase_infopage( const void* context, + const lr1121_modem_system_infopage_id_t info_page_id ); + +/*! + * @brief Write data in an info page + * + * @param [in] context Chip implementation context + * @param [in] info_page_id Info page where data are written. Only LR1121_MODEM_SYSTEM_INFOPAGE_1 is allowed. + * @param [in] address Address within the info page (aligned on 32-bit data) + * @param [in] data Pointer to the data to write (data buffer shall be - at least - length words long) + * @param [in] length Number of 32-bit data to write (maximum value is 64) + * + * @returns Operation status + * + * @see lr1121_modem_system_erase_infopage, lr1121_modem_system_read_infopage + */ +lr1121_modem_response_code_t lr1121_modem_system_write_infopage( const void* context, + const lr1121_modem_system_infopage_id_t info_page_id, + const uint16_t address, const uint32_t* data, + const uint8_t length ); + +/*! + * @brief Read data from an info page + * + * It is possible to cross from page 0 to 1 if (address + length >= 512) + * + * @param [in] context Chip implementation context + * @param [in] info_page_id Info page where data are read + * @param [in] address Address within the info page (aligned on 32-bit data) + * @param [out] data Pointer to the data to read (data buffer shall be - at least - length words long) + * @param [in] length Number of 32-bit data to read (maximum value is 64) + * + * @returns Operation status + * + * @see lr1121_modem_system_erase_infopage, lr1121_modem_system_write_infopage + */ +lr1121_modem_response_code_t lr1121_modem_system_read_infopage( const void* context, + const lr1121_modem_system_infopage_id_t info_page_id, + const uint16_t address, uint32_t* data, + const uint8_t length ); + +/*! + * @brief Read and return the Unique Identifier of the LR1121 + * + * @param [in] context Chip implementation context + * @param [out] unique_identifier The buffer to be filled with the Unique Identifier of the LR1121. It is up to the + * application to ensure unique_identifier is long enough to hold the unique identifier + * + * @returns Operation status + * + * @see LR1121_MODEM_SYSTEM_UID_LENGTH + */ +lr1121_modem_response_code_t lr1121_modem_system_read_uid( const void* context, + lr1121_modem_system_uid_t unique_identifier ); + +/*! + * @brief Read and return the Join EUI of the LR1121 + * + * @param [in] context Chip implementation context + * @param [out] join_eui The buffer to be filled with Join EUI of the LR1121. It is up to the application to ensure + * join_eui is long enough to hold the join EUI + * + * @returns Operation status + * + * @see LR1121_MODEM_SYSTEM_JOIN_EUI_LENGTH + */ +lr1121_modem_response_code_t lr1121_modem_system_read_join_eui( const void* context, + lr1121_modem_system_join_eui_t join_eui ); + +/*! + * @brief Compute and return the PIN of the LR1121 based on factory default EUIs + * + * @remark Calling this command also triggers a derivation of network and application keys based on factory default EUIs + * + * @param [in] context Chip implementation context + * @param [out] pin The buffer to be filled with PIN of the LR1121. It is up to the application to ensure pin is long + * enough to hold the PIN + * + * @returns Operation status + * + * @see LR1121_MODEM_SYSTEM_PIN_LENGTH + */ +lr1121_modem_response_code_t lr1121_modem_system_read_pin( const void* context, lr1121_modem_system_pin_t pin ); + +/*! + * @brief Compute and return the PIN of the LR1121 based on EUIs provided as parameters + * + * @remark Calling this command also triggers a derivation of network and application keys based on EUIs provided as + * parameters + * + * @param [in] context Chip implementation context + * @param [in] device_eui Custom Device EUI + * @param [in] join_eui Custom Join EUI + * @param [in] rfu Parameter RFU - shall be set to 0x00 + * @param [out] pin The buffer to be filled with PIN of the LR1121. It is up to the application to ensure pin is long + * enough to hold the PIN + * + * @returns Operation status + * + * @see LR1121_MODEM_SYSTEM_PIN_LENGTH + */ +lr1121_modem_response_code_t lr1121_modem_system_read_pin_custom_eui( const void* context, + lr1121_modem_system_uid_t device_eui, + lr1121_modem_system_join_eui_t join_eui, + uint8_t rfu, lr1121_modem_system_pin_t pin ); + +/*! + * @brief Read and return a 32-bit random number + * + * @remark Radio operating mode must be set into standby. + * + * @param [in] context Chip implementation context + * @param [out] random_number 32-bit random number + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_system_get_random_number( const void* context, uint32_t* random_number ); + +/*! + * @brief Enable the CRC on SPI transactions + * + * @remark This command shall always be sent with a CRC (to both enable and disable the feature). The function does not + * take care of this additional byte - which is under the responsibility of the underlying HAL functions + * + * @param [in] context Chip implementation context + * @param [in] enable_crc CRC + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_system_enable_spi_crc( const void* context, bool enable_crc ); + +/*! + * @brief Configure the GPIO drive in sleep mode + * + * @remark GPIO stands for RF switch and IRQ line DIOs + * + * @note This command is available from firmware version 0x0306 + * + * @param [in] context Chip implementation context + * @param [in] enable_drive GPIO drive configuration (true: enabled / false: disabled) + * + * @returns Operation status + */ +lr1121_modem_response_code_t lr1121_modem_system_drive_dio_in_sleep_mode( const void* context, bool enable_drive ); + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_SYSTEM_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_system_types.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_system_types.h new file mode 100755 index 0000000..badcede --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_modem_system_types.h @@ -0,0 +1,348 @@ +/*! + * @file lr1121_modem_system_types.h + * + * @brief System driver types for LR1121 + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_MODEM_SYSTEM_TYPES_H +#define LR1121_MODEM_SYSTEM_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/*! + * @brief Length of the LR1121 Unique Identifier in bytes + * + * The LR1121 Unique Identifiers is an 8 byte long buffer + */ +#define LR1121_MODEM_SYSTEM_UID_LENGTH ( 8 ) + +/** + * @brief Length of Join Unique Identifier in bytes + */ +#define LR1121_MODEM_SYSTEM_JOIN_EUI_LENGTH ( 8 ) + +/** + * @brief Length of PIN number in bytes + */ +#define LR1121_MODEM_SYSTEM_PIN_LENGTH ( 4 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief Fixed-length array to store a UID + */ +typedef uint8_t lr1121_modem_system_uid_t[LR1121_MODEM_SYSTEM_UID_LENGTH]; + +/** + * @brief Fixed-length array to store a joinEUI + */ +typedef uint8_t lr1121_modem_system_join_eui_t[LR1121_MODEM_SYSTEM_JOIN_EUI_LENGTH]; + +/** + * @brief Fixed-length array to store a PIN + */ +typedef uint8_t lr1121_modem_system_pin_t[LR1121_MODEM_SYSTEM_PIN_LENGTH]; + +/** + * @brief Type to store system interrupt flags + */ +typedef uint32_t lr1121_modem_system_irq_mask_t; + +/** + * @brief Interrupt flags + */ +enum lr1121_modem_system_irq_e +{ + LR1121_MODEM_SYSTEM_IRQ_NONE = ( 0 << 0 ), + LR1121_MODEM_SYSTEM_IRQ_TX_DONE = ( 1 << 2 ), + LR1121_MODEM_SYSTEM_IRQ_RX_DONE = ( 1 << 3 ), + LR1121_MODEM_SYSTEM_IRQ_PREAMBLE_DETECTED = ( 1 << 4 ), + LR1121_MODEM_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID = ( 1 << 5 ), + LR1121_MODEM_SYSTEM_IRQ_HEADER_ERROR = ( 1 << 6 ), + LR1121_MODEM_SYSTEM_IRQ_CRC_ERROR = ( 1 << 7 ), + LR1121_MODEM_SYSTEM_IRQ_CAD_DONE = ( 1 << 8 ), + LR1121_MODEM_SYSTEM_IRQ_CAD_DETECTED = ( 1 << 9 ), + LR1121_MODEM_SYSTEM_IRQ_TIMEOUT = ( 1 << 10 ), + LR1121_MODEM_SYSTEM_IRQ_LR_FHSS_INTRA_PKT_HOP = ( 1 << 11 ), + LR1121_MODEM_SYSTEM_IRQ_EOL = ( 1 << 21 ), + LR1121_MODEM_SYSTEM_IRQ_CMD_ERROR = ( 1 << 22 ), + LR1121_MODEM_SYSTEM_IRQ_ERROR = ( 1 << 23 ), + LR1121_MODEM_SYSTEM_IRQ_FSK_LEN_ERROR = ( 1 << 24 ), + LR1121_MODEM_SYSTEM_IRQ_FSK_ADDR_ERROR = ( 1 << 25 ), + LR1121_MODEM_SYSTEM_IRQ_LORA_RX_TIMESTAMP = ( 1 << 27 ), + LR1121_MODEM_SYSTEM_IRQ_ALL_MASK = + LR1121_MODEM_SYSTEM_IRQ_TX_DONE | LR1121_MODEM_SYSTEM_IRQ_RX_DONE | LR1121_MODEM_SYSTEM_IRQ_PREAMBLE_DETECTED | + LR1121_MODEM_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID | LR1121_MODEM_SYSTEM_IRQ_HEADER_ERROR | + LR1121_MODEM_SYSTEM_IRQ_CRC_ERROR | LR1121_MODEM_SYSTEM_IRQ_CAD_DONE | LR1121_MODEM_SYSTEM_IRQ_CAD_DETECTED | + LR1121_MODEM_SYSTEM_IRQ_TIMEOUT | LR1121_MODEM_SYSTEM_IRQ_LR_FHSS_INTRA_PKT_HOP | LR1121_MODEM_SYSTEM_IRQ_EOL | + LR1121_MODEM_SYSTEM_IRQ_CMD_ERROR | LR1121_MODEM_SYSTEM_IRQ_ERROR | LR1121_MODEM_SYSTEM_IRQ_FSK_LEN_ERROR | + LR1121_MODEM_SYSTEM_IRQ_FSK_ADDR_ERROR | LR1121_MODEM_SYSTEM_IRQ_LORA_RX_TIMESTAMP, +}; + +/** + * @brief Calibration flags + */ +enum lr1121_modem_system_calibration_e +{ + LR1121_MODEM_SYSTEM_CALIB_LF_RC_MASK = ( 1 << 0 ), + LR1121_MODEM_SYSTEM_CALIB_HF_RC_MASK = ( 1 << 1 ), + LR1121_MODEM_SYSTEM_CALIB_PLL_MASK = ( 1 << 2 ), + LR1121_MODEM_SYSTEM_CALIB_ADC_MASK = ( 1 << 3 ), + LR1121_MODEM_SYSTEM_CALIB_IMG_MASK = ( 1 << 4 ), + LR1121_MODEM_SYSTEM_CALIB_PLL_TX_MASK = ( 1 << 5 ), +}; + +/** + * @brief Type for calibration mask + * + * @see lr1121_modem_system_calibration_e + */ +typedef uint8_t lr1121_modem_system_cal_mask_t; + +/** + * @brief Error flags + */ +enum lr1121_modem_system_errors_e +{ + LR1121_MODEM_SYSTEM_ERRORS_LF_RC_CALIB_MASK = ( 1 << 0 ), + LR1121_MODEM_SYSTEM_ERRORS_HF_RC_CALIB_MASK = ( 1 << 1 ), + LR1121_MODEM_SYSTEM_ERRORS_ADC_CALIB_MASK = ( 1 << 2 ), + LR1121_MODEM_SYSTEM_ERRORS_PLL_CALIB_MASK = ( 1 << 3 ), + LR1121_MODEM_SYSTEM_ERRORS_IMG_CALIB_MASK = ( 1 << 4 ), + LR1121_MODEM_SYSTEM_ERRORS_HF_XOSC_START_MASK = ( 1 << 5 ), + LR1121_MODEM_SYSTEM_ERRORS_LF_XOSC_START_MASK = ( 1 << 6 ), + LR1121_MODEM_SYSTEM_ERRORS_PLL_LOCK_MASK = ( 1 << 7 ), +}; + +/** + * @brief Type for system errors mask + * + * @see lr1121_modem_system_errors_e + */ +typedef uint16_t lr1121_modem_system_errors_t; + +/** + * @brief Chip modes + */ +typedef enum +{ + LR1121_MODEM_SYSTEM_CHIP_MODE_SLEEP = 0x00, + LR1121_MODEM_SYSTEM_CHIP_MODE_STBY_RC = 0x01, + LR1121_MODEM_SYSTEM_CHIP_MODE_STBY_XOSC = 0x02, + LR1121_MODEM_SYSTEM_CHIP_MODE_FS = 0x03, + LR1121_MODEM_SYSTEM_CHIP_MODE_RX = 0x04, + LR1121_MODEM_SYSTEM_CHIP_MODE_TX = 0x05, + LR1121_MODEM_SYSTEM_CHIP_MODE_LOC = 0x06, +} lr1121_modem_system_chip_modes_t; + +/** + * @brief Reset status + */ +typedef enum +{ + LR1121_MODEM_SYSTEM_RESET_STATUS_CLEARED = 0x00, + LR1121_MODEM_SYSTEM_RESET_STATUS_ANALOG = 0x01, + LR1121_MODEM_SYSTEM_RESET_STATUS_EXTERNAL = 0x02, + LR1121_MODEM_SYSTEM_RESET_STATUS_SYSTEM = 0x03, + LR1121_MODEM_SYSTEM_RESET_STATUS_WATCHDOG = 0x04, + LR1121_MODEM_SYSTEM_RESET_STATUS_IOCD_RESTART = 0x05, + LR1121_MODEM_SYSTEM_RESET_STATUS_RTC_RESTART = 0x06, +} lr1121_modem_system_reset_status_t; + +/** + * @brief Command status + */ +typedef enum +{ + LR1121_MODEM_SYSTEM_CMD_STATUS_FAIL = 0x00, + LR1121_MODEM_SYSTEM_CMD_STATUS_PERR = 0x01, + LR1121_MODEM_SYSTEM_CMD_STATUS_OK = 0x02, + LR1121_MODEM_SYSTEM_CMD_STATUS_DATA = 0x03, +} lr1121_modem_system_command_status_t; + +/** + * @brief Low-frequency clock modes + */ +typedef enum +{ + LR1121_MODEM_SYSTEM_LFCLK_RC = 0x00, //!< (Default) + LR1121_MODEM_SYSTEM_LFCLK_XTAL = 0x01, + LR1121_MODEM_SYSTEM_LFCLK_EXT = 0x02 +} lr1121_modem_system_lfclk_cfg_t; + +/** + * @brief Regulator modes + */ +typedef enum +{ + LR1121_MODEM_SYSTEM_REG_MODE_LDO = 0x00, //!< (Default) + LR1121_MODEM_SYSTEM_REG_MODE_DCDC = 0x01, +} lr1121_modem_system_reg_mode_t; + +/** + * @brief Info page ID + */ +typedef enum +{ + LR1121_MODEM_SYSTEM_INFOPAGE_0 = 0x00, //!< Info page #0 + LR1121_MODEM_SYSTEM_INFOPAGE_1 = 0x01, //!< Info page #1 +} lr1121_modem_system_infopage_id_t; + +/** + * @brief RF switch configuration pin + */ +enum lr1121_modem_system_rfswitch_cfg_pin_e +{ + LR1121_MODEM_SYSTEM_RFSW0_HIGH = ( 1 << 0 ), + LR1121_MODEM_SYSTEM_RFSW1_HIGH = ( 1 << 1 ), + LR1121_MODEM_SYSTEM_RFSW2_HIGH = ( 1 << 2 ), + LR1121_MODEM_SYSTEM_RFSW3_HIGH = ( 1 << 3 ), + LR1121_MODEM_SYSTEM_RFSW4_HIGH = ( 1 << 4 ), +}; + +/** + * @brief RF switch configuration structure definition + */ +typedef struct lr1121_modem_system_rfswitch_cfg_s +{ + uint8_t enable; //!< Bitmask for DIO to control as RF switches + uint8_t standby; //!< Bitmask for DIO state while chip is in standby mode + uint8_t rx; //!< Bitmask for DIO state while chip is in reception mode + uint8_t tx; //!< Bitmask for DIO state while chip is in transmission mode + uint8_t tx_hp; //!< Bitmask for DIO state while chip is in high power transmission mode + uint8_t tx_hf; //!< Bitmask for DIO state while chip is in high frequency transmission mode +} lr1121_modem_system_rfswitch_cfg_t; + +/** + * @brief Stand by configuration values + */ +typedef enum +{ + LR1121_MODEM_SYSTEM_STANDBY_CFG_RC = 0x00, + LR1121_MODEM_SYSTEM_STANDBY_CFG_XOSC = 0x01 +} lr1121_modem_system_standby_cfg_t; + +/** + * @brief TCXO supply voltage values + */ +typedef enum +{ + LR1121_MODEM_SYSTEM_TCXO_CTRL_1_6V = 0x00, //!< Supply voltage = 1.6v + LR1121_MODEM_SYSTEM_TCXO_CTRL_1_7V = 0x01, //!< Supply voltage = 1.7v + LR1121_MODEM_SYSTEM_TCXO_CTRL_1_8V = 0x02, //!< Supply voltage = 1.8v + LR1121_MODEM_SYSTEM_TCXO_CTRL_2_2V = 0x03, //!< Supply voltage = 2.2v + LR1121_MODEM_SYSTEM_TCXO_CTRL_2_4V = 0x04, //!< Supply voltage = 2.4v + LR1121_MODEM_SYSTEM_TCXO_CTRL_2_7V = 0x05, //!< Supply voltage = 2.7v + LR1121_MODEM_SYSTEM_TCXO_CTRL_3_0V = 0x06, //!< Supply voltage = 3.0v + LR1121_MODEM_SYSTEM_TCXO_CTRL_3_3V = 0x07, //!< Supply voltage = 3.3v +} lr1121_modem_system_tcxo_supply_voltage_t; + +/** + * @brief Status register 1 structure definition + */ +typedef struct lr1121_modem_system_stat1_s +{ + lr1121_modem_system_command_status_t command_status; //!< Status of last command + bool is_interrupt_active; //!< Indicates at least one interrupt is active +} lr1121_modem_system_stat1_t; + +/** + * @brief Status register 2 structure definition + */ +typedef struct lr1121_modem_system_stat2_s +{ + lr1121_modem_system_reset_status_t reset_status; //!< Source of reset + lr1121_modem_system_chip_modes_t chip_mode; //!< Current mode the chip is running + bool is_running_from_flash; //!< Flag indicating if the chip is currently running from flash +} lr1121_modem_system_stat2_t; + +/** + * @brief Chip type values + */ +typedef enum +{ + LR1121_MODEM_SYSTEM_VERSION_TYPE_LR1121 = 0x03, +} lr1121_modem_system_version_type_t; + +/** + * @brief Version structure definition + */ +typedef struct lr1121_modem_system_version_s +{ + uint8_t hw; //!< Hardware field of system version + lr1121_modem_system_version_type_t type; //!< Type field of system version + uint16_t fw; //!< Software field of system version +} lr1121_modem_system_version_t; + +/** + * @brief Sleep configuration structure definition + */ +typedef struct lr1121_modem_system_sleep_cfg_s +{ + bool is_warm_start; //!< Keep configuration and state in retention memory, allowing warm start +} lr1121_modem_system_sleep_cfg_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR1121_MODEM_SYSTEM_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr1121_types.h b/components/esp_lora_1121/include/lr1121_modem/lr1121_types.h new file mode 100755 index 0000000..4a7b0ad --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr1121_types.h @@ -0,0 +1,75 @@ +/*! + * @file lr1121_types.h + * + * @brief Type definitions for LR1121 + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +#ifndef LR1121_TYPES_H +#define LR1121_TYPES_H + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief LR1121 status + */ +typedef enum lr1121_status_e +{ + LR1121_STATUS_OK = 0, //!< Operation terminated successfully + LR1121_STATUS_ERROR = 3, //!< Operation terminated with error +} lr1121_status_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#endif // LR1121_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_modem/lr_fhss_v1_base_types.h b/components/esp_lora_1121/include/lr1121_modem/lr_fhss_v1_base_types.h new file mode 100755 index 0000000..c6afffd --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_modem/lr_fhss_v1_base_types.h @@ -0,0 +1,127 @@ +/** + * @file lr_fhss_v1_base_types.h + * + * @brief Radio-independent LR-FHSS base type definitions, version 1 + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR_FHSS_V1_BASE_TYPES_H__ +#define LR_FHSS_V1_BASE_TYPES_H__ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief LR-FHSS modulation type + */ +typedef enum lr_fhss_v1_modulation_type_e +{ + LR_FHSS_V1_MODULATION_TYPE_GMSK_488 = 0, +} lr_fhss_v1_modulation_type_t; + +/** + * @brief LR-FHSS coding rate + */ +typedef enum lr_fhss_v1_cr_e +{ + LR_FHSS_V1_CR_5_6 = 0x00, + LR_FHSS_V1_CR_2_3 = 0x01, + LR_FHSS_V1_CR_1_2 = 0x02, + LR_FHSS_V1_CR_1_3 = 0x03, +} lr_fhss_v1_cr_t; + +/** + * @brief LR-FHSS grid + */ +typedef enum lr_fhss_v1_grid_e +{ + LR_FHSS_V1_GRID_25391_HZ = 0x00, + LR_FHSS_V1_GRID_3906_HZ = 0x01, +} lr_fhss_v1_grid_t; + +/** + * @brief LR-FHSS bandwidth + */ +typedef enum lr_fhss_v1_bw_e +{ + LR_FHSS_V1_BW_39063_HZ = 0x00, + LR_FHSS_V1_BW_85938_HZ = 0x01, + LR_FHSS_V1_BW_136719_HZ = 0x02, + LR_FHSS_V1_BW_183594_HZ = 0x03, + LR_FHSS_V1_BW_335938_HZ = 0x04, + LR_FHSS_V1_BW_386719_HZ = 0x05, + LR_FHSS_V1_BW_722656_HZ = 0x06, + LR_FHSS_V1_BW_773438_HZ = 0x07, + LR_FHSS_V1_BW_1523438_HZ = 0x08, + LR_FHSS_V1_BW_1574219_HZ = 0x09, +} lr_fhss_v1_bw_t; + +/** + * @brief LR-FHSS parameter structure + */ +typedef struct lr_fhss_v1_params_s +{ + const uint8_t* sync_word; /**< 4-byte sync word */ + lr_fhss_v1_modulation_type_t modulation_type; + lr_fhss_v1_cr_t cr; + lr_fhss_v1_grid_t grid; + lr_fhss_v1_bw_t bw; + bool enable_hopping; + uint8_t header_count; /**< Number of header blocks */ +} lr_fhss_v1_params_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#endif // LR_FHSS_V1_BASE_TYPES_H__ + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_printers/lr1121_modem_printf_info.h b/components/esp_lora_1121/include/lr1121_printers/lr1121_modem_printf_info.h new file mode 100755 index 0000000..b40d7a1 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_printers/lr1121_modem_printf_info.h @@ -0,0 +1,141 @@ +/** + * @file lr1121_modem_printf_info.h + * + * @brief Common Application Helper functions + * + * @copyright + * @parblock + * The Clear BSD License + * Copyright Semtech Corporation 2024. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * @endparblock + */ +#ifndef LR1121_MODEM_PRINTF_INFO_H +#define LR1121_MODEM_PRINTF_INFO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ +#include +#include +#include + +#include "lr1121_modem/lr1121_modem_lorawan.h" +#include "lr1121_modem/lr1121_modem_modem.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/** + * @brief Prints the provided buffer in HEX + * + * @param [in] buffer Buffer to be printed + * @param [in] size Buffer size to be printed + */ +void print_hex_buffer( const uint8_t* buffer, uint8_t size ); + +/** + * @brief Prints the LoRaWAN keys + * + * @param [in] dev_eui Device EUI to be printed + * @param [in] join_eui Join EUI to be printed + * @param [in] use_internal_credentials specify if the internal credentials are used + */ +void print_lorawan_credentials( const uint8_t* dev_eui, const uint8_t* join_eui, const uint8_t* pin, + const bool use_internal_credentials ); + +/** + * @brief Prints the LoRaWAN version + * + * @param [in] modem_version Modem version to be printed + */ +void print_version( lr1121_modem_version_t modem_version ); + +/** + * @brief convert lr1121 modem-e status to string + */ +void modem_status_to_string( lr1121_modem_lorawan_status_t modem_status ); + +/** + * @brief Get the lorawan region from modem and print it + * + * @param [in] context Chip implementation context + * @param [out] modem_region The LoRaWAN region returned by the modem. This pointer can be NULL: in this case the region + * is only printed, and not returned to caller + */ +void get_and_print_lorawan_region_from_modem( const void* context, lr1121_modem_regions_t* modem_region ); + +/** + * @brief Prints the LoRaWAN region + * + * @param [in] region Region to be printed + */ +void print_lorawan_region( lr1121_modem_regions_t region ); + +/** + * @brief Prints the state of certification mode + * + * @param [in] certif_running State of certification mode + */ +void print_certification( lr1121_modem_certification_mode_t certif_running ); + +/** + * @brief Gets and prints the crashlog if the crashlog status bit is set + * + * @param [in] context Chip implementation context + */ +void get_and_print_crashlog( const void* context ); + +#ifdef __cplusplus +} +#endif + +#endif // APPS_UTILITIES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr1121_printers/lr11xx_bootloader_types_str.h b/components/esp_lora_1121/include/lr1121_printers/lr11xx_bootloader_types_str.h new file mode 100755 index 0000000..93f559a --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_printers/lr11xx_bootloader_types_str.h @@ -0,0 +1,48 @@ +/*! + * @file lr11xx_bootloader_types_str.h + * + * @brief Printer helper functions for LR11xx bootloader types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_BOOTLOADER_TYPES_STR_H +#define LR11XX_BOOTLOADER_TYPES_STR_H +#include "lr11xx_driver/lr11xx_bootloader_types.h" +#ifdef __cplusplus +extern "C" { +#endif +const char* lr11xx_bootloader_chip_modes_to_str( const lr11xx_bootloader_chip_modes_t value ); +const char* lr11xx_bootloader_reset_status_to_str( const lr11xx_bootloader_reset_status_t value ); +const char* lr11xx_bootloader_command_status_to_str( const lr11xx_bootloader_command_status_t value ); +#ifdef __cplusplus +} +#endif +#endif // LR11XX_BOOTLOADER_TYPES_STR_H diff --git a/components/esp_lora_1121/include/lr1121_printers/lr11xx_crypto_engine_types_str.h b/components/esp_lora_1121/include/lr1121_printers/lr11xx_crypto_engine_types_str.h new file mode 100755 index 0000000..59ec333 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_printers/lr11xx_crypto_engine_types_str.h @@ -0,0 +1,49 @@ +/*! + * @file lr11xx_crypto_engine_types_str.h + * + * @brief Printer helper functions for LR11xx crypto engine types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_CRYPTO_ENGINE_TYPES_STR_H +#define LR11XX_CRYPTO_ENGINE_TYPES_STR_H +#include "lr11xx_driver/lr11xx_crypto_engine_types.h" +#ifdef __cplusplus +extern "C" { +#endif +const char* lr11xx_crypto_element_to_str( const lr11xx_crypto_element_t value ); +const char* lr11xx_crypto_status_to_str( const lr11xx_crypto_status_t value ); +const char* lr11xx_crypto_lorawan_version_to_str( const lr11xx_crypto_lorawan_version_t value ); +const char* lr11xx_crypto_keys_idx_to_str( const lr11xx_crypto_keys_idx_t value ); +#ifdef __cplusplus +} +#endif +#endif // LR11XX_CRYPTO_ENGINE_TYPES_STR_H diff --git a/components/esp_lora_1121/include/lr1121_printers/lr11xx_lr_fhss_types_str.h b/components/esp_lora_1121/include/lr1121_printers/lr11xx_lr_fhss_types_str.h new file mode 100755 index 0000000..d73cd3a --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_printers/lr11xx_lr_fhss_types_str.h @@ -0,0 +1,49 @@ +/*! + * @file lr11xx_lr_fhss_types_str.h + * + * @brief Printer helper functions for LR11xx LRFHSS types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_LR_FHSS_TYPES_STR_H +#define LR11XX_LR_FHSS_TYPES_STR_H +#include "lr11xx_driver/lr11xx_lr_fhss_types.h" +#ifdef __cplusplus +extern "C" { +#endif +const char* lr_fhss_v1_modulation_type_to_str( const lr_fhss_v1_modulation_type_t value ); +const char* lr_fhss_v1_cr_to_str( const lr_fhss_v1_cr_t value ); +const char* lr_fhss_v1_grid_to_str( const lr_fhss_v1_grid_t value ); +const char* lr_fhss_v1_bw_to_str( const lr_fhss_v1_bw_t value ); +#ifdef __cplusplus +} +#endif +#endif // LR11XX_LR_FHSS_TYPES_STR_H diff --git a/components/esp_lora_1121/include/lr1121_printers/lr11xx_printf_info.h b/components/esp_lora_1121/include/lr1121_printers/lr11xx_printf_info.h new file mode 100755 index 0000000..d850052 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_printers/lr11xx_printf_info.h @@ -0,0 +1,14 @@ +#ifndef LR11XX_VER_TEMP_H +#define LR11XX_VER_TEMP_H +#include "lr11xx_driver/lr11xx_system.h" +#ifdef __cplusplus +extern "C" { +#endif + +void lora_print_version( const void* context ); +void lora_print_temp( const void* context ); +void lora_print_vbat( const void* context ); +#ifdef __cplusplus +} +#endif +#endif // LR11XX_VER_TEMP_H \ No newline at end of file diff --git a/components/esp_lora_1121/include/lr1121_printers/lr11xx_radio_types_str.h b/components/esp_lora_1121/include/lr1121_printers/lr11xx_radio_types_str.h new file mode 100755 index 0000000..7780d97 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_printers/lr11xx_radio_types_str.h @@ -0,0 +1,70 @@ +/*! + * @file lr11xx_radio_types_str.h + * + * @brief Printer helper functions for LR11xx radio types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_RADIO_TYPES_STR_H +#define LR11XX_RADIO_TYPES_STR_H +#include "lr11xx_driver/lr11xx_radio_types.h" +#ifdef __cplusplus +extern "C" { +#endif +const char* lr11xx_radio_pa_selection_to_str( const lr11xx_radio_pa_selection_t value ); +const char* lr11xx_radio_gfsk_address_filtering_to_str( const lr11xx_radio_gfsk_address_filtering_t value ); +const char* lr11xx_radio_fallback_modes_to_str( const lr11xx_radio_fallback_modes_t value ); +const char* lr11xx_radio_ramp_time_to_str( const lr11xx_radio_ramp_time_t value ); +const char* lr11xx_radio_lora_network_type_to_str( const lr11xx_radio_lora_network_type_t value ); +const char* lr11xx_radio_lora_sf_to_str( const lr11xx_radio_lora_sf_t value ); +const char* lr11xx_radio_lora_bw_to_str( const lr11xx_radio_lora_bw_t value ); +const char* lr11xx_radio_lora_cr_to_str( const lr11xx_radio_lora_cr_t value ); +const char* lr11xx_radio_intermediary_mode_to_str( const lr11xx_radio_intermediary_mode_t value ); +const char* lr11xx_radio_gfsk_crc_type_to_str( const lr11xx_radio_gfsk_crc_type_t value ); +const char* lr11xx_radio_gfsk_dc_free_to_str( const lr11xx_radio_gfsk_dc_free_t value ); +const char* lr11xx_radio_gfsk_pkt_len_modes_to_str( const lr11xx_radio_gfsk_pkt_len_modes_t value ); +const char* lr11xx_radio_gfsk_preamble_detector_to_str( const lr11xx_radio_gfsk_preamble_detector_t value ); +const char* lr11xx_radio_lora_crc_to_str( const lr11xx_radio_lora_crc_t value ); +const char* lr11xx_radio_lora_pkt_len_modes_to_str( const lr11xx_radio_lora_pkt_len_modes_t value ); +const char* lr11xx_radio_lora_iq_to_str( const lr11xx_radio_lora_iq_t value ); +const char* lr11xx_radio_pkt_type_to_str( const lr11xx_radio_pkt_type_t value ); +const char* lr11xx_radio_pa_reg_supply_to_str( const lr11xx_radio_pa_reg_supply_t value ); +const char* lr11xx_radio_rx_duty_cycle_mode_to_str( const lr11xx_radio_rx_duty_cycle_mode_t value ); +const char* lr11xx_radio_gfsk_bw_to_str( const lr11xx_radio_gfsk_bw_t value ); +const char* lr11xx_radio_cad_exit_mode_to_str( const lr11xx_radio_cad_exit_mode_t value ); +const char* lr11xx_radio_gfsk_pulse_shape_to_str( const lr11xx_radio_gfsk_pulse_shape_t value ); +const char* lr11xx_radio_bpsk_pulse_shape_to_str( const lr11xx_radio_bpsk_pulse_shape_t value ); +const char* lr11xx_radio_lr_fhss_bitrate_to_str( const lr11xx_radio_lr_fhss_bitrate_t value ); +const char* lr11xx_radio_lr_fhss_pulse_shape_to_str( const lr11xx_radio_lr_fhss_pulse_shape_t value ); +#ifdef __cplusplus +} +#endif +#endif // LR11XX_RADIO_TYPES_STR_H diff --git a/components/esp_lora_1121/include/lr1121_printers/lr11xx_rttof_types_str.h b/components/esp_lora_1121/include/lr1121_printers/lr11xx_rttof_types_str.h new file mode 100755 index 0000000..debc97c --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_printers/lr11xx_rttof_types_str.h @@ -0,0 +1,46 @@ +/*! + * @file lr11xx_rttof_types_str.h + * + * @brief Printer helper functions for LR11xx RTToF types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_RTTOF_TYPES_STR_H +#define LR11XX_RTTOF_TYPES_STR_H +#include "lr11xx_driver/lr11xx_rttof_types.h" +#ifdef __cplusplus +extern "C" { +#endif +const char* lr11xx_rttof_result_type_to_str( const lr11xx_rttof_result_type_t value ); +#ifdef __cplusplus +} +#endif +#endif // LR11XX_RTTOF_TYPES_STR_H diff --git a/components/esp_lora_1121/include/lr1121_printers/lr11xx_system_types_str.h b/components/esp_lora_1121/include/lr1121_printers/lr11xx_system_types_str.h new file mode 100755 index 0000000..bc23f5d --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_printers/lr11xx_system_types_str.h @@ -0,0 +1,54 @@ +/*! + * @file lr11xx_system_types_str.h + * + * @brief Printer helper functions for LR11xx system types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_SYSTEM_TYPES_STR_H +#define LR11XX_SYSTEM_TYPES_STR_H +#include "lr11xx_driver/lr11xx_system_types.h" +#ifdef __cplusplus +extern "C" { +#endif +const char* lr11xx_system_chip_modes_to_str( const lr11xx_system_chip_modes_t value ); +const char* lr11xx_system_reset_status_to_str( const lr11xx_system_reset_status_t value ); +const char* lr11xx_system_command_status_to_str( const lr11xx_system_command_status_t value ); +const char* lr11xx_system_lfclk_cfg_to_str( const lr11xx_system_lfclk_cfg_t value ); +const char* lr11xx_system_reg_mode_to_str( const lr11xx_system_reg_mode_t value ); +const char* lr11xx_system_infopage_id_to_str( const lr11xx_system_infopage_id_t value ); +const char* lr11xx_system_standby_cfg_to_str( const lr11xx_system_standby_cfg_t value ); +const char* lr11xx_system_tcxo_supply_voltage_to_str( const lr11xx_system_tcxo_supply_voltage_t value ); +const char* lr11xx_system_version_type_to_str( const lr11xx_system_version_type_t value ); +#ifdef __cplusplus +} +#endif +#endif // LR11XX_SYSTEM_TYPES_STR_H diff --git a/components/esp_lora_1121/include/lr1121_printers/lr11xx_types_str.h b/components/esp_lora_1121/include/lr1121_printers/lr11xx_types_str.h new file mode 100755 index 0000000..fa25d27 --- /dev/null +++ b/components/esp_lora_1121/include/lr1121_printers/lr11xx_types_str.h @@ -0,0 +1,46 @@ +/*! + * @file lr11xx_types_str.h + * + * @brief Printer helper functions for LR11xx types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_TYPES_STR_H +#define LR11XX_TYPES_STR_H +#include "lr11xx_driver/lr11xx_types.h" +#ifdef __cplusplus +extern "C" { +#endif +const char* lr11xx_status_to_str( const lr11xx_status_t value ); +#ifdef __cplusplus +} +#endif +#endif // LR11XX_TYPES_STR_H diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_bootloader.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_bootloader.h new file mode 100755 index 0000000..d0e695a --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_bootloader.h @@ -0,0 +1,213 @@ +/*! + * @file lr11xx_bootloader.h + * + * @brief Bootloader driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_BOOTLOADER_H +#define LR11XX_BOOTLOADER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_bootloader_types.h" +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +typedef uint32_t lr11xx_bootloader_irq_mask_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Return the status registers and interrupt flags + * + * @remark To simplify system integration, this function does not actually execute the GetStatus command, which would + * require bidirectional SPI communication. It obtains the stat1, stat2, and irq_status values by performing an ordinary + * SPI read (which is required to send null/NOP bytes on the MOSI line). This is possible since the LR11XX returns these + * values automatically whenever a read that does not directly follow a response-carrying command is performed. Unlike + * with the GetStatus command, however, the reset status information is NOT cleared by this command. The function @ref + * lr11xx_bootloader_clear_reset_status_info may be used for this purpose when necessary. + * + * @param [in] context Chip implementation context + * @param [out] stat1 Content of status register 1 + * @param [out] stat2 Content of status register 2 + * @param [out] irq_status Interrupt flags + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_get_status( const void* context, lr11xx_bootloader_stat1_t* stat1, + lr11xx_bootloader_stat2_t* stat2, + lr11xx_bootloader_irq_mask_t* irq_status ); + +/*! + * @brief Clear the reset status information stored in stat2 + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_clear_reset_status_info( const void* context ); + +/*! + * @brief Return the version of the system (hardware and software) + * + * @param [in] context Chip implementation context + * @param [out] version Pointer to the structure holding the system version + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_get_version( const void* context, lr11xx_bootloader_version_t* version ); + +/*! + * @brief Erase the whole flash memory of the chip + * + * This function shall be called before any attempt to write a new firmware in flash memory + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_erase_flash( const void* context ); + +/*! + * @brief Write encrypted data in program flash memory of the chip + * + * This function shall be used when updating the encrypted flash content of the LR11XX. + * The encrypted flash payload to transfer shall be represented as an array of words (i.e. 4-byte values). + * + * Updating flash code of the chip with this function MUST respect the following constraints: + * - the complete flash image MUST be splitted into chunks of 64 words each, except the last one that can be shorter + * - the chunks MUST be sent to the chip in-order, starting with @p offset_in_byte = 0 + * + * @param [in] context Chip implementation context + * @param [in] offset_in_byte The offset from start register of flash in byte + * @param [in] buffer Buffer holding the encrypted content. Its size in words must be at least length + * @param [in] length_in_word Number of words (i.e. 4 bytes) in the buffer to transfer. MUST be 64 for all chunks except + * the last one where it can be lower. + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_write_flash_encrypted( const void* context, const uint32_t offset_in_byte, + const uint32_t* buffer, const uint8_t length_in_word ); + +/*! + * @brief Write encrypted data in program flash memory of the chip + * + * This function shall be used when updating the encrypted flash content of the LR11XX. + * The encrypted flash payload to transfer shall be represented as an array of words (ie 4-byte values). + * + * @param [in] context Chip implementation context + * @param [in] offset_in_byte The offset from start register of flash in byte + * @param [in] buffer Buffer holding the encrypted content. Its size in words must be at least length + * @param [in] length_in_word Number of words (i.e. 4 bytes) in the buffer to transfer + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_write_flash_encrypted_full( const void* context, const uint32_t offset_in_byte, + const uint32_t* buffer, const uint32_t length_in_word ); + +/*! + * @brief Software reset of the chip. + * + * This method should be used to reboot the chip in a specified mode. + * Rebooting in flash mode presumes that the content in flash memory is not corrupted (i.e. the integrity check + * performed by the bootloader before executing the first instruction in flash is OK). + * + * @param [in] context Chip implementation context + * @param [in] stay_in_bootloader Selector to stay in bootloader or execute flash code after reboot. If true, the + * bootloader will not execute the flash code but activate SPI interface to allow firmware upgrade + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_reboot( const void* context, const bool stay_in_bootloader ); + +/*! + * @brief Returns the 4-byte PIN which can be used to claim a device on cloud services. + * + * @param [in] context Chip implementation context + * @param [out] pin Pointer to the array to be populated with the PIN + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_read_pin( const void* context, lr11xx_bootloader_pin_t pin ); + +/*! + * @brief Read and return the Chip EUI + * + * @param [in] context Chip implementation context + * @param [out] chip_eui The buffer to be filled with chip EUI of the LR11XX. It is up to the application to ensure + * chip_eui is long enough to hold the chip EUI + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_read_chip_eui( const void* context, lr11xx_bootloader_chip_eui_t chip_eui ); + +/*! + * @brief Read and return the Join EUI + * + * @param [in] context Chip implementation context + * @param [out] join_eui The buffer to be filled with Join EUI of the LR11XX. It is up to the application to ensure + * join_eui is long enough to hold the join EUI + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_bootloader_read_join_eui( const void* context, lr11xx_bootloader_join_eui_t join_eui ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_BOOTLOADER_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_bootloader_types.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_bootloader_types.h new file mode 100755 index 0000000..257d019 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_bootloader_types.h @@ -0,0 +1,179 @@ +/*! + * @file lr11xx_bootloader_types.h + * + * @brief Bootloader driver types for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_BOOTLOADER_TYPES_H +#define LR11XX_BOOTLOADER_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/*! + * @brief Length in byte of the LR11XX version blob + */ +#define LR11XX_BL_VERSION_LENGTH ( 4 ) + +/*! + * @brief Length in bytes of a PIN + */ +#define LR11XX_BL_PIN_LENGTH ( 4 ) + +/*! + * @brief Length in bytes of a chip EUI + */ +#define LR11XX_BL_CHIP_EUI_LENGTH ( 8 ) + +/*! + * @brief Length in bytes of a join EUI + */ +#define LR11XX_BL_JOIN_EUI_LENGTH ( 8 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Fixed-length array to store a PIN + */ +typedef uint8_t lr11xx_bootloader_pin_t[LR11XX_BL_PIN_LENGTH]; + +/*! + * @brief Fixed-length array to store a chipEUI + */ +typedef uint8_t lr11xx_bootloader_chip_eui_t[LR11XX_BL_CHIP_EUI_LENGTH]; + +/*! + * @brief Fixed-length array to store a joinEUI + */ +typedef uint8_t lr11xx_bootloader_join_eui_t[LR11XX_BL_JOIN_EUI_LENGTH]; + +/*! + * @brief Chip modes + */ +typedef enum lr11xx_bootloader_chip_modes_e +{ + LR11XX_BOOTLOADER_CHIP_MODE_SLEEP = 0x00, + LR11XX_BOOTLOADER_CHIP_MODE_STBY_RC = 0x01, + LR11XX_BOOTLOADER_CHIP_MODE_STBY_XOSC = 0x02, + LR11XX_BOOTLOADER_CHIP_MODE_FS = 0x03, + LR11XX_BOOTLOADER_CHIP_MODE_RX = 0x04, + LR11XX_BOOTLOADER_CHIP_MODE_TX = 0x05, + LR11XX_BOOTLOADER_CHIP_MODE_LOC = 0x06, +} lr11xx_bootloader_chip_modes_t; + +/*! + * @brief Reset status + */ +typedef enum lr11xx_bootloader_reset_status_e +{ + LR11XX_BOOTLOADER_RESET_STATUS_CLEARED = 0x00, + LR11XX_BOOTLOADER_RESET_STATUS_ANALOG = 0x01, + LR11XX_BOOTLOADER_RESET_STATUS_EXTERNAL = 0x02, + LR11XX_BOOTLOADER_RESET_STATUS_SYSTEM = 0x03, + LR11XX_BOOTLOADER_RESET_STATUS_WATCHDOG = 0x04, + LR11XX_BOOTLOADER_RESET_STATUS_IOCD_RESTART = 0x05, + LR11XX_BOOTLOADER_RESET_STATUS_RTC_RESTART = 0x06, +} lr11xx_bootloader_reset_status_t; + +/*! + * @brief Command status + */ +typedef enum lr11xx_bootloader_command_status_e +{ + LR11XX_BOOTLOADER_CMD_STATUS_FAIL = 0x00, + LR11XX_BOOTLOADER_CMD_STATUS_PERR = 0x01, + LR11XX_BOOTLOADER_CMD_STATUS_OK = 0x02, + LR11XX_BOOTLOADER_CMD_STATUS_DATA = 0x03, +} lr11xx_bootloader_command_status_t; + +/*! + * @brief Status register 1 structure definition + */ +typedef struct lr11xx_bootloader_stat1_s +{ + lr11xx_bootloader_command_status_t command_status; + bool is_interrupt_active; +} lr11xx_bootloader_stat1_t; + +/*! + * @brief Status register 2 structure definition + */ +typedef struct lr11xx_bootloader_stat2_s +{ + lr11xx_bootloader_reset_status_t reset_status; + lr11xx_bootloader_chip_modes_t chip_mode; + bool is_running_from_flash; +} lr11xx_bootloader_stat2_t; + +/*! + * @brief Bootloader version structure definition + */ +typedef struct lr11xx_bootloader_version_s +{ + uint8_t hw; + uint8_t type; + uint16_t fw; +} lr11xx_bootloader_version_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_BOOTLOADER_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_crypto_engine.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_crypto_engine.h new file mode 100755 index 0000000..759e737 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_crypto_engine.h @@ -0,0 +1,342 @@ +/*! + * @file lr11xx_crypto_engine.h + * + * @brief Cryptographic engine driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_CRYPTO_ENGINE_H +#define LR11XX_CRYPTO_ENGINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr11xx_crypto_engine_types.h" +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Select the crypto element to be used + * + * By default, the internal crypto engine is selected. It is not needed to call this command if one plans to use the + * internal crypto engine. + * + * @param [in] context Chip implementation context + * @param [in] element The type of crypto element to use + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_select( const void* context, const lr11xx_crypto_element_t element ); + +/*! + * @brief Set a key in the previously selected crypto element. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] key_id The identifier of the key to be set + * @param [in] key The key to be set + * + * @returns Operation status + * + * @see lr11xx_crypto_derive_key + */ +lr11xx_status_t lr11xx_crypto_set_key( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const lr11xx_crypto_key_t key ); + +/*! + * @brief Derive a key previously set. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] src_key_id The identifier of the key to be derived + * @param [in] dest_key_id The identifier where the derived key will be stored after call to @ref + * lr11xx_crypto_store_to_flash + * @param [in] nonce The nonce to be used to perform the derivation + * + * @returns Operation status + * + * @see lr11xx_crypto_set_key + */ +lr11xx_status_t lr11xx_crypto_derive_key( const void* context, lr11xx_crypto_status_t* status, const uint8_t src_key_id, + const uint8_t dest_key_id, const lr11xx_crypto_nonce_t nonce ); + +/*! + * @brief Perform the needed operations to extract the payload from a join accept message. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] dec_key_id The identifier of the key used for message decryption + * @param [in] ver_key_id The identifier of the key used for MIC verification + * @param [in] lorawan_version LoRaWAN version to know the size of the header + * @param [in] header The header to compute (length linked to lorawan_version) + * @param [in] data The data to compute + * @param [in] length The length in bytes of the data to compute + * @param [out] data_out Placeholder for the decrypted data + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_process_join_accept( const void* context, lr11xx_crypto_status_t* status, + const uint8_t dec_key_id, const uint8_t ver_key_id, + const lr11xx_crypto_lorawan_version_t lorawan_version, + const uint8_t* header, const uint8_t* data, const uint8_t length, + uint8_t* data_out ); + +/*! + * @brief Compute an AES-CMAC. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] key_id The identifier of the keyused for the computation + * @param [in] data The data to compute + * @param [in] length The length in bytes of the data to compute + * @param [out] mic Placeholder for the computed MIC (first 4 bytes of the AES-CMAC) + * + * @returns Operation status + * + * @see lr11xx_crypto_verify_aes_cmac + */ +lr11xx_status_t lr11xx_crypto_compute_aes_cmac( const void* context, lr11xx_crypto_status_t* status, + const uint8_t key_id, const uint8_t* data, const uint16_t length, + lr11xx_crypto_mic_t mic ); + +/*! + * @brief Compute an AES-CMAC and make a comparison with a value given as parameter. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] key_id The identifier of the key to be used for the computation + * @param [in] data The data to compute + * @param [in] length The length in bytes of the data to compute + * @param [in] mic The MIC value (first 4 bytes of the CMAC) use for comparison + * + * @returns Operation status + * + * @see lr11xx_crypto_compute_aes_cmac + */ +lr11xx_status_t lr11xx_crypto_verify_aes_cmac( const void* context, lr11xx_crypto_status_t* status, + const uint8_t key_id, const uint8_t* data, const uint16_t length, + const lr11xx_crypto_mic_t mic ); + +/*! + * @brief Compute an AES encryption with a key ID specified in parameter. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] key_id The identifier of the key to be used for the computation + * @param [in] data The data to encrypt + * @param [in] length The length in bytes of the data to encrypt - this value shall be a multiple of 16 + * @param [out] result A pointer to a data buffer that will be filled with the encrypted data. Values of this buffer are + * meaningful if and only if the return status is LR11XX_CRYPTO_STATUS_SUCCESS + * + * @returns Operation status + * + * @see lr11xx_crypto_set_key, lr11xx_crypto_derive_key + */ +lr11xx_status_t lr11xx_crypto_aes_encrypt_01( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const uint8_t* data, const uint16_t length, uint8_t* result ); + +/*! + * @brief Compute an AES encryption with a key ID specified in parameter. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] key_id The identifier of the key to be used for the computation + * @param [in] data The data to encrypt + * @param [in] length The length in bytes of the data to encrypt - this value shall be a multiple of 16 + * @param [out] result A pointer to a data buffer that will be filled with the encrypted data. Values of this buffer are + * meaningful if and only if the return status is LR11XX_CRYPTO_STATUS_SUCCESS + * + * @returns Operation status + * + * @see lr11xx_crypto_set_key, lr11xx_crypto_derive_key + */ +lr11xx_status_t lr11xx_crypto_aes_encrypt( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const uint8_t* data, const uint16_t length, uint8_t* result ); + +/*! + * @brief Compute an AES decryption with a key ID specified in parameter. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] key_id The identifier of the key to be used for the computation + * @param [in] data The data to decrypt + * @param [in] length The length in bytes of the data to decrypt - this value shall be a multiple of 16 + * @param [out] result A pointer to a data buffer that will be filled with the decrypted data. Values of this buffer are + * meaningful if and only if the return status is LR11XX_CRYPTO_STATUS_SUCCESS + * + * @returns Operation status + * + * @see lr11xx_crypto_set_key, lr11xx_crypto_derive_key + */ +lr11xx_status_t lr11xx_crypto_aes_decrypt( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const uint8_t* data, const uint16_t length, uint8_t* result ); + +/*! + * @brief Store the crypto data (keys, parameters) from RAM into the flash memory. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * + * @returns Operation status + * + * @see lr11xx_crypto_restore_from_flash + */ +lr11xx_status_t lr11xx_crypto_store_to_flash( const void* context, lr11xx_crypto_status_t* status ); + +/*! + * @brief Restore the crypto data (keys, parameters) from flash memory into RAM. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * + * @returns Operation status + * + * @see lr11xx_crypto_store_to_flash + */ +lr11xx_status_t lr11xx_crypto_restore_from_flash( const void* context, lr11xx_crypto_status_t* status ); + +/*! + * @brief Set a specific parameter identified by param_id in the crypto RAM. + * + * This function does not store a parameter in the flash memory. The parameters shall be stored after using @ref + * lr11xx_crypto_store_to_flash command. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] param_id The identifier of the parameter to be set + * @param [in] parameter The parameter to be set + * + * @returns Operation status + * + * @see lr11xx_crypto_get_parameter + */ +lr11xx_status_t lr11xx_crypto_set_parameter( const void* context, lr11xx_crypto_status_t* status, + const uint8_t param_id, const lr11xx_crypto_param_t parameter ); + +/*! + * @brief Get a specific parameter identified by paramID from the crypto RAM. + * + * This function does not fetch a parameter from the flash memory. The parameters shall be restored before using @ref + * lr11xx_crypto_restore_from_flash command. + * + * @param [in] context Chip implementation context + * @param [out] status The status returned by the execution of this cryptographic function + * @param [in] param_id The identifier of the parameter to get + * @param [out] parameter The placeholder to store the parameter + * + * @returns Operation status + * + * @see lr11xx_crypto_set_parameter + */ +lr11xx_status_t lr11xx_crypto_get_parameter( const void* context, lr11xx_crypto_status_t* status, + const uint8_t param_id, lr11xx_crypto_param_t parameter ); + +/*! + * @brief Check if an encrypted firmware image is suitable for the transceiver on which the check is done + * + * @remark The result can be read by calling @ref lr11xx_crypto_get_check_encrypted_firmware_image_result + * + * @remark A user checks the suitability of a firmware image by calling this function with 64-word long chunk of data + * sent in-order (except for the last one that can be shorter). + * + * @param [in] context Chip implementation context + * @param [in] offset_in_byte Offset of data buffer in firmware image - has to be a multiple of 4 + * @param [in] data Buffer holding the encrypted content. Its size in words must be at least length + * @param [in] length_in_word Number of words (i.e. 4 bytes) in the buffer to transfer. This value must be in the range + * [0:64] + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_check_encrypted_firmware_image( const void* context, const uint32_t offset_in_byte, + const uint32_t* data, const uint8_t length_in_word ); + +/*! + * @brief Check if an encrypted firmware image is suitable for the transceiver on which the check is done + * + * @remark The result can be read by calling @ref lr11xx_crypto_get_check_encrypted_firmware_image_result + * + * @remark This function is developed on top of @ref lr11xx_crypto_check_encrypted_firmware_image and takes care of the + * whole firmware image transfer + * + * @param [in] context Chip implementation context + * @param [in] offset_in_byte Offset of data buffer in firmware image - has to be a multiple of 4 + * @param [in] data Buffer holding the encrypted content. Its size in words must be at least length + * @param [in] length_in_word Number of words (i.e. 4 bytes) in the buffer to transfer + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_check_encrypted_firmware_image_full( const void* context, const uint32_t offset_in_byte, + const uint32_t* data, + const uint32_t length_in_word ); + +/*! + * @brief Get the result of the encrypted firmware image check + * + * @param [in] context Chip implementation context + * @param [out] is_encrypted_fw_image_ok Result of the encrypted firmware image check + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_crypto_get_check_encrypted_firmware_image_result( const void* context, + bool* is_encrypted_fw_image_ok ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_CRYPTO_ENGINE_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_crypto_engine_types.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_crypto_engine_types.h new file mode 100755 index 0000000..50e2b88 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_crypto_engine_types.h @@ -0,0 +1,199 @@ +/*! + * @file lr11xx_crypto_engine_types.h + * + * @brief Cryptographic engine driver types for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_CRYPTO_ENGINE_TYPES_H +#define LR11XX_CRYPTO_ENGINE_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/*! + * @brief Length in bytes of a MIC + */ +#define LR11XX_CRYPTO_MIC_LENGTH 0x04 + +/*! + * @brief Length in bytes of a AES CMAC + */ +#define LR11XX_CRYPTO_AES_CMAC_LENGTH 0x10 + +/*! + * @brief Maximum length in bytes of data to be encrypted / decrypted + */ +#define LR11XX_CRYPTO_DATA_MAX_LENGTH 0x0100 + +/*! + * @brief Length in bytes of a key for AES computation + */ +#define LR11XX_CRYPTO_KEY_LENGTH 0x10 + +/*! + * @brief Length in bytes of a nonce + */ +#define LR11XX_CRYPTO_NONCE_LENGTH 0x10 + +/*! + * @brief Length in bytes of a crypto parameter + */ +#define LR11XX_CRYPTO_PARAMETER_LENGTH 0x04 + +/*! + * @brief Length in bytes of the status returned by an API + */ +#define LR11XX_CRYPTO_STATUS_LENGTH 0x01 + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Fixed-length array to store an AES CMAC + */ +typedef uint8_t lr11xx_crypto_mic_t[LR11XX_CRYPTO_MIC_LENGTH]; + +/*! + * @brief Fixed-length array to store an AES CMAC + */ +typedef uint8_t lr11xx_crypto_aes_cmac_t[LR11XX_CRYPTO_AES_CMAC_LENGTH]; + +/*! + * @brief Fixed-length array to store a crypto key + */ +typedef uint8_t lr11xx_crypto_key_t[LR11XX_CRYPTO_KEY_LENGTH]; + +/*! + * @brief Fixed-length array to store a crypto nonce + */ +typedef uint8_t lr11xx_crypto_nonce_t[LR11XX_CRYPTO_NONCE_LENGTH]; + +/*! + * @brief Fixed-length array to store a crypto parameter + */ +typedef uint8_t lr11xx_crypto_param_t[LR11XX_CRYPTO_PARAMETER_LENGTH]; + +/*! + * @brief The supported crypto elements + */ +typedef enum +{ + LR11XX_CRYPTO_ELEMENT_CRYPTO_ENGINE = 0x00, //!< Internal crypto engine (default) + LR11XX_CRYPTO_ELEMENT_SECURE_ELEMENT = 0x01, //!< External secure element +} lr11xx_crypto_element_t; + +/*! + * @brief The status returned by the crypto API + */ +typedef enum +{ + LR11XX_CRYPTO_STATUS_SUCCESS = 0x00, //!< The API command was successful + LR11XX_CRYPTO_STATUS_ERROR_FAIL_CMAC = 0x01, //!< AES-CMAC invalid or comparison failed + LR11XX_CRYPTO_STATUS_ERROR_INVALID_KEY_ID = 0x03, //!< Invalid key ID (source, destination) + LR11XX_CRYPTO_STATUS_ERROR_BUFFER_SIZE = 0x05, //!< Invalid data buffer size + LR11XX_CRYPTO_STATUS_ERROR = 0x06, //!< Other error +} lr11xx_crypto_status_t; + +/*! + * @brief The supported LoRaWAN versions + */ +typedef enum +{ + LR11XX_CRYPTO_LORAWAN_VERSION_1_0_X = 0x00, + LR11XX_CRYPTO_LORAWAN_VERSION_1_1_X = 0x01, +} lr11xx_crypto_lorawan_version_t; + +/*! + * @brief Crypto keys table index definition. + */ +typedef enum lr11xx_crypto_keys_idx_e +{ + LR11XX_CRYPTO_KEYS_IDX_MOTHER_KEY = 1, + LR11XX_CRYPTO_KEYS_IDX_NWK_KEY = 2, + LR11XX_CRYPTO_KEYS_IDX_APP_KEY = 3, + LR11XX_CRYPTO_KEYS_IDX_J_S_ENC_KEY = 4, + LR11XX_CRYPTO_KEYS_IDX_J_S_INT_KEY = 5, + LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_0 = 6, + LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_1 = 7, + LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_2 = 8, + LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_3 = 9, + LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_4 = 10, + LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_5 = 11, + LR11XX_CRYPTO_KEYS_IDX_APP_S_KEY = 12, + LR11XX_CRYPTO_KEYS_IDX_F_NWK_S_INT_KEY = 13, + LR11XX_CRYPTO_KEYS_IDX_S_NWK_S_INT_KEY = 14, + LR11XX_CRYPTO_KEYS_IDX_NWK_S_ENC_KEY = 15, + LR11XX_CRYPTO_KEYS_IDX_RFU_0 = 16, + LR11XX_CRYPTO_KEYS_IDX_RFU_1 = 17, + LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_0 = 18, + LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_1 = 19, + LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_2 = 20, + LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_3 = 21, + LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_0 = 22, + LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_1 = 23, + LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_2 = 24, + LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_3 = 25, + LR11XX_CRYPTO_KEYS_IDX_GP0 = 26, + LR11XX_CRYPTO_KEYS_IDX_GP1 = 27, +} lr11xx_crypto_keys_idx_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_CRYPTO_ENGINE_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_driver_version.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_driver_version.h new file mode 100755 index 0000000..0411fa1 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_driver_version.h @@ -0,0 +1,90 @@ +/*! + * @file lr11xx_driver_version.h + * + * @brief Placeholder to keep the version of LR11XX driver. + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_DRIVER_VERSION_H +#define LR11XX_DRIVER_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +#define LR11XX_DRIVER_VERSION_MAJOR 2 +#define LR11XX_DRIVER_VERSION_MINOR 4 +#define LR11XX_DRIVER_VERSION_PATCH 1 + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Compare version information with current ones + * + * This macro expands to true boolean value if the version information provided in argument is compatible or + * retro-compatible with the version of this code base + */ +#define LR11XX_DRIVER_VERSION_CHECK( x, y, z ) \ + ( x == LR11XX_DRIVER_VERSION_MAJOR && \ + ( y < LR11XX_DRIVER_VERSION_MINOR || \ + ( y == LR11XX_DRIVER_VERSION_MINOR && z <= LR11XX_DRIVER_VERSION_PATCH ) ) ) + +const char* lr11xx_driver_version_get_version_string( void ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_DRIVER_VERSION_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_gnss.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_gnss.h new file mode 100755 index 0000000..b591ea5 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_gnss.h @@ -0,0 +1,757 @@ +/*! + * @file lr11xx_gnss.h + * + * @brief GNSS scan driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_GNSS_H +#define LR11XX_GNSS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_gnss_types.h" +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Get the size of results + * + * This method returns the size in bytes of the results available in LR11XX result buffer. + * + * @param [in] context Chip implementation context + * @param [out] result_size Result size + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_result_size( const void* context, uint16_t* result_size ); + +/*! + * @brief Read GNSS results + * + * The GNSS results are pushed into a buffer directly. This buffer is provided by the application using the driver. It + * MUST be long enough to contains at least result_buffer_size bytes. + * + * @warning No check is done on result_buffer size. If this application provided buffer is too small, there will be a + * buffer overflow bug! + * + * @param [in] context Chip implementation context + * @param [out] result_buffer Application provided buffer to be filled with result + * @param [in] result_buffer_size The number of bytes to read from the LR11XX + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_results( const void* context, uint8_t* result_buffer, + const uint16_t result_buffer_size ); + +/*! + * @brief Update almanacs given as parameter + * + * @remark Note that information header and almanacs for all 128 SV (i.e. 129 20-byte long blocks) must be updated in a + * row for the whole operation to be successful. Therefore, this function must be called as many times as needed without + * any other operations in between. + * + * @param [in] context Chip implementation context + * @param [in] blocks Buffer containing at least (nb_of_blocks * LR11XX_GNSS_SINGLE_ALMANAC_WRITE_SIZE) bytes of almanac + * @param [in] nb_of_blocks Number of blocks to transfer + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_almanac_update( const void* context, const uint8_t* blocks, const uint8_t nb_of_blocks ); + +/*! + * @brief Read the almanac + * + * @param [in] context Chip implementation context + * @param [out] almanac_bytestream The bytestream of the almanac + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_almanac( const void* context, + lr11xx_gnss_almanac_full_read_bytestream_t almanac_bytestream ); + +/*! + * @brief Function to read the frequency search space around the Doppler frequency + * + * @param [in] radio Radio abstraction + * @param [out] freq_search_space Frequency search space configuration read from the chip + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_freq_search_space( const void* radio, + lr11xx_gnss_freq_search_space_t* freq_search_space ); + +/*! + * @brief Function to set the frequency search space around the Doppler frequency + * + * @param [in] radio Radio abstraction + * @param [in] freq_search_space Frequency search space configuration to be applied + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_set_freq_search_space( const void* radio, + const lr11xx_gnss_freq_search_space_t freq_search_space ); + +/*! + * @brief Get almanac age for a satellite + * + * @param [in] context Chip implementation context + * @param [in] sv_id ID of the satellite corresponding the to almanac requested + * @param [out] almanac_age Almanac age in days since last GPS time overlap + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_almanac_age_for_satellite( const void* context, const lr11xx_gnss_satellite_id_t sv_id, + uint16_t* almanac_age ); + +/*! + * @brief Push data received from solver to LR11XX + * + * @param [in] context Chip implementation context + * @param [in] payload Payload received from solver + * @param [in] payload_size Size of the payload received from solver (in bytes) + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_push_solver_msg( const void* context, const uint8_t* payload, const uint16_t payload_size ); + +/** + * @brief Return the theoretical number of visible satellites based on the given parameters. + * + * @param [in] context Chip implementation context + * @param [in] date The actual date of scan. Its format is the number of seconds elapsed since January the 6th 1980 + * 00:00:00 with leap seconds included. + * @param [in] assistance_position, latitude 12 bits and longitude 12 bits + * @param [in] constellation Bit mask of the constellations to use. See @ref lr11xx_gnss_constellation_t for + * the possible values + * @param [out] nb_visible_sv theoretical number of visible satellites + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_nb_visible_satellites( + const void* context, const lr11xx_gnss_date_t date, + const lr11xx_gnss_solver_assistance_position_t* assistance_position, + const lr11xx_gnss_constellation_t constellation, uint8_t* nb_visible_sv ); + +/** + * @brief Return the theoretical doppler information of theoretical visible satellites, this function shall be called + * after lr11xx_gnss_get_nb_visible_satellites function. + * + * @param [in] context Chip implementation context + * @param [in] nb_visible_satellites number of visible satellites returned by lr11xx_gnss_get_nb_visible_satellites + * function, + * @param [out] visible_satellite_id_doppler Doppler information of each satellite. + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_visible_satellites( const void* context, const uint8_t nb_visible_satellites, + lr11xx_gnss_visible_satellite_t* visible_satellite_id_doppler ); + +/*! + * @brief Activate the GNSS scan constellation + * + * @param [in] context Chip implementation context + * @param [in] constellation_mask Bit mask of the constellations to use. See @ref lr11xx_gnss_constellation_t for + * the possible values + * + * @returns Operation status + * + * @see lr11xx_gnss_read_used_constellations + */ +lr11xx_status_t lr11xx_gnss_set_constellations_to_use( const void* context, + const lr11xx_gnss_constellation_mask_t constellation_mask ); + +/*! + * @brief Read constellation used by the GNSS scanner from the almanac update configuration + * + * @param [in] context Chip implementation context + * @param [out] constellations_used Bit mask of the constellations used. See @ref lr11xx_gnss_constellation_t for the + * possible values + * + * @returns Operation status + * + * @see lr11xx_gnss_set_constellations_to_use + */ +lr11xx_status_t lr11xx_gnss_read_used_constellations( const void* context, + lr11xx_gnss_constellation_mask_t* constellations_used ); + +/*! + * @brief Activate the almanac update + * + * @param [in] context Chip implementation context + * @param [in] constellations_to_update Bit mask of the constellations to mark to update. See @ref + * lr11xx_gnss_constellation_t for the possible values + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_set_almanac_update( const void* context, + const lr11xx_gnss_constellation_mask_t constellations_to_update ); + +/*! + * @brief Function to read the almanac update configuration + * + * @param [in] context Chip implementation context + * @param [out] constellations_to_update Bit mask of the constellations to mark to update. See @ref + * lr11xx_gnss_constellation_t for the possible values + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_almanac_update( const void* context, + lr11xx_gnss_constellation_mask_t* constellations_to_update ); + +/*! + * @brief Function to read the GNSS firmware version + * + * @param [in] context Chip implementation context + * @param [in] version GNSS Firmware version currently running on the chip + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_firmware_version( const void* context, lr11xx_gnss_version_t* version ); + +/*! + * @brief Function to read the supported constellation, GPS or BEIDOU other constellations + * + * @param [in] context Chip implementation context + * @param [out] supported_constellations Bit mask of the constellations used. See @ref lr11xx_gnss_constellation_t for + * the possible values + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_supported_constellations( const void* context, + lr11xx_gnss_constellation_mask_t* supported_constellations ); + +/*! + * @brief Function to set the GNSS scan mode configuration + * + * @param [in] context Chip implementation context + * @param [in] scan_mode GNSS scan mode + * + * @returns Operation status + * + * @see lr11xx_gnss_scan_mode_t + */ +lr11xx_status_t lr11xx_gnss_set_scan_mode( const void* context, const lr11xx_gnss_scan_mode_t scan_mode ); + +/*! + * @brief Start the gnss scan + * + * @param [in] context Chip implementation context + * @param [in] effort_mode Effort mode @ref lr11xx_gnss_search_mode_t + * @param [in] gnss_input_parameters Bit mask indicating which information is added in the output payload @ref + * lr11xx_gnss_result_fields_e + * @param [in] nb_sat The expected number of satellite to provide. This value must be in the range [0:128] + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_scan( const void* context, const lr11xx_gnss_search_mode_t effort_mode, + const uint8_t gnss_input_parameters, const uint8_t nb_sat ); + +/*! + * @brief Function to set the assistance position. + * + * @param [in] context Chip implementation context + * @param [in] assistance_position, latitude 12 bits and longitude 12 bits + * + * @returns Operation status + * + * @see lr11xx_gnss_solver_assistance_position_t + */ +lr11xx_status_t lr11xx_gnss_set_assistance_position( + const void* context, const lr11xx_gnss_solver_assistance_position_t* assistance_position ); + +/*! + * @brief Function to read the assistance position. + * + * The assistance position read may be different from the one set beforehand with @ref + * lr11xx_gnss_set_assistance_position due to a scaling computation. + * + * @param [in] context Chip implementation context + * @param [in] assistance_position, latitude 12 bits and longitude 12 bits + * + * @returns Operation status + * + * @see lr11xx_gnss_solver_assistance_position_t + */ +lr11xx_status_t lr11xx_gnss_read_assistance_position( const void* context, + lr11xx_gnss_solver_assistance_position_t* assistance_position ); + +/*! + * @brief Host receives an update from the network or assembles itself the update message and send it to the LR11XX. + * + * @param [in] context Chip implementation context + * @param [in] dmc_msg buffer containing the update the network + * @param [in] dmc_msg_len length of this buffer + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_push_dmc_msg( const void* context, uint8_t* dmc_msg, uint16_t dmc_msg_len ); + +/*! + * @brief Get the GNSS context status + * + * This function returns the GNSS context status as a raw buffer. It is possible to use + * lr11xx_gnss_parse_context_status_buffer to obtain the details of the context status. + * + * @param [in] context Chip implementation context + * @param [out] context_status_buffer Pointer to a buffer to be filled with context status information. Must be at least + * 7 bytes long. It is up to the caller to ensure there is enough place in this buffer. + * + * @returns Operation status + * + * @see lr11xx_gnss_parse_context_status_buffer + */ +lr11xx_status_t lr11xx_gnss_get_context_status( const void* context, + lr11xx_gnss_context_status_bytestream_t context_status_buffer ); + +/*! + * @brief Get the number of detected satellites during last scan + * + * @param [in] context Chip implementation context + * @param [out] nb_detected_satellites Number of satellites detected + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_nb_detected_satellites( const void* context, uint8_t* nb_detected_satellites ); + +/*! + * @brief Get the satellites detected on last scan with their IDs, C/N (aka CNR) and doppler + * + * @note Doppler is returned with 6ppm accuracy. + * + * @param [in] context Chip implementation context + * @param [in] nb_detected_satellites Number of detected satellites on last scan (obtained by calling + * lr11xx_gnss_get_nb_detected_satellites) + * @param [out] detected_satellite_id_snr_doppler Pointer to an array of structures of size big enough to contain + * nb_detected_satellites elements + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_detected_satellites( + const void* context, const uint8_t nb_detected_satellites, + lr11xx_gnss_detected_satellite_t* detected_satellite_id_snr_doppler ); + +/*! + * @brief Read almanacs per satellite range + * + * @note Doppler is returned with 6ppm accuracy. + * + * @param [in] context Chip implementation context + * @param [in] sv_id_init Index of the satellite to start reading almanac from + * @param [in] n_sv Number of satellite almanac to read from sv_id_init + * @param [out] almanacs Pointer to an array to be filled by almanac data. It is up to the caller to ensure the + * available length of almanacs buffer is at least (n_sv * LR11XX_GNSS_SINGLE_ALMANAC_READ_SIZE) + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_almanac_per_satellites( const void* context, uint8_t sv_id_init, uint8_t n_sv, + uint8_t* almanacs ); + +/*! + * @brief Read RSSI on GNSS path + * + * This is a test function to read RSSI on GNSS path. + * + * @param [in] context Chip implementation context + * @param [out] rssi_gnss_dbm RSSI read on GNSS path in dbm + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_gnss_rssi_test( const void* context, int8_t* rssi_gnss_dbm ); + +/** + * @brief Parse a raw buffer of context status + * + * @param [in] context_status_bytestream The raw buffer of context status to parse. It is up to the caller to ensure the + * buffer is at least LR11XX_GNSS_CONTEXT_STATUS_LENGTH bytes long + * @param [out] context_status Pointer to a structure of lr11xx_gnss_context_status_t to be filled with information from + * context_status_bytestream + * + * @returns Operation status + * + * @see lr11xx_gnss_get_context_status + */ +lr11xx_status_t lr11xx_gnss_parse_context_status_buffer( + const lr11xx_gnss_context_status_bytestream_t context_status_bytestream, + lr11xx_gnss_context_status_t* context_status ); + +/** + * @brief Extract the destination from the result returned by a GNSS scan + * + * @param [in] result_buffer Pointer to the buffer holding the result + * @param [in] result_buffer_size Size of the result in byte + * @param [out] destination Destination of the result + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_result_destination( const uint8_t* result_buffer, const uint16_t result_buffer_size, + lr11xx_gnss_destination_t* destination ); + +/** + * @brief Helper function that computes the age of an almanac. + * + * This function does not call the LR11XX. + * The almanac age is computed based on the following elements: + * - almanac age as obtained from lr11xx_gnss_get_almanac_age_for_satellite + * - the number of days elapsed between Epoch (January 6th 1980) and the GPS rollover reference of the current + * almanac + * - the GPS date of today expressed in number of days elapsed since Epoch + * + * @remark It is important to use for nb_days_between_epoch_and_corresponding_gps_time_rollover the GPS time rollover + * corresponding to the reference of the almanac_date. This is especially true when current date is just after a GPS + * time rollover. + * + * @param [in] almanac_date Almanac date as obtained from lr11xx_gnss_get_almanac_age_for_satellite + * @param [in] nb_days_between_epoch_and_corresponding_gps_time_rollover Number of days elapsed between GPS Epoch and + * the GPS rollover corresponding to the almanac_date + * @param [in] nb_days_since_epoch Number of days elapsed between January 6th 1980 and now + * + * @returns Age of the almanac expressed in number of days between its start valid instant and now + */ +uint16_t lr11xx_gnss_compute_almanac_age( uint16_t almanac_date, + uint16_t nb_days_between_epoch_and_corresponding_gps_time_rollover, + uint16_t nb_days_since_epoch ); + +/*! + * @brief Start the time acquisition/domulation. + * + * @param [in] context Chip implementation context + * @param [in] effort_mode Effort mode @ref lr11xx_gnss_search_mode_t, note that LR11XX_GNSS_OPTION_HIGH_EFFORT is not + * supported here + * @param [in] option Fetch time option @ref lr11xx_gnss_fetch_time_option_t + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_fetch_time( const void* context, const lr11xx_gnss_search_mode_t effort_mode, + const lr11xx_gnss_fetch_time_option_t option ); +/*! + * @brief Read time from LR11XX. + * + * @param [in] context Chip implementation context + * @param [out] time Structure containing the time \ref lr11xx_gnss_time_t + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_time( const void* context, lr11xx_gnss_time_t* time ); + +/*! + * @brief Reset the internal time. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_reset_time( const void* context ); + +/*! + * @brief Reset the location and the history Doppler buffer. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_reset_position( const void* context ); + +/*! + * @brief Read the week number rollover. + * + * @param [in] context Chip implementation context + * @param [out] wn_rollover_status Week number rollover status \ref lr11xx_gnss_week_number_rollover_status_t + * @param [out] wn_number_rollover Week number rollover since 1980 + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_week_number_rollover( const void* context, + lr11xx_gnss_week_number_rollover_status_t* wn_rollover_status, + uint8_t* wn_number_rollover ); + +/*! + * @brief Read demod status. + * + * @param [in] context Chip implementation context + * @param [out] demod_status Demodulation status \ref lr11xx_gnss_demod_status_t + * @param [out] demod_info Demodulation info \ref lr11xx_gnss_demod_info_t + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_demod_status( const void* context, lr11xx_gnss_demod_status_t* demod_status, + lr11xx_gnss_demod_info_t* demod_info ); + +/*! + * @brief Read cumulative timing. + * + * @param [in] context Chip implementation context + * @param [out] cumulative_timing Cumulative timing status \ref lr11xx_gnss_cumulative_timing_t, The value of time is in + * counter of 32KhZ, to have it in second, the counter must be divided by 32768 + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_cumulative_timing( const void* context, + lr11xx_gnss_cumulative_timing_t* cumulative_timing ); + +/*! + * @brief Compute power consumption based on timings and instantaneous power consumption. + * + * @param [in] cumulative_timing Timings read from lr11xx_gnss_read_cumulative_timing API \ref + * lr11xx_gnss_cumulative_timing_t + * @param [in] instantaneous_power_consumption_ua Instantaneous power consumption associated to each timings \ref + * lr11xx_gnss_instantaneous_power_consumption_ua_t + * @param [out] power_consumption_nah Power consumption computed in nAh + * @param [out] power_consumption_nwh Power consumption computed in nWh + */ +void lr11xx_gnss_compute_power_consumption( + const lr11xx_gnss_cumulative_timing_t* cumulative_timing, + const lr11xx_gnss_instantaneous_power_consumption_ua_t* instantaneous_power_consumption_ua, + uint32_t* power_consumption_nah, uint32_t* power_consumption_nwh ); + +/*! + * @brief Set the GPS time. + * + * This command is to be used when the 32kHz clock feeding the LR11xx is turned off. + * The LR11xx needs the 32kHz clock to track the absolute time. However if the clock is turned off, it will attempt to + * get the absolute time from GNSS SV demodulation on next GNSS scan, which is power consuming. + * However, if the MCU has capability to keep the absolute time when 32kHz clock is turned off, then it can use this + * command to configure the LR11xx, so that the LR11xx is more power efficient when fetching time from SV signal. + * + * Typical usage is: + * 1. MCU get absolute GPS time from any (possibly not accurate) source (like LoRaWAN network for instance) + * 2. On next scan, the MCU turns on the 32kHz clock, uses lr11xx_gnss_set_time to set the time, with an accuracy that + * depends on its crystal drift, and start the scan + * 3. MCU reads the time from LR11xx (lr11xx_gnss_read_time) and stores it internally + * 4. MCU turns off 32kHz clock of the LR11xx + * + * @param [in] context Chip implementation context + * @param [in] time GPS time in sec from 6 January 1980 00:00:00 + * @param [in] time_accuracy Accuracy in millisecond of the time given. If set to 0, the accuracy of time given is + * considered to be unknown + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_set_time( const void* context, const uint32_t time, const uint16_t time_accuracy ); + +/*! + * @brief Configures the time delay in sec. If the time elapsed from last Assistance position update is larger than this + * delay and there is always no SV detected, LR11xx will reset the Assistance position and the GNSS scan switches from + * assisted scan to autonomous scan. + * + * @param [in] context Chip implementation context + * @param [in] delay Delay in second on 3 bytes + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_config_delay_reset_assistance_position( const void* context, const uint32_t delay ); + +/*! + * @brief Read the assisted position based on the internal doppler solver executed during lr11xx_gnss_scan or + * lr11xx_gnss_almanac_update_from_sat functions. + * + * @param [in] context Chip implementation context + * @param [out] results \ref lr11xx_gnss_doppler_solver_result_t + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_doppler_solver_result( const void* context, + lr11xx_gnss_doppler_solver_result_t* results ); + +/*! + * @brief Read the time delay in sec. If the time elapsed from last Assistance position update is larger than this + * delay and there is always no SV detected, LR11xx will reset the Assistance position and the GNSS scan switches from + * assisted scan to autonomous scan. + * + * @param [in] context Chip implementation context + * @param [out] delay Delay in second on 3 bytes + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_delay_reset_assistance_position( const void* context, uint32_t* delay ); + +/*! + * @brief This command launches one scan to download from satellite almanac parameters broadcasted in one page by one + * constellation. + * + * @param [in] context Chip implementation context + * @param [in] constellation_mask Bit mask of the constellations to use. See @ref lr11xx_gnss_constellation_t for + * the possible values + * @param [in] effort_mode Effort mode @ref lr11xx_gnss_search_mode_t, note that LR11XX_GNSS_OPTION_HIGH_EFFORT is not + * supported here + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_almanac_update_from_sat( const void* context, + const lr11xx_gnss_constellation_mask_t constellation_mask, + const lr11xx_gnss_search_mode_t effort_mode ); + +/*! + * @brief This command read the number of visible satellites and the time elapsed from last detected satellite list + * update of this constellation. + * + * @param [in] context Chip implementation context + * @param [in] constellation_mask Bit mask of the constellations to use. See @ref lr11xx_gnss_constellation_t for + * the possible values. Only one constellation shall be selected otherwise the command will return an error + * @param [out] nb_visible_sat number of visible satellites + * @param [out] time_elapsed elapsed from last sv list update in ms + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_keep_sync_status( const void* context, + const lr11xx_gnss_constellation_mask_t constellation_mask, + uint8_t* nb_visible_sat, uint32_t* time_elapsed ); + +/*! + * @brief This command returns the actual state of almanac GPS and Beidou. + * + * @param [in] context Chip implementation context + * @param [in] almanac_status almanac status for GPS and Beidou @ref lr11xx_gnss_read_almanac_status_t + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_almanac_status( const void* context, + lr11xx_gnss_read_almanac_status_t* almanac_status ); + +/*! + * @brief Configures the almanac update period. + * + * @param [in] context Chip implementation context + * @param [in] constellation_mask Bit mask of the constellations to use. See @ref lr11xx_gnss_constellation_t for + * the possible values. Only one constellation shall be selected otherwise the command will return an error + * @param [in] sv_type sv type to configure. See @ref lr11xx_gnss_sv_type_t for + * the possible values. This parameter has no impact when constellation_mask is set to LR11XX_GNSS_GPS_MASK but is value + * must be a valid lr11xx_gnss_sv_type_t one + * @param [in] period delta in day computed between age of almanac in flash and current day and compared to this period + * to indicate to the application during a lr11xx_gnss_read_almanac_status if it must be downloaded + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_config_almanac_update_period( const void* context, + const lr11xx_gnss_constellation_mask_t constellation_mask, + const lr11xx_gnss_sv_type_t sv_type, const uint16_t period ); + +/*! + * @brief Read the almanac update period. + * + * @param [in] context Chip implementation context + * @param [in] constellation_mask Bit mask of the constellations to use. See @ref lr11xx_gnss_constellation_t for + * the possible values. Only one constellation shall be selected otherwise the command will return an error + * @param [in] sv_type sv type of satellites to read period from. See @ref lr11xx_gnss_sv_type_t for + * the possible values. This parameter has no impact when constellation_mask is set to LR11XX_GNSS_GPS_MASK but is value + * must be a valid lr11xx_gnss_sv_type_t one + * @param [out] period delta in day computed between age of almanac in flash and current day and compared to this period + * to indicate to the application during a lr11xx_gnss_read_almanac_status if it must be downloaded + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_almanac_update_period( const void* context, + const lr11xx_gnss_constellation_mask_t constellation_mask, + const lr11xx_gnss_sv_type_t sv_type, uint16_t* period ); + +/*! + * @brief Returns the list of satellite for the next keep sync scan. + * + * @param [in] context Chip implementation context + * @param [in] constellation_mask Bit mask of the constellations to use. See @ref lr11xx_gnss_constellation_t for + * the possible values. Only one constellation shall be selected otherwise the command will return an error + * @param [in] nb_sv_to_get Number of sv to read, the user must call lr11xx_gnss_read_keep_sync_status to know exactly + * the number of satellites in the list + * @param [out] sv_sync_list list of sync. It is up to the caller to ensure it is at least nb_sv_to_get byte long + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_get_sv_sync( const void* context, const lr11xx_gnss_constellation_mask_t constellation_mask, + const uint8_t nb_sv_to_get, uint8_t* sv_sync_list ); + +/*! + * @brief Configures the ability of the LR11xx to search almanac for each GPS satellites. + * + * @param [in] context Chip implementation context + * @param [in] gps_sat_activated_1_32 32-bit bit mask sat activated: sat 1-32 activated (default value: 0xFFFFFFFF) + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_set_gps_bit_mask_sat_activated( const void* context, + const uint32_t gps_sat_activated_1_32 ); + +/*! + * @brief Configures the ability of the LR11xx to search almanac for each Beidou satellites. + * + * @param [in] context Chip implementation context + * @param [in] beidou_sat_activated_1_32 32-bit bit mask sat activated: sat 1-32 activated (default value: 0xBFFCBFFF)) + * @param [in] beidou_sat_activated_33_63 32-bit bit mask sat activated: sat 33-63 activated (default value: 0xC0007FF)) + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_set_beidou_bit_mask_sat_activated( const void* context, + const uint32_t beidou_sat_activated_1_32, + const uint32_t beidou_sat_activated_33_63 ); + +/*! + * @brief Get the type of scan launched during the last scan + * + * @param [in] context Chip implementation context + * @param [out] last_scan_mode last scan launched. See @ref lr11xx_gnss_scan_mode_launched_t for + * the possible values. + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_gnss_read_last_scan_mode_launched( const void* context, + lr11xx_gnss_scan_mode_launched_t* last_scan_mode ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_GNSS_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_gnss_types.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_gnss_types.h new file mode 100755 index 0000000..e06c3dc --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_gnss_types.h @@ -0,0 +1,573 @@ +/*! + * @file lr11xx_gnss_types.h + * + * @brief GNSS scan driver types for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_GNSS_TYPES_H +#define LR11XX_GNSS_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/*! + * @brief Maximal buffer size + */ +#define LR11XX_GNSS_MAX_SIZE_ARRAY 2820 //!< (128sv * 22bytes + 4bytes for CRC) + +/*! + * @brief Number of almanacs in full update payload + */ +#define LR11XX_GNSS_FULL_UPDATE_N_ALMANACS ( 128 ) + +/*! + * @brief Size of the almanac of a single satellite when reading + */ +#define LR11XX_GNSS_SINGLE_ALMANAC_READ_SIZE ( 22 ) + +/*! + * @brief Size of the almanac of a single satellite when writing + */ +#define LR11XX_GNSS_SINGLE_ALMANAC_WRITE_SIZE ( 20 ) + +/*! + * @brief Size of the almanac of the GNSS context status buffer + */ +#define LR11XX_GNSS_CONTEXT_STATUS_LENGTH ( 9 ) + +/*! + * @brief Size of the whole almanac when reading + */ +#define LR11XX_GNSS_FULL_ALMANAC_READ_BUFFER_SIZE \ + ( ( LR11XX_GNSS_FULL_UPDATE_N_ALMANACS * LR11XX_GNSS_SINGLE_ALMANAC_READ_SIZE ) + 4 ) + +#define LR11XX_GNSS_DMC_ALMANAC_UPDATE_POS ( 1U ) +#define LR11XX_GNSS_DMC_ALMANAC_UPDATE_GPS_MASK ( 0x01UL << LR11XX_GNSS_DMC_ALMANAC_UPDATE_POS ) +#define LR11XX_GNSS_DMC_ALMANAC_UPDATE_BEIDOU_MASK ( 0x02UL << LR11XX_GNSS_DMC_ALMANAC_UPDATE_POS ) + +#define LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_MSB_POS ( 0U ) +#define LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_MSB_MASK ( 0x01UL << LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_MSB_POS ) + +#define LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_LSB_POS ( 7U ) +#define LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_LSB_MASK ( 0x01UL << LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_LSB_POS ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Satellite ID type + */ +typedef uint8_t lr11xx_gnss_satellite_id_t; + +/*! + * @brief bit mask indicating which information is added in the output payload + */ +enum lr11xx_gnss_result_fields_e +{ + LR11XX_GNSS_RESULTS_DOPPLER_ENABLE_MASK = ( 1 << 0 ), //!< Add Doppler information if set + LR11XX_GNSS_RESULTS_DOPPLER_MASK = ( 1 << 1 ), //!< Add up to 14 Doppler if set - up to 7 if not. Valid if @ref + //!< LR11XX_GNSS_RESULTS_DOPPLER_ENABLE_MASK is set + LR11XX_GNSS_RESULTS_BIT_CHANGE_MASK = + ( 1 << 2 ), //!< Add bit change if set, LR11XX_GNSS_SCAN_MODE_3_SINGLE_SCAN_AND_5_FAST_SCANS mode only + LR11XX_GNSS_RESULTS_DEMODULATE_TIME_MASK = + ( 1 << 3 ), //!< Add time demodulation if set, LR11XX_GNSS_SCAN_MODE_3_SINGLE_SCAN_AND_5_FAST_SCANS mode only + LR11XX_GNSS_RESULTS_REMOVE_TIME_FROM_NAV_MASK = ( 1 << 4 ), //!< Remove time from NAV if set + LR11XX_GNSS_RESULTS_REMOVE_AP_FROM_NAV_MASK = ( 1 << 5 ), //!< Remove aiding position from NAV if set +}; + +/*! + * @brief Constellation identifiers + */ +typedef enum +{ + LR11XX_GNSS_GPS_MASK = 0x01, + LR11XX_GNSS_BEIDOU_MASK = 0x02, +} lr11xx_gnss_constellation_t; + +/*! + * @brief Bit mask of constellation configurations + * + * @see lr11xx_gnss_constellation_t + */ +typedef uint8_t lr11xx_gnss_constellation_mask_t; + +/*! + * @brief Search mode for GNSS scan + */ +typedef enum +{ + LR11XX_GNSS_OPTION_LOW_EFFORT = 0x00, //!< Search all requested satellites or fail, scan duration is low + LR11XX_GNSS_OPTION_MID_EFFORT = + 0x01, //!< Add additional search if not all satellites are found, scan duration is standard + LR11XX_GNSS_OPTION_HIGH_EFFORT = + 0x02, //!< Add additional search if not all satellites are found, scan duration is very high +} lr11xx_gnss_search_mode_t; + +/*! + * @brief GNSS response type indicates the destination: Host MCU, GNSS solver or GNSS DMC + */ +typedef enum +{ + LR11XX_GNSS_DESTINATION_HOST = 0x00, //!< Host MCU + LR11XX_GNSS_DESTINATION_SOLVER = 0x01, //!< GNSS Solver + LR11XX_GNSS_DESTINATION_DMC = 0x02, //!< GNSS DMC +} lr11xx_gnss_destination_t; + +/*! + * @brief Message to host indicating the status of the message + */ +typedef enum +{ + LR11XX_GNSS_HOST_OK = 0x00, + LR11XX_GNSS_HOST_UNEXPECTED_CMD = 0x01, + LR11XX_GNSS_HOST_UNIMPLEMENTED_CMD = 0x02, + LR11XX_GNSS_HOST_INVALID_PARAMETERS = 0x03, + LR11XX_GNSS_HOST_MESSAGE_SANITY_CHECK_ERROR = 0x04, + LR11XX_GNSS_HOST_IQ_CAPTURE_FAILS = 0x05, + LR11XX_GNSS_HOST_NO_TIME = 0x06, + LR11XX_GNSS_HOST_NO_SATELLITE_DETECTED = 0x07, + LR11XX_GNSS_HOST_ALMANAC_IN_FLASH_TOO_OLD = 0x08, + LR11XX_GNSS_HOST_ALMANAC_UPDATE_FAILS_CRC_ERROR = 0x09, + LR11XX_GNSS_HOST_ALMANAC_UPDATE_FAILS_FLASH_INTEGRITY_ERROR = 0x0A, + LR11XX_GNSS_HOST_ALMANAC_UPDATE_NOT_ALLOWED = 0x0C, + LR11XX_GNSS_HOST_ALMANAC_CRC_ERROR = 0x0D, + LR11XX_GNSS_HOST_ALMANAC_VERSION_NOT_SUPPORTED = 0x0E, + LR11XX_GNSS_HOST_NOT_ENOUGH_SV_DETECTED_TO_BUILD_A_NAV_MESSAGE = 0x10, + LR11XX_GNSS_HOST_TIME_DEMODULATION_FAIL = 0x11, + LR11XX_GNSS_HOST_ALMANAC_DEMODULATION_FAIL = 0x12, + LR11XX_GNSS_HOST_AT_LEAST_THE_DETECTED_SV_OF_ONE_CONSTELLATION_ARE_DEACTIVATED = 0x13, + LR11XX_GNSS_HOST_ASSISTANCE_POSITION_POSSIBLY_WRONG_BUT_FAILS_TO_UPDATE = 0x14, + LR11XX_GNSS_HOST_SCAN_ABORTED = 0x15, + LR11XX_GNSS_HOST_NAV_MESSAGE_CANNOT_BE_GENERATED_INTERVAL_GREATER_THAN_63_SEC = 0x16, +} lr11xx_gnss_message_host_status_t; + +/*! + * @brief Message to DMC operation code + */ +typedef enum +{ + LR11XX_GNSS_DMC_STATUS = 0x18, //!< Status message in payload +} lr11xx_gnss_message_dmc_opcode_t; + +/*! + * @brief GNSS single or double scan mode + */ +typedef enum +{ + LR11XX_GNSS_SCAN_MODE_0_SINGLE_SCAN_LEGACY = 0x00, //!< Generated NAV message format = NAV3 + LR11XX_GNSS_SCAN_MODE_3_SINGLE_SCAN_AND_5_FAST_SCANS = 0x03, //!< Generated NAV message format = NAV3 +} lr11xx_gnss_scan_mode_t; + +/*! + * @brief GNSS error codes + */ +typedef enum lr11xx_gnss_error_code_e +{ + LR11XX_GNSS_NO_ERROR = 0, + LR11XX_GNSS_ERROR_ALMANAC_TOO_OLD = 1, + LR11XX_GNSS_ERROR_UPDATE_CRC_MISMATCH = 2, + LR11XX_GNSS_ERROR_UPDATE_FLASH_MEMORY_INTEGRITY = 3, + LR11XX_GNSS_ERROR_ALMANAC_UPDATE_NOT_ALLOWED = 4, //!< Impossible to update more than one constellation at a time +} lr11xx_gnss_error_code_t; + +/*! + * @brief GNSS frequency search space + */ +typedef enum lr11xx_gnss_freq_search_space_e +{ + LR11XX_GNSS_FREQUENCY_SEARCH_SPACE_250_HZ = 0, + LR11XX_GNSS_FREQUENCY_SEARCH_SPACE_500_HZ = 1, + LR11XX_GNSS_FREQUENCY_SEARCH_SPACE_1_KHZ = 2, + LR11XX_GNSS_FREQUENCY_SEARCH_SPACE_2_KHZ = 3, +} lr11xx_gnss_freq_search_space_t; + +/*! + * @brief GNSS fetch time option + */ +typedef enum lr11xx_gnss_fetch_time_option_e +{ + LR11XX_GNSS_SEARCH_TOW = 0, //!< Fetch the time of week. This option can only be used either after a successful + //!< GNSS scan, or after a successful call to lr11xx_gnss_fetch_time with option + //!< LR11XX_GNSS_SEARCH_TOW_WN or LR11XX_GNSS_SEARCH_TOW_WN_ROLLOVER + LR11XX_GNSS_SEARCH_TOW_WN = 1, //!< Fetch the time of week and the week number + LR11XX_GNSS_SEARCH_TOW_WN_ROLLOVER = + 2, //!< Fetch the time of week, week number and week number rollover since 1980 +} lr11xx_gnss_fetch_time_option_t; + +/*! + * @brief GNSS time status + */ +typedef enum lr11xx_gnss_read_time_status_e +{ + LR11XX_GNSS_READ_TIME_STATUS_NO_ERROR = 0, + LR11XX_GNSS_READ_TIME_STATUS_32K_STOPPED = 1, + LR11XX_GNSS_READ_TIME_STATUS_WN_TOW_NOT_SET = 2 +} lr11xx_gnss_read_time_status_t; + +/*! + * @brief GNSS week number number rollover status + */ +typedef enum lr11xx_gnss_week_number_rollover_status_e +{ + LR11XX_GNSS_WN_ROLLOVER_ROLLOVER_NEVER_SET = 0, + LR11XX_GNSS_WN_ROLLOVER_ROLLOVER_SET_BY_SCAN = 1, +} lr11xx_gnss_week_number_rollover_status_t; + +/*! + * @brief GNSS demod status + */ +typedef enum lr11xx_gnss_demod_status_e +{ + LR11XX_GNSS_NO_DEMOD_BDS_ALMANAC_SV31_43 = -21, + LR11XX_GNSS_SV_SELECTED_FOR_DEMOD_LOST = -20, + LR11XX_GNSS_ALMANAC_DEMOD_ERROR = -19, + LR11XX_GNSS_WAKE_UP_AFTER_PREAMBLE = -18, + LR11XX_GNSS_20MS_REAL_TIME_FAILURE = -17, + LR11XX_GNSS_WAKE_UP_SYNC_FAILURE = -16, + LR11XX_GNSS_WEEK_NUMBER_NOT_VALIDATED = -15, + LR11XX_GNSS_NO_ACTIVATED_SAT_IN_SV_LIST = -14, + LR11XX_GNSS_SLEEP_TIME_TOO_LONG = -13, + LR11XX_GNSS_WRONG_TIME_OF_WEEK_DEMOD = -12, + LR11XX_GNSS_PREAMBLE_NOT_VALIDATED = -11, + LR11XX_GNSS_DEMOD_DISABLE = -10, + LR11XX_GNSS_DEMOD_EXTRACTION_FAILURE = -9, + LR11XX_GNSS_NO_BIT_CHANGE_FOUND_DURING_START_DEMOD = -8, + LR11XX_GNSS_NO_BIT_CHANGE_FOUND_DURING_MULTISCAN = -7, + LR11XX_GNSS_NO_SAT_FOUND = -6, + LR11XX_GNSS_WORD_SYNC_LOST = -5, + LR11XX_GNSS_NOT_ENOUGH_PARITY_CHECK_FOUND = -3, + LR11XX_GNSS_TOO_MANY_PARITY_CHECK_FOUND = -2, + LR11XX_GNSS_NO_PARITY_CHECK_FOUND = -1, + LR11XX_GNSS_WORD_SYNC_SEARCH_NOT_STARTED = 0, + LR11XX_GNSS_WORD_SYNC_POTENTIALLY_FOUND = 1, + LR11XX_GNSS_WORD_SYNC_FOUND = 2, + LR11XX_GNSS_TIME_OF_WEEK_FOUND = 3, + LR11XX_GNSS_WEEK_NUMBER_FOUND = 4, + LR11XX_GNSS_ALMANAC_FOUND_BUT_NO_SAVED = 5, + LR11XX_GNSS_HALF_ALMANAC_FOUND_AND_SAVED = 6, + LR11XX_GNSS_ALMANAC_FOUND_AND_SAVED = 7, +} lr11xx_gnss_demod_status_t; + +/*! + * @brief GNSS doppler solver error code + */ +typedef enum lr11xx_gnss_doppler_solver_error_code_e +{ + LR11XX_GNSS_DOPPLER_SOLVER_NO_ERROR = 0, + LR11XX_GNSS_DOPPLER_SOLVER_ERROR_RESIDUE_HIGH = 1, + LR11XX_GNSS_DOPPLER_SOLVER_ERROR_NOT_CONVERGED = 2, + LR11XX_GNSS_DOPPLER_SOLVER_ERROR_NOT_ENOUGH_SV = 3, + LR11XX_GNSS_DOPPLER_SOLVER_ERROR_ILL_MATRIX = 4, + LR11XX_GNSS_DOPPLER_SOLVER_ERROR_TIME_ERROR = 5, + LR11XX_GNSS_DOPPLER_SOLVER_ERROR_PARTIAL_ALMANAC_TOO_OLD = 6, + LR11XX_GNSS_DOPPLER_SOLVER_ERROR_NOT_CONSISTENT_WITH_HISTORY = 7, + LR11XX_GNSS_DOPPLER_SOLVER_ERROR_ALL_ALMANAC_TOO_OLD = 8, +} lr11xx_gnss_doppler_solver_error_code_t; + +/*! + * @brief GNSS almanac status + */ +typedef enum lr11xx_gnss_almanac_status_e +{ + LR11XX_GNSS_INTERNAL_ACCURACY_TOO_LOW = -4, + LR11XX_GNSS_NO_TIME_SET = -3, + LR11XX_GNSS_IMPOSSIBLE_TO_FIND_NEXT_TIME = -2, + LR11XX_GNSS_NO_PAGE_ID_KNOWN = -1, + LR11XX_GNSS_NO_SAT_TO_UPDATE = 0, + LR11XX_GNSS_AT_LEAST_ONE_SAT_MUST_BE_UPDATED = 1, +} lr11xx_gnss_almanac_status_t; + +/*! + * @brief GNSS SV type + */ +typedef enum lr11xx_gnss_sv_type_e +{ + LR11XX_GNSS_MEO_SAT = 0, + LR11XX_GNSS_IGSO_SAT = 1, +} lr11xx_gnss_sv_type_t; + +typedef enum +{ + LR11XX_GNSS_LAST_SCAN_MODE_ASSISTED = 3, + LR11XX_GNSS_LAST_SCAN_MODE_AUTONOMOUS_NO_TIME_NO_AP = 4, + LR11XX_GNSS_LAST_SCAN_MODE_AUTONOMOUS_NO_AP = 5, + LR11XX_GNSS_LAST_SCAN_FETCH_TIME_OR_DOPPLER_SOLVER = 6, + LR11XX_GNSS_LAST_SCAN_ALMANAC_UPDATE = 7, + LR11XX_GNSS_LAST_SCAN_KEEP_SYNC = 8, + LR11XX_GNSS_LAST_SCAN_ALMANAC_UPDATE_1_CONSTELLATION = 9, + LR11XX_GNSS_LAST_SCAN_ALMANAC_UPDATE_2_CONSTELLATIONS = 10, +} lr11xx_gnss_scan_mode_launched_t; + +/*! + * @brief GNSS time structure + */ +typedef struct lr11xx_gnss_time_s +{ + lr11xx_gnss_read_time_status_t error_code; + uint32_t gps_time_s; + uint32_t nb_us_in_s; + uint32_t time_accuracy; +} lr11xx_gnss_time_t; + +/*! + * @brief GNSS demod info structure + */ +typedef struct lr11xx_gnss_demod_info_s +{ + bool word_sync_found; //!< 0: no word synchronization found / 1: a word synchronization has been found + bool first_tow_found; //!< 0: no Time Of Week found / 1: a Time Of Week has been found + bool wn_demodulated; //!< 0: no Week number demodulated / 1: a Week Number has been demodulated + bool wn_found; //!< 0: no Week number found / 1: a Week Number has been found + bool sub1_found; //!< 0: subframe ID not found / 1: subframe ID found + bool sub4_found; //!< 0: subframe ID not found / 1: subframe ID found + bool sub5_found; //!< 0: subframe ID not found / 1: subframe ID found +} lr11xx_gnss_demod_info_t; + +/*! + * @brief GNSS cumulative_timing + */ +typedef struct lr11xx_gnss_cumulative_timing_s +{ + uint32_t init; + uint32_t phase1_gps_capture; + uint32_t phase1_gps_process; + uint32_t multiscan_gps_capture; + uint32_t multiscan_gps_process; + uint32_t multiscan_gps_sleep_32k; + uint32_t phase1_beidou_capture; + uint32_t phase1_beidou_process; + uint32_t multiscan_beidou_capture; + uint32_t multiscan_beidou_process; + uint32_t multiscan_beidou_sleep_32k; + uint32_t demod_capture; + uint32_t demod_process; + uint32_t demod_sleep_32k; + uint32_t demod_sleep_32m; + uint32_t total_gps_capture; + uint32_t total_gps_process; + uint32_t total_gps_sleep_32k; + uint32_t total_gps_sleep_32m; + uint32_t total_gps; + uint32_t total_beidou_capture; + uint32_t total_beidou_process; + uint32_t total_beidou_sleep_32k; + uint32_t total_beidou_sleep_32m; + uint32_t total_beidou; + uint32_t total_capture; + uint32_t total_process; + uint32_t total_sleep_32k; + uint32_t total_sleep_32m; + uint32_t total; + uint32_t last_capture_size_32k_cnt; + uint8_t constellation_demod; +} lr11xx_gnss_cumulative_timing_t; + +/*! + * @brief GNSS instantaneous power consumption in ua + */ +typedef struct lr11xx_gnss_instantaneous_power_consumption_ua_s +{ + uint16_t board_voltage_mv; + uint16_t init_ua; + uint16_t phase1_gps_capture_ua; + uint16_t phase1_gps_process_ua; + uint16_t multiscan_gps_capture_ua; + uint16_t multiscan_gps_process_ua; + uint16_t phase1_beidou_capture_ua; + uint16_t phase1_beidou_process_ua; + uint16_t multiscan_beidou_capture_ua; + uint16_t multiscan_beidou_process_ua; + uint16_t sleep_32k_ua; + uint16_t demod_sleep_32m_ua; +} lr11xx_gnss_instantaneous_power_consumption_ua_t; + +/*! + * @brief + */ +typedef struct lr11xx_gnss_doppler_solver_result_s +{ + lr11xx_gnss_doppler_solver_error_code_t error_code; + uint8_t nb_sv_used; + uint16_t one_shot_latitude; + uint16_t one_shot_longitude; + uint16_t one_shot_accuracy; + uint16_t one_shot_xtal_ppb; + uint16_t filtered_latitude; + uint16_t filtered_longitude; + uint16_t filtered_accuracy; + uint16_t filtered_xtal_ppb; +} lr11xx_gnss_doppler_solver_result_t; + +/*! + * @brief + */ +typedef struct lr11xx_gnss_read_almanac_status_s +{ + lr11xx_gnss_almanac_status_t status_gps; + uint32_t next_gps_time_sat_to_update; //!< Next gps time sat to update: give the duration in milliseconds before + //!< the next start subframe where to catch the new almanac + uint8_t next_gps_nb_subframe_to_demodulate; + uint8_t next_gps_sat_id_to_update_in_sub_4; //!< Next gps sat id to update in subframe 4: satellite number that can + //!< be demodulated in next subframe 4 + uint8_t next_gps_sat_id_to_update_in_sub_5; //!< Next gps sat id to update in subframe 5: satellite number that can + //!< be demodulated in next subframe 5 + uint8_t nb_sat_gps_to_update; //!< the number total gps and bds that needs almanac update + uint8_t next_gps_subframe_id_start; //!< Next gps subframe ID start: can be equal to 4, 5 or 0 + uint32_t sat_id_gps_to_update; //!< Sat id gps to update: bit mask indicating which sat id almanac must be updated. + //!< bit at 0 : almanac is already updated, bit at 1: almanac sat must be updated + uint32_t sat_id_gps_activated; //!< Sat id gps activated : bit mask indicating which sat id is activated . bit at 0 + //!< : sat id is not activated, bit at 1: sat id is activated + lr11xx_gnss_almanac_status_t status_beidou; + uint32_t next_beidou_time_sat_to_update; //!< Next beidou time sat to update: give the duration in milliseconds + //!< before the next start subframe where to catch the new almanac + uint8_t next_beidou_nb_subframe_to_demodulate; + uint8_t next_beidou_sat_id_to_update_in_sub_4; //!< Next beidou sat id to update in subframe 4: satellite number + //!< that can be demodulated in next subframe 4 + uint8_t next_beidou_sat_id_to_update_in_sub_5; //!< Next beidou sat id to update in subframe 5: satellite number + //!< that can be demodulated in next subframe 5 + uint8_t nb_sat_beidou_to_update; //!< the number total gps and bds that needs almanac update + uint8_t next_beidou_subframe_id_start; //!< Next beidou subframe ID start: can be equal to 4, 5 or 0 + uint32_t sat_id_beidou_to_update[2]; //!< Sat id beidou to update: bit mask indicating which sat id almanac must be + //!< updated. bit at 0 : almanac is already updated, bit at 1: almanac sat + //!< must be updated + uint32_t sat_id_beidou_activated[2]; //!< Sat id gps activated : bit mask indicating which sat id is activated . + //!< bit at 0 : sat id is not activated, bit at 1: sat id is activated + uint32_t sat_id_beidou_black_list[2]; //!< Sat id bds black list: bit mask indicating which bds sv does not + //!< broadcast the almanac + //!< bit at 0 : sat id is not black listed, bit at 1: sat id is black listed + uint8_t next_am_id; //!< Next AmID: For beidou only. Page 11-24 of subframe 5 are used to broadcast almanac of sat + //!< 31 to 63 +} lr11xx_gnss_read_almanac_status_t; + +/*! + * @brief Representation of absolute time for GNSS operations + * + * The GNSS absolute time is represented as a 32 bits word that is the number of seconds elapsed since January 6th + * 1980, 00:00:00 + * + * The GNSS absolute time must take into account the Leap Seconds between UTC time and GPS time. + */ +typedef uint32_t lr11xx_gnss_date_t; + +/*! + * @brief Buffer that holds data for all almanacs full update - when reading + */ +typedef uint8_t lr11xx_gnss_almanac_full_read_bytestream_t[LR11XX_GNSS_FULL_ALMANAC_READ_BUFFER_SIZE]; + +/*! + * @brief Buffer that holds data for context status + */ +typedef uint8_t lr11xx_gnss_context_status_bytestream_t[LR11XX_GNSS_CONTEXT_STATUS_LENGTH]; + +/*! + * @brief Assistance position. + */ +typedef struct lr11xx_gnss_solver_assistance_position_s +{ + float latitude; //!< Latitude 12 bits (latitude in degree * 2048/90) with resolution 0.044° + float longitude; //!< Longitude 12 bits (longitude in degree * 2048/180) with resolution 0.088° +} lr11xx_gnss_solver_assistance_position_t; + +/*! + * @brief Detected SV structure + */ +typedef struct lr11xx_gnss_detected_satellite_s +{ + lr11xx_gnss_satellite_id_t satellite_id; + int8_t cnr; //!< Carrier-to-noise ration (C/N) in dB + int16_t doppler; //!< SV doppler in Hz +} lr11xx_gnss_detected_satellite_t; + +/*! + * @brief Version structure of the LR11XX GNSS firmware + */ +typedef struct lr11xx_gnss_version_s +{ + uint8_t gnss_firmware; //!< Version of the firmware + uint8_t gnss_almanac; //!< Version of the almanac format +} lr11xx_gnss_version_t; + +/*! + * @brief Structure for GNSS context status + */ +typedef struct lr11xx_gnss_context_status_s +{ + uint8_t firmware_version; + uint32_t global_almanac_crc; + lr11xx_gnss_error_code_t error_code; + bool almanac_update_gps; + bool almanac_update_beidou; + lr11xx_gnss_freq_search_space_t freq_search_space; +} lr11xx_gnss_context_status_t; + +/*! + * @brief Structure for information about visible SV + */ +typedef struct lr11xx_gnss_visible_satellite_s +{ + lr11xx_gnss_satellite_id_t satellite_id; //!< SV ID + int16_t doppler; //!< SV doppler in Hz + int16_t doppler_error; //!< SV doppler error - step of 125Hz +} lr11xx_gnss_visible_satellite_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_GNSS_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_hal.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_hal.h new file mode 100755 index 0000000..55b9bc7 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_hal.h @@ -0,0 +1,211 @@ +/*! + * @file lr11xx_hal.h + * + * @brief Hardware Abstraction Layer (HAL) interface for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_HAL_H +#define LR11XX_HAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ +#include +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/** + * @brief Write this to SPI bus while reading data, or as a dummy/placeholder + */ +#define LR11XX_NOP ( 0x00 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief LR11XX HAL status + */ +typedef enum lr11xx_hal_status_e +{ + LR11XX_HAL_STATUS_OK = 0, + LR11XX_HAL_STATUS_ERROR = 3, +} lr11xx_hal_status_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Radio data transfer - write + * + * @param [in] context Radio implementation parameters + * @param [in] command Pointer to the buffer to be transmitted + * @param [in] command_length Buffer size to be transmitted + * @param [in] data Pointer to the buffer to be transmitted + * @param [in] data_length Buffer size to be transmitted + * + * @returns Operation status + */ +lr11xx_hal_status_t lr11xx_hal_write( const void* context, const uint8_t* command, const uint16_t command_length, + const uint8_t* data, const uint16_t data_length ); + +/*! + * @brief Radio data transfer - read + * + * @remark This is a two-step radio read operation. It consists of writing the command, releasing then re-asserting the + * NSS line, then reading a discarded dummy byte followed by data_length bytes of response data from the transceiver. + * While reading the dummy bytes and the response data, the implementation of this function must ensure that only zero + * bytes (NOP) are written to the SPI bus. + * + * @param [in] context Radio implementation parameters + * @param [in] command Pointer to the buffer to be transmitted + * @param [in] command_length Buffer size to be transmitted + * @param [out] data Pointer to the buffer to be received + * @param [in] data_length Buffer size to be received + * + * @returns Operation status + * + * @remark Some hardware SPI implementations write arbitary values on the MOSI line while reading. If this is done on + * the LR11XX, non-zero values may be interpreted as commands. This driver does not exploit this functionality, and + * expects that zeros be sent on the MOSI line when this command is reading the command response data. + */ +lr11xx_hal_status_t lr11xx_hal_read( const void* context, const uint8_t* command, const uint16_t command_length, + uint8_t* data, const uint16_t data_length ); + +/*! + * @brief Direct read from the SPI bus + * + * @remark Unlike @ref lr11xx_hal_read, this is a simple direct SPI bus SS/read/nSS operation. While reading the + * response data, the implementation of this function must ensure that only zero bytes (NOP) are written to the SPI bus. + * + * @remark Formerly, that function depended on a lr11xx_hal_write_read API function, which required bidirectional SPI + * communication. Given that all other radio functionality can be implemented with unidirectional SPI, it has been + * decided to make this HAL API change to simplify implementation requirements. + * + * @remark Only required by the @ref lr11xx_system_get_status and @ref lr11xx_bootloader_get_status commands + * + * @param [in] context Radio implementation parameters + * @param [out] data Pointer to the buffer to be received + * @param [in] data_length Buffer size to be received + * + * @returns Operation status + */ +lr11xx_hal_status_t lr11xx_hal_direct_read( const void* context, uint8_t* data, const uint16_t data_length ); + +/*! + * @brief Reset the radio + * + * @param [in] context Radio implementation parameters + * + * @returns Operation status + */ +lr11xx_hal_status_t lr11xx_hal_reset( const void* context ); + +/*! + * @brief Wake the radio up. + * + * @param [in] context Radio implementation parameters + * + * @returns Operation status + */ +lr11xx_hal_status_t lr11xx_hal_wakeup( const void* context ); + +/*! + * @brief Abort a blocking command + * + * @param [in] context Radio implementation parameters + * + * @returns Operation status + */ +lr11xx_hal_status_t lr11xx_hal_abort_blocking_cmd( const void* context ); + +/*! + * @brief Return the computed CRC + * + * @param [in] initial_value initial value of the CRC + * @param [in] buffer Buffer containing data used to compute the CRC + * @param [in] length Length of buffer + * + * @returns CRC value + */ +inline static uint8_t lr11xx_hal_compute_crc( const uint8_t initial_value, const uint8_t* buffer, uint16_t length ) +{ + uint8_t crc = initial_value; + if (buffer == NULL) { + printf("Error: buffer is NULL!\n"); + return 0; + } + for( uint16_t i = 0; i < length; i++ ) + { + // printf("%d %d %d\r\n",i,length,sizeof(buffer) / sizeof(buffer[0])); + uint8_t extract = buffer[i]; + uint8_t sum; + + for( uint8_t j = 8; j > 0; j-- ) + { + sum = ( crc ^ extract ) & 0x01; + crc >>= 1; + + if( sum != 0 ) + { + crc ^= 0x65; + } + + extract >>= 1; + } + } + + return crc; +} + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_HAL_H diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_lr_fhss.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_lr_fhss.h new file mode 100755 index 0000000..3b44f32 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_lr_fhss.h @@ -0,0 +1,138 @@ +/*! + * @file lr11xx_lr_fhss.h + * + * @brief LR_FHSS driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_LR_FHSS_H +#define LR11XX_LR_FHSS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_lr_fhss_types.h" +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/** + * @brief Length, in bytes, of a LR-FHSS sync word + */ +#define LR_FHSS_SYNC_WORD_BYTES ( 4 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Initialize the LR_FHSS + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_lr_fhss_init( const void* context ); + +/** + * @brief Get the delay in microsecond between the last bit sent and the TX done interrupt + * + * @param [in] params lr11xx LR-FHSS parameter structure + * @param [in] payload_length Length of application-layer payload + * + * @returns Delay in microseconds + */ +uint16_t lr11xx_lr_fhss_get_bit_delay_in_us( const lr11xx_lr_fhss_params_t* params, uint16_t payload_length ); + +/*! + * @brief Configure a payload to be sent with LR_FHSS + * + * When calling this method, lr11xx_radio_set_lr_fhss_sync_word is implicitely called to configure the sync word. + * Note that the syncword must be 4 bytes long. + * + * @param [in] context Chip implementation context + * @param [in] lr_fhss_params Parameter configuration structure of the LRFHSS + * @param [in] hop_sequence_id Seed used to derive the hopping sequence pattern. Only the nine LSBs are taken into + * account + * @param [in] payload The payload to send. It is the responsibility of the caller to ensure that this references an + * array containing at least payload_length elements + * @param [in] payload_length The length of the payload + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_lr_fhss_build_frame( const void* context, const lr11xx_lr_fhss_params_t* lr_fhss_params, + uint16_t hop_sequence_id, const uint8_t* payload, uint8_t payload_length ); + +/*! + * @brief Get the time on air in ms for LR-FHSS transmission + * + * @param [in] params LR11XX LR-FHSS parameter structure + * @param [in] payload_length Length of application-layer payload + * + * @returns Time-on-air value in ms for LR-FHSS transmission + */ +uint32_t lr11xx_lr_fhss_get_time_on_air_in_ms( const lr11xx_lr_fhss_params_t* params, uint16_t payload_length ); + +/** + * @brief Return the number of hop sequences available using the given parameters + * + * @param [in] lr_fhss_params Parameter configuration structure of the LRFHSS + * + * @return Returns the number of valid hop sequences (512 or 384) + */ +unsigned int lr11xx_lr_fhss_get_hop_sequence_count( const lr11xx_lr_fhss_params_t* lr_fhss_params ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_LR_FHSS_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_lr_fhss_types.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_lr_fhss_types.h new file mode 100755 index 0000000..2e37a06 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_lr_fhss_types.h @@ -0,0 +1,65 @@ +/*! + * @file lr11xx_lr_fhss_types.h + * + * @brief LR_FHSS types definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_LR_FHSS_TYPES_H +#define LR11XX_LR_FHSS_TYPES_H + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr_fhss_v1_base_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief LR FHSS parameter structure + */ +typedef struct +{ + lr_fhss_v1_params_t lr_fhss_params; //!< Base LR FHSS parameters + int8_t device_offset; // + * Special values Meaning + * 0x000000 RX single: LR11XX stays in RX mode until a + * packet is received, then switch to standby RC mode 0xFFFFFF + * RX continuous: LR11XX stays in RX mode even after reception of a + * packet + * + * + * @param [in] context Chip implementation context + * @param [in] timeout_in_rtc_step The timeout configuration for RX operation + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type, lr11xx_radio_set_rx_tx_fallback_mode + */ +lr11xx_status_t lr11xx_radio_set_rx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step ); + +/*! + * @brief Start RX operations with a timeout in RTC step and configure the LNA LF0 mode + * + * This command sets the LR11XX to RX mode and configures the LNA LF0 mode. The radio must have been configured before + * using this command with @ref lr11xx_radio_set_pkt_type It internally calls lr11xx_radio_set_lna_mode. This command + * must be issued only for sub-GHz RX operations as it configures the LNA LF0, which is only used for sub-GHz Rx + * operations. + * + * By default, the timeout parameter allows to return automatically to standby RC mode if no packets have been received + * after a certain amount of time. This behavior can be altered by @ref lr11xx_radio_set_rx_tx_fallback_mode and @ref + * lr11xx_radio_auto_tx_rx. + * + * The timeout duration is obtained by: + * \f$ timeout\_duration\_ms = timeout \times \frac{1}{32.768} \f$ + * + * Maximal timeout value is 0xFFFFFF, which gives a maximal timeout of 511 seconds. + * + * The timeout argument can also have the following special values: + * + * + *
    Special values Meaning
    0x000000 RX single: LR11XX stays in RX mode until a + * packet is received, then switch to standby RC mode
    0xFFFFFF + * RX continuous: LR11XX stays in RX mode even after reception of a + * packet + *
    + * + * @param [in] context Chip implementation context + * @param [in] timeout_in_rtc_step The timeout configuration for RX operation + * @param [in] lna_mode Path to use for reception + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type, lr11xx_radio_set_rx_tx_fallback_mode + */ +lr11xx_status_t lr11xx_radio_set_rx_with_timeout_in_rtc_step_and_lna_mode( const void* context, + const uint32_t timeout_in_rtc_step, + lr11xx_radio_lna_mode_t lna_mode ); + +/*! + * @brief Start TX operations + * + * This command sets the LR11XX to TX mode. The radio must have been configured before using this command with @ref + * lr11xx_radio_set_pkt_type + * + * By default, the timeout parameter allows to return automatically to standby RC mode if the packet has not been + * completely transmitted after a certain amount of time. This behavior can be altered by @ref + * lr11xx_radio_set_rx_tx_fallback_mode and @ref lr11xx_radio_auto_tx_rx. + * + * @param [in] context Chip implementation context + * @param [in] timeout_in_ms The timeout configuration for TX operation + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type, lr11xx_radio_set_rx_tx_fallback_mode + */ +lr11xx_status_t lr11xx_radio_set_tx( const void* context, const uint32_t timeout_in_ms ); + +/*! + * @brief Start TX operations + * + * This command sets the LR11XX to TX mode. The radio must have been configured before using this command with @ref + * lr11xx_radio_set_pkt_type + * + * By default, the timeout parameter allows to return automatically to standby RC mode if the packet has not been + * completely transmitted after a certain amount of time. This behavior can be altered by @ref + * lr11xx_radio_set_rx_tx_fallback_mode and @ref lr11xx_radio_auto_tx_rx. + * + * The timeout duration is obtained by: + * \f$ timeout\_duration\_ms = timeout \times \frac{1}{32.768} \f$ + * + * Maximal value is 0xFFFFFF. + * + * If the timeout argument is 0, then no timeout is used. + * + * @param [in] context Chip implementation context + * @param [in] timeout_in_rtc_step The timeout configuration for TX operation + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type, lr11xx_radio_set_rx_tx_fallback_mode + */ +lr11xx_status_t lr11xx_radio_set_tx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step ); + +/*! + * @brief Set the frequency for future radio operations. + * + * This commands does not set frequency for Wi-Fi and GNSS scan operations. + * + * @param [in] context Chip implementation context + * @param [in] freq_in_hz The frequency in Hz to set for radio operations + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_rf_freq( const void* context, const uint32_t freq_in_hz ); + +/*! + * @brief Configure automatic TX after RX or automatic RX after TX + * + * After issuing this command, using the command @ref SetTx will make the LR11XX doing the following: + * - Enter TX mode as usual + * - Enter configurable Intermediary mode during configurable delay + * - Enter RX mode + * + * Similarly, after a @ref SetRx command, the LR11XX will do the following: + * - Enter RX mode as usual + * - Enter configurable Intermediary mode during configurable delay + * - Enter TX mode + * + * In case delay is 0, the LR11XX does not enter Intermediary mode and directly enter the following mode. + * + * To disable this behavior, use this function with delay set to 0xFFFFFFFF. + * + * @param [in] context Chip implementation context + * @param [in] delay Time to spend in Intermediary mode expressed as steps of \f$\frac{1}{32.768 KHz}\f$ steps. + * @param [in] intermediary_mode The mode the LR11XX enters after first mode completion during delay time + * @param [in] timeout The timeout duration of the automatic RX or TX, expressed as steps of \f$ \frac{1}{32.768KHz} \f$ + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_auto_tx_rx( const void* context, const uint32_t delay, + const lr11xx_radio_intermediary_mode_t intermediary_mode, + const uint32_t timeout ); + +/*! + * @brief Set Channel Activity Detection configuration + * + * @param [in] context Chip implementation context + * @param [in] cad_params The structure defining CAD configuration + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_cad_params( const void* context, const lr11xx_radio_cad_params_t* cad_params ); + +/*! + * @brief Set the packet type + * + * @param [in] context Chip implementation context + * @param [in] pkt_type Packet type to set + * + * @returns Operation status + * + * @see lr11xx_radio_get_pkt_type + */ +lr11xx_status_t lr11xx_radio_set_pkt_type( const void* context, const lr11xx_radio_pkt_type_t pkt_type ); + +/*! + * @brief Set the modulation parameters for GFSK packets + * + * The command @ref lr11xx_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] mod_params The structure of modulation configuration + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type + */ +lr11xx_status_t lr11xx_radio_set_gfsk_mod_params( const void* context, + const lr11xx_radio_mod_params_gfsk_t* mod_params ); + +/*! + * @brief Set the modulation parameters for BPSK packets + * + * The command @ref lr11xx_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] mod_params The structure of modulation configuration + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type + */ +lr11xx_status_t lr11xx_radio_set_bpsk_mod_params( const void* context, + const lr11xx_radio_mod_params_bpsk_t* mod_params ); + +/*! + * @brief Set the modulation parameters for LoRa packets + * + * The command @ref lr11xx_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] mod_params The structure of modulation configuration + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type + */ +lr11xx_status_t lr11xx_radio_set_lora_mod_params( const void* context, + const lr11xx_radio_mod_params_lora_t* mod_params ); + +/*! + * @brief Set the modulation parameters for LR-FHSS + * + * The command @ref lr11xx_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] mod_params The structure of modulation configuration + * + * @returns Operation status + * + * @see lr11xx_lr_fhss_set_pkt_type + */ +lr11xx_status_t lr11xx_radio_set_lr_fhss_mod_params( const void* context, + const lr11xx_radio_mod_params_lr_fhss_t* mod_params ); + +/*! + * @brief Set the packet parameters for GFSK packets + * + * The command @ref lr11xx_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] pkt_params The structure of packet configuration + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type, lr11xx_radio_set_gfsk_mod_params + */ +lr11xx_status_t lr11xx_radio_set_gfsk_pkt_params( const void* context, + const lr11xx_radio_pkt_params_gfsk_t* pkt_params ); + +/*! + * @brief Set the packet parameters for BPSK packets + * + * The command @ref lr11xx_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] pkt_params The structure of packet configuration + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type, lr11xx_radio_set_bpsk_mod_params + */ +lr11xx_status_t lr11xx_radio_set_bpsk_pkt_params( const void* context, + const lr11xx_radio_pkt_params_bpsk_t* pkt_params ); + +/*! + * @brief Set the packet parameters for LoRa packets + * + * The command @ref lr11xx_radio_set_pkt_type must be called prior this one. + * + * @param [in] context Chip implementation context + * @param [in] pkt_params The structure of packet configuration + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type, lr11xx_radio_set_lora_mod_params + */ +lr11xx_status_t lr11xx_radio_set_lora_pkt_params( const void* context, + const lr11xx_radio_pkt_params_lora_t* pkt_params ); + +/*! + * @brief Set the parameters for TX power and power amplifier ramp time + * + * The command @ref lr11xx_radio_set_pa_cfg must be called prior calling + * lr11xx_radio_set_tx_params. + * + * The range of possible TX output power values depends on PA selected with @ref lr11xx_radio_set_pa_cfg : + * - for LPA: power value goes from -17dBm to +14dBm (ie. from 0xEF to 0x0E) + * - for HPA: power value goes from -9dBm to +22dBm (ie. from 0xF7 to 0x16) + * + * Moreover, to use TX output power value higher than +10dBm, the @ref REGPASUPPLY_VBAT supply must have been selected + * with @ref lr11xx_radio_set_pa_cfg. + * + * @param [in] context Chip implementation context + * @param [in] pwr_in_dbm The TX output power in dBm + * @param [in] ramp_time The ramping time configuration for the PA + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_tx_params( const void* context, const int8_t pwr_in_dbm, + const lr11xx_radio_ramp_time_t ramp_time ); + +/*! + * @brief Sets the Node and Broadcast address used for GFSK + * + * This setting is used only when filtering is enabled. + * + * @param [in] context Chip implementation context + * @param [in] node_address The node address used as filter + * @param [in] broadcast_address The broadcast address used as filter + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_pkt_address( const void* context, const uint8_t node_address, + const uint8_t broadcast_address ); + +/*! + * @brief Alter the chip mode after successfull transmission or reception operation + * + * This setting is not used during Rx Duty Cycle mode or Auto Tx Rx. + * + * @param [in] context Chip implementation context + * @param [in] fallback_mode The chip mode to enter after successfull transmission or reception. + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_rx_tx_fallback_mode( const void* context, + const lr11xx_radio_fallback_modes_t fallback_mode ); + +/*! + * @brief Configure and start a Rx Duty Cycle operation + * + * It executes the following steps: + * 1. Reception: enters reception state for duration defined by rx_period + * - If mode is LR11XX_RADIO_RX_DUTY_CYCLE_MODE_RX: it is standard RX mode + * - If mode is LR11XX_RADIO_RX_DUTY_CYCLE_MODE_CAD (only in LoRa) : it is CAD operation + * 2. Depending on the over-the-air activity detection: + * - In case of positive over-the-air detection, the rx_period timeout is recomputed to the value + * \f$2 \times rx\_period + sleep\_period\f$ + * - If no air activity is detected, the LR11XX goes back to sleep mode with retention for a duration defined by + * sleep_period + * 3. On wake-up, the LR11XX restarts the process with the reception state. + * + * @remark If mode is configured to @ref LR11XX_RADIO_RX_DUTY_CYCLE_MODE_CAD, then the CAD configuration used in step 1. + * is the one set from the last call to @ref lr11xx_radio_set_cad_params. + * + * @param [in] context Chip implementation context + * @param [in] rx_period_in_ms The length of Rx period + * @param [in] sleep_period_in_ms The length of sleep period + * @param [in] mode The operation mode during Rx phase + * + * @returns Operation status + * + * @see lr11xx_radio_set_cad_params + */ +lr11xx_status_t lr11xx_radio_set_rx_duty_cycle( const void* context, const uint32_t rx_period_in_ms, + const uint32_t sleep_period_in_ms, + const lr11xx_radio_rx_duty_cycle_mode_t mode ); + +/*! + * @brief Configure and start a Rx Duty Cycle operation + * + * It executes the following steps: + * 1. Reception: enters reception state for duration defined by rx_period + * - If mode is LR11XX_RADIO_RX_DUTY_CYCLE_MODE_RX: it is standard RX mode + * - If mode is LR11XX_RADIO_RX_DUTY_CYCLE_MODE_CAD (only in LoRa) : it is CAD operation + * 2. Depending on the over-the-air activity detection: + * - In case of positive over-the-air detection, the rx_period timeout is recomputed to the value + * \f$2 \times rx\_period + sleep\_period\f$ + * - If no air activity is detected, the LR11XX goes back to sleep mode with retention for a duration defined by + * sleep_period + * 3. On wake-up, the LR11XX restarts the process with the reception state. + * + * @remark If mode is configured to @ref LR11XX_RADIO_RX_DUTY_CYCLE_MODE_CAD, then the CAD configuration used in step 1. + * is the one set from the last call to @ref lr11xx_radio_set_cad_params. + * + * @param [in] context Chip implementation context + * @param [in] rx_period_in_rtc_step The length of Rx period + * @param [in] sleep_period_in_rtc_step The length of sleep period + * @param [in] mode The operation mode during Rx phase + * + * @returns Operation status + * + * @see lr11xx_radio_set_cad_params + */ +lr11xx_status_t lr11xx_radio_set_rx_duty_cycle_with_timings_in_rtc_step( const void* context, + const uint32_t rx_period_in_rtc_step, + const uint32_t sleep_period_in_rtc_step, + const lr11xx_radio_rx_duty_cycle_mode_t mode ); + +/*! + * @brief Set the Power Amplifier configuration + * + * It must be called prior using @ref lr11xx_radio_set_tx_params. + * + * @param [in] context Chip implementation context + * @param [in] pa_cfg The structure for PA configuration + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_pa_cfg( const void* context, const lr11xx_radio_pa_cfg_t* pa_cfg ); + +/*! + * @brief Define on which event the Rx timeout shall be stopped + * + * The two options are: + * - Syncword / Header detection + * - Preamble detection + * + * @param [in] context Chip implementation context + * @param [in] stop_timeout_on_preamble The choice of the event to be taken into account + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_stop_timeout_on_preamble( const void* context, const bool stop_timeout_on_preamble ); + +/*! + * @brief Start the CAD mode + * + * The LoRa packet type shall be selected before this function is called. The fallback mode is configured with + * lr11xx_radio_set_cad_params. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr11xx_radio_set_cad_params, lr11xx_radio_set_pkt_type + */ +lr11xx_status_t lr11xx_radio_set_cad( const void* context ); + +/*! + * @brief Set the device into Tx continuous wave (RF tone). + * + * A packet type shall be selected before this function is called. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr11xx_radio_set_pkt_type + */ +lr11xx_status_t lr11xx_radio_set_tx_cw( const void* context ); + +/*! + * @brief Set the device into Tx continuous preamble (modulated signal). + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_tx_infinite_preamble( const void* context ); + +/*! + * @brief Configure the LoRa modem to issue a RX timeout after an exact number of symbols given in parameter if no LoRa + * modulation is detected + * + * @warning Values of nb_symbol higher than 255 are only valid for chip firmware equal to or more recent than 0x308. + * + * @param [in] context Chip implementation context + * @param [in] nb_symbol number of symbols to compute the timeout + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_lora_sync_timeout( const void* context, const uint16_t nb_symbol ); + +/*! + * @brief Configure the LoRa modem to issue a RX timeout after an exact number of symbols given in parameter if no LoRa + * modulation is detected + * + * @warning This command has been introduced in chip firware 0x0308 and is not available in earlier version + * + * @remark The number of symbol is computed as mantissa ^ (2*exponent + 1) + * + * @param [in] context Chip implementation context + * @param [in] mantissa Mantissa - from 0 to 31 - to compute the number of symbols of the timeout + * @param [in] exponent Exponent - from 0 to 7 - to compute the number of symbols of the timeout + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_lora_sync_timeout_with_mantissa_exponent( const void* context, const uint8_t mantissa, + const uint8_t exponent ); + +/*! + * @brief Configure the seed and the polynomial used to compute CRC in GFSK packet + * + * @param [in] context Chip implementation context + * @param [in] seed Seed used to compute the CRC value + * @param [in] polynomial Polynomial used to compute the CRC value + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_gfsk_crc_params( const void* context, const uint32_t seed, const uint32_t polynomial ); + +/*! + * @brief Configure the whitening seed used in GFSK packet + * + * @param [in] context Chip implementation context + * @param [in] seed Whitening seed value + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_gfsk_whitening_seed( const void* context, const uint16_t seed ); + +/*! + * @brief Configure the boost mode in reception + * + * @param [in] context Chip implementation context + * @param [in] enable_boost_mode Boost mode activation + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_cfg_rx_boosted( const void* context, const bool enable_boost_mode ); + +/*! + * @brief Set RSSI calibration table + * + * @param [in] context Chip implementation context + * @param [in] rssi_cal_table RSSI calibration table + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_set_rssi_calibration( const void* context, + const lr11xx_radio_rssi_calibration_table_t* rssi_cal_table ); + +/*! + * @brief Gets the radio bw parameter for a given bandwidth in Hz + * + * @param [in] bw_in_hz Requested GFSK Rx bandwidth + * @param [out] bw_parameter Radio parameter immediately above requested bw_in_hz + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_get_gfsk_rx_bandwidth( uint32_t bw_in_hz, lr11xx_radio_gfsk_bw_t* bw_parameter ); + +/** + * @brief Compute the numerator for LoRa time-on-air computation. + * + * @remark To get the actual time-on-air in seconds, this value has to be divided by the LoRa bandwidth in Hertz. + * + * @param [in] pkt_p Pointer to the structure holding the LoRa packet parameters + * @param [in] mod_p Pointer to the structure holding the LoRa modulation parameters + * + * @returns LoRa time-on-air numerator + */ +uint32_t lr11xx_radio_get_lora_time_on_air_numerator( const lr11xx_radio_pkt_params_lora_t* pkt_p, + const lr11xx_radio_mod_params_lora_t* mod_p ); + +/** + * @brief Get the actual value in Hertz of a given LoRa bandwidth + * + * @param [in] bw LoRa bandwidth parameter + * + * @returns Actual LoRa bandwidth in Hertz + */ +uint32_t lr11xx_radio_get_lora_bw_in_hz( lr11xx_radio_lora_bw_t bw ); + +/*! + * @brief Get the time on air in ms for LoRa transmission + * + * @param [in] pkt_p Pointer to a structure holding the LoRa packet parameters + * @param [in] mod_p Pointer to a structure holding the LoRa modulation parameters + * + * @returns Time-on-air value in ms for LoRa transmission + */ +uint32_t lr11xx_radio_get_lora_time_on_air_in_ms( const lr11xx_radio_pkt_params_lora_t* pkt_p, + const lr11xx_radio_mod_params_lora_t* mod_p ); + +/** + * @brief Compute the numerator for GFSK time-on-air computation. + * + * @remark To get the actual time-on-air in seconds, this value has to be divided by the GFSK bitrate in bits per + * second. + * + * @param [in] pkt_p Pointer to the structure holding the GFSK packet parameters + * + * @returns GFSK time-on-air numerator + */ +uint32_t lr11xx_radio_get_gfsk_time_on_air_numerator( const lr11xx_radio_pkt_params_gfsk_t* pkt_p ); + +/** + * @brief Get the time on air in ms for GFSK transmission + * + * @param [in] pkt_p Pointer to a structure holding the GFSK packet parameters + * @param [in] mod_p Pointer to a structure holding the GFSK modulation parameters + * + * @returns Time-on-air value in ms for GFSK transmission + */ +uint32_t lr11xx_radio_get_gfsk_time_on_air_in_ms( const lr11xx_radio_pkt_params_gfsk_t* pkt_p, + const lr11xx_radio_mod_params_gfsk_t* mod_p ); + +/** + * @brief Get the number of RTC steps for a given time in millisecond + * + * @param [in] time_in_ms Timeout in millisecond + * + * @returns Number of RTC steps + */ +uint32_t lr11xx_radio_convert_time_in_ms_to_rtc_step( uint32_t time_in_ms ); + +/*! + * @brief Configure the radio for Bluetooth® Low Energy Beaconing Compatibility. + * + * The caller shall ensure that the payload, if provided, follows the format of the Advertising physical channel PDU as + * defined in the Bluetooth® core specification. + * + * This automatically configures the syncword to 0x8e89bed6 and the 3-byte CRC (polynomial set to 0x100065b, seed set to + * 0x555555). + * + * @param [in] context Chip implementation context + * @param [in] channel_id BLE channel - allowed channels are 37, 38, 39 + * @param [in] buffer Array of bytes to be used as beacon payload (optional) + * @param [in] length Number of bytes in the @ref buffer array + * + * @returns Operation status + * + * @note As opposed to the function @ref lr11xx_radio_cfg_and_send_bluetooth_low_energy_beaconning_compatibility, this + * function only configures the radio interface for Bluetooth® Low Energy Beaconing Compatibility advertising. To + * actually start the transmission, the function @ref lr11xx_radio_set_tx must be called. + * @note The previously configured payload with @ref lr11xx_radio_cfg_bluetooth_low_energy_beaconning_compatibility or + * @ref lr11xx_radio_cfg_and_send_bluetooth_low_energy_beaconning_compatibility is sent if @p length is set to 0, except + * if a call to @ref lr11xx_regmem_write_buffer8, @ref lr11xx_lr_fhss_build_frame is done in between or @ref + * lr11xx_system_set_sleep with warm start disabled + * + * @sa lr11xx_radio_cfg_and_send_bluetooth_low_energy_beaconning_compatibility + * @sa lr11xx_radio_set_tx + */ +lr11xx_status_t lr11xx_radio_cfg_bluetooth_low_energy_beaconning_compatibility( const void* context, + const uint8_t channel_id, + const uint8_t* buffer, + const uint8_t length ); + +/** + * @brief Get the information from the last received LoRa packet header (if @ref LR11XX_RADIO_LORA_PKT_EXPLICIT) or the + * locally configured settings (if @ref LR11XX_RADIO_LORA_PKT_IMPLICIT) + * + * @remark This function can be called only if @ref LR11XX_RADIO_PKT_TYPE_LORA is selected with @ref + * lr11xx_radio_set_pkt_type + * + * @param [in] context Chip implementation context + * @param [out] is_crc_present CRC configuration + * @param [out] cr LoRa coding rate + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_get_lora_rx_info( const void* context, bool* is_crc_present, lr11xx_radio_lora_cr_t* cr ); + +/*! + * @brief Configure the radio for Bluetooth® Low Energy Beaconing Compatibility and send the given beacon on the desired + * channel. + * + * The caller shall ensure that the payload, if provided, follows the format of the Advertising physical channel PDU as + * defined in the Bluetooth® core specification. + * + * This automatically configures the syncword to 0x8e89bed6 and the 3-byte CRC (polynomial set to 0x100065b, seed set to + * 0x555555). + * + * @param [in] context Chip implementation context + * @param [in] channel_id BLE channel - allowed channels are 37, 38, 39 + * @param [in] buffer Array of bytes to be used as beacon payload (optional) + * @param [in] length Number of bytes in the @ref buffer array + * + * @returns Operation status + * + * @note This function combines the configuration for Bluetooth® Low Energy Beaconing Compatibility - done with @ref + * lr11xx_radio_cfg_bluetooth_low_energy_beaconning_compatibility) - and the actual transmission - done with @ref + * lr11xx_radio_set_tx. + * @note The previously configured payload with @ref lr11xx_radio_cfg_bluetooth_low_energy_beaconning_compatibility or + * @ref lr11xx_radio_cfg_and_send_bluetooth_low_energy_beaconning_compatibility is sent if @p length is set to 0, except + * if a call to @ref lr11xx_regmem_write_buffer8, @ref lr11xx_lr_fhss_build_frame is done in between or @ref + * lr11xx_system_set_sleep with warm start disabled + * + * @sa lr11xx_radio_cfg_bluetooth_low_energy_beaconning_compatibility + */ +lr11xx_status_t lr11xx_radio_cfg_and_send_bluetooth_low_energy_beaconning_compatibility( const void* context, + const uint8_t channel_id, + const uint8_t* buffer, + const uint8_t length ); + +/*! + * @brief Apply the workaround for the high ACP limitation + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_radio_apply_high_acp_workaround( const void* context ); + +/** + * @brief Get the mantissa and exponent for a given number of symbol + * + * @remark This function computes the [mantissa, exponent] duple which corresponds to nb_of_symb: the smallest + * value verifying both following conditions: + * - nb_of_symb >= nb_symbol; and + * - nb_of_symb = mant * 2 ^ (2 * exp + 1) + * + * @param [in] nb_symbol Number of symbols + * @param [out] mant Mantissa computed from nb_symb + * @param [out] ext Exponent computed from nb_symb + * + * @returns Number of symbols corresponding to the [mantissa, exponent] duple computed with the following formula: + * nb_of_symb = mant * 2 ^ (2 * exp + 1) + */ +uint16_t lr11xx_radio_convert_nb_symb_to_mant_exp( const uint16_t nb_symbol, uint8_t* mant, uint8_t* exp ); + +/** + * @brief Configure LNA LF0 mode + * + * @remark This function shall be called after each call to lr11xx_radio_set_rx or + * lr11xx_radio_set_rx_with_timeout_in_rtc_step to be taken into account. Helper functions + * lr11xx_radio_set_rx_on_lna_path or lr11xx_radio_set_rx_with_timeout_in_rtc_step_on_lna_path can be used instead. + * + * @param [in] context Chip implementation context + * @param [in] lna_mode Value to put in the register, enum type + * + * @returns Operation status + * + * @see lr11xx_radio_set_rx_on_lna_path, lr11xx_radio_set_rx_with_timeout_in_rtc_step_on_lna_path + */ +lr11xx_status_t lr11xx_radio_set_lna_mode( const void* context, lr11xx_radio_lna_mode_t lna_mode ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_RADIO_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_radio_timings.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_radio_timings.h new file mode 100755 index 0000000..6270d56 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_radio_timings.h @@ -0,0 +1,95 @@ +/** + * @file lr11xx_radio_timings.h + * + * @brief LR11XX timing helper functions definition + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_RADIO_TIMINGS_H +#define LR11XX_RADIO_TIMINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_radio_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/** + * @brief Get the time between the last bit sent (on Tx side) and the Rx done event (on Rx side) + * + * @param [in] mod_params Pointer to a structure holding the LoRa modulation parameters used for the computation + * + * @returns Delay in microsecond + */ +uint32_t lr11xx_radio_timings_get_delay_between_last_bit_sent_and_rx_done_in_us( + const lr11xx_radio_mod_params_lora_t* mod_params ); + +/** + * @brief Get the time between the last bit sent and the Tx done event + * + * @param [in] ramp_time Power amplifier ramp time + * + * @returns Delay in microsecond + */ +uint32_t lr11xx_radio_timings_get_delay_between_last_bit_sent_and_tx_done_in_us( + const lr11xx_radio_ramp_time_t ramp_time ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_RADIO_TIMINGS_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_radio_types.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_radio_types.h new file mode 100755 index 0000000..4794f41 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_radio_types.h @@ -0,0 +1,654 @@ +/*! + * @file lr11xx_radio_types.h + * + * @brief Radio driver types for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_RADIO_TYPES_H +#define LR11XX_RADIO_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/*! + * @brief Bit mask to set to indicate a LR-FHSS bitrate is defined in steps of 1/256 bit per seconds + */ +#define LR11XX_RADIO_LR_FHSS_BITRATE_DIVIDE_BY_256 ( 0x80000000 ) + +/*! + * @brief LR-FHSS bitrate value at 488.28125 bps defined as steps of 1/256 bitrate per seconds + */ +#define LR11XX_RADIO_LR_FHSS_BITRATE_IN_256_BPS_STEPS ( 125000 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Power Amplifier Selection values + * + * - Low-power Power Amplifier can reach up to 14dBm + * - High-power Power Amplifier can reach up to 22 dBm + */ +typedef enum +{ + LR11XX_RADIO_PA_SEL_LP = 0x00, //!< Low-power Power Amplifier + LR11XX_RADIO_PA_SEL_HP = 0x01, //!< High-power Power Amplifier + LR11XX_RADIO_PA_SEL_HF = 0x02, //!< High-frequency Power Amplifier +} lr11xx_radio_pa_selection_t; + +/*! + * @brief GFSK Address Filtering configurations + * + * If Address Filtering is enabled but a wrong address is received, therefore the reception is aborted and the address + * error flag of packet status is set. + */ +typedef enum +{ + LR11XX_RADIO_GFSK_ADDRESS_FILTERING_DISABLE = 0x00, //!< Filter deactivated + LR11XX_RADIO_GFSK_ADDRESS_FILTERING_NODE_ADDRESS = 0x01, //!< Filter on Node Address + LR11XX_RADIO_GFSK_ADDRESS_FILTERING_NODE_AND_BROADCAST_ADDRESSES = + 0x02, //!< Filtering on Node and Broadcast addresses +} lr11xx_radio_gfsk_address_filtering_t; + +/*! + * @brief Chip mode after successfull transmission or reception + * + * Unused for RX duty cycle and AutoTxRx operations + */ +typedef enum +{ + LR11XX_RADIO_FALLBACK_STDBY_RC = 0x01, //!< Standby RC (Default) + LR11XX_RADIO_FALLBACK_STDBY_XOSC = 0x02, //!< Standby XOSC + LR11XX_RADIO_FALLBACK_FS = 0x03 //!< FS +} lr11xx_radio_fallback_modes_t; + +/*! + * @brief Ramping time for PA + * + * This parameter is the ramping time of the PA. A high value improves spectral quality. + */ +typedef enum +{ + LR11XX_RADIO_RAMP_16_US = 0x00, //!< 16 us Ramp Time + LR11XX_RADIO_RAMP_32_US = 0x01, //!< 32 us Ramp Time + LR11XX_RADIO_RAMP_48_US = 0x02, //!< 48 us Ramp Time (Default) + LR11XX_RADIO_RAMP_64_US = 0x03, //!< 64 us Ramp Time + LR11XX_RADIO_RAMP_80_US = 0x04, //!< 80 us Ramp Time + LR11XX_RADIO_RAMP_96_US = 0x05, //!< 96 us Ramp Time + LR11XX_RADIO_RAMP_112_US = 0x06, //!< 112 us Ramp Time + LR11XX_RADIO_RAMP_128_US = 0x07, //!< 128 us Ramp Time + LR11XX_RADIO_RAMP_144_US = 0x08, //!< 144 us Ramp Time + LR11XX_RADIO_RAMP_160_US = 0x09, //!< 160 us Ramp Time + LR11XX_RADIO_RAMP_176_US = 0x0A, //!< 176 us Ramp Time + LR11XX_RADIO_RAMP_192_US = 0x0B, //!< 192 us Ramp Time + LR11XX_RADIO_RAMP_208_US = 0x0C, //!< 208 us Ramp Time + LR11XX_RADIO_RAMP_240_US = 0x0D, //!< 240 us Ramp Time + LR11XX_RADIO_RAMP_272_US = 0x0E, //!< 272 us Ramp Time + LR11XX_RADIO_RAMP_304_US = 0x0F, //!< 304 us Ramp Time +} lr11xx_radio_ramp_time_t; + +/*! + * @brief LoRa network type configuration + */ +typedef enum +{ + LR11XX_RADIO_LORA_NETWORK_PRIVATE = 0x00, //!< LoRa private network + LR11XX_RADIO_LORA_NETWORK_PUBLIC = 0x01, //!< LoRa public network +} lr11xx_radio_lora_network_type_t; + +/*! + * @brief LoRa Spreading Factor configurations + */ +typedef enum +{ + LR11XX_RADIO_LORA_SF5 = 0x05, //!< Spreading Factor 5 + LR11XX_RADIO_LORA_SF6 = 0x06, //!< Spreading Factor 6 + LR11XX_RADIO_LORA_SF7 = 0x07, //!< Spreading Factor 7 + LR11XX_RADIO_LORA_SF8 = 0x08, //!< Spreading Factor 8 + LR11XX_RADIO_LORA_SF9 = 0x09, //!< Spreading Factor 9 + LR11XX_RADIO_LORA_SF10 = 0x0A, //!< Spreading Factor 10 + LR11XX_RADIO_LORA_SF11 = 0x0B, //!< Spreading Factor 11 + LR11XX_RADIO_LORA_SF12 = 0x0C, //!< Spreading Factor 12 +} lr11xx_radio_lora_sf_t; + +/*! + * @brief LoRa Bandwidth configurations + */ +typedef enum +{ + LR11XX_RADIO_LORA_BW_10 = 0x08, //!< Bandwidth 10.42 kHz + LR11XX_RADIO_LORA_BW_15 = 0x01, //!< Bandwidth 15.63 kHz + LR11XX_RADIO_LORA_BW_20 = 0x09, //!< Bandwidth 20.83 kHz + LR11XX_RADIO_LORA_BW_31 = 0x02, //!< Bandwidth 31.25 kHz + LR11XX_RADIO_LORA_BW_41 = 0x0A, //!< Bandwidth 41.67 kHz + LR11XX_RADIO_LORA_BW_62 = 0x03, //!< Bandwidth 62.50 kHz + LR11XX_RADIO_LORA_BW_125 = 0x04, //!< Bandwidth 125.00 kHz + LR11XX_RADIO_LORA_BW_250 = 0x05, //!< Bandwidth 250.00 kHz + LR11XX_RADIO_LORA_BW_500 = 0x06, //!< Bandwidth 500.00 kHz + LR11XX_RADIO_LORA_BW_200 = 0x0D, //!< Bandwidth 203.00 kHz, 2G4 and compatible with LR112x chips only + LR11XX_RADIO_LORA_BW_400 = 0x0E, //!< Bandwidth 406.00 kHz, 2G4 and compatible with LR112x chips only + LR11XX_RADIO_LORA_BW_800 = 0x0F, //!< Bandwidth 812.00 kHz, 2G4 and compatible with LR112x chips only +} lr11xx_radio_lora_bw_t; + +/*! + * @brief LoRa Coding Rate configurations + */ +typedef enum +{ + LR11XX_RADIO_LORA_NO_CR = 0x00, //!< No Coding Rate + LR11XX_RADIO_LORA_CR_4_5 = 0x01, //!< Coding Rate 4/5 Short Interleaver + LR11XX_RADIO_LORA_CR_4_6 = 0x02, //!< Coding Rate 4/6 Short Interleaver + LR11XX_RADIO_LORA_CR_4_7 = 0x03, //!< Coding Rate 4/7 Short Interleaver + LR11XX_RADIO_LORA_CR_4_8 = 0x04, //!< Coding Rate 4/8 Short Interleaver + LR11XX_RADIO_LORA_CR_LI_4_5 = 0x05, //!< Coding Rate 4/5 Long Interleaver + LR11XX_RADIO_LORA_CR_LI_4_6 = 0x06, //!< Coding Rate 4/6 Long Interleaver + LR11XX_RADIO_LORA_CR_LI_4_8 = 0x07, //!< Coding Rate 4/8 Long Interleaver +} lr11xx_radio_lora_cr_t; + +/*! + * @brief Values for intermediary mode + */ +typedef enum +{ + LR11XX_RADIO_MODE_SLEEP = 0x00, //!< Sleep / Not recommended with LR1110 FW from 0x0303 to 0x0307 and LR1120 FW + //!< 0x0101 in case of transition from Rx to Tx in LoRa + LR11XX_RADIO_MODE_STANDBY_RC = 0x01, //!< Standby RC + LR11XX_RADIO_MODE_STANDBY_XOSC = 0x02, //!< Standby XOSC + LR11XX_RADIO_MODE_FS = 0x03 //!< Frequency Synthesis +} lr11xx_radio_intermediary_mode_t; + +/*! + * @brief GFSK Cyclic Redundancy Check configurations + * + * If this value is set to something other than CRC_OFF, a CRC is automatically computed and added after the end of the + * payload on transmitter side. On receiver side, the CRC check is automatically processed. + */ +typedef enum +{ + LR11XX_RADIO_GFSK_CRC_OFF = 0x01, //!< CRC check deactivated + LR11XX_RADIO_GFSK_CRC_1_BYTE = 0x00, + LR11XX_RADIO_GFSK_CRC_2_BYTES = 0x02, + LR11XX_RADIO_GFSK_CRC_1_BYTE_INV = 0x04, + LR11XX_RADIO_GFSK_CRC_2_BYTES_INV = 0x06, +} lr11xx_radio_gfsk_crc_type_t; + +/*! + * @brief GFSK data whitening configurations + */ +typedef enum +{ + LR11XX_RADIO_GFSK_DC_FREE_OFF = 0x00, //!< Whitening deactivated + LR11XX_RADIO_GFSK_DC_FREE_WHITENING = 0x01, //!< Whitening enabled + LR11XX_RADIO_GFSK_DC_FREE_WHITENING_SX128X_COMP = 0x03, //!< Whitening enabled - SX128x compatibility +} lr11xx_radio_gfsk_dc_free_t; + +/*! + * @brief GFSK Header Type configurations + * + * This parameter indicates whether or not the payload length is sent and read over the air. + * + * If the payload length is known beforehand by both transmitter and receiver, therefore there is no need to send it + * over the air. Otherwise, setting this parameter to LR11XX_RADIO_GFSK_PKT_VAR_LEN will make the modem to automatically + * prepand a byte containing the payload length to the the payload on transmitter side. On receiver side, this first + * byte is read to set the payload length to read. + * + * This configuration is only available for GFSK packet types. + */ +typedef enum +{ + LR11XX_RADIO_GFSK_PKT_FIX_LEN = 0x00, //!< Payload length is not sent/read over the air + LR11XX_RADIO_GFSK_PKT_VAR_LEN = 0x01, //!< Payload length is sent/read over the air + LR11XX_RADIO_GFSK_PKT_VAR_LEN_SX128X_COMP = + 0x02, //!< Payload length is sent/read over the air - SX128x compatibility +} lr11xx_radio_gfsk_pkt_len_modes_t; + +/*! + * @brief GFSK Preamble Detector Length configurations + * + * This parameter sets the minimum length of preamble bits to be received to continue reception of incoming packet. If a + * packet with preamble length lower than this value is being received, the reception stops without generating IRQ. + * + * This parameter has no impact on TX operations. + */ +typedef enum +{ + LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_OFF = 0x00, + LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_8BITS = 0x04, + LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_16BITS = 0x05, + LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_24BITS = 0x06, + LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_32BITS = 0x07 +} lr11xx_radio_gfsk_preamble_detector_t; + +/*! + * @brief LoRa Cyclic Redundancy Check configurations + */ +typedef enum +{ + LR11XX_RADIO_LORA_CRC_OFF = 0x00, //!< CRC deactivated + LR11XX_RADIO_LORA_CRC_ON = 0x01, //!< CRC activated +} lr11xx_radio_lora_crc_t; + +/*! + * @brief LoRa Header type configurations + */ +typedef enum +{ + LR11XX_RADIO_LORA_PKT_EXPLICIT = 0x00, //!< Explicit header: transmitted over the air + LR11XX_RADIO_LORA_PKT_IMPLICIT = 0x01, //!< Implicit header: not transmitted over the air +} lr11xx_radio_lora_pkt_len_modes_t; + +/*! + * @brief LoRa IQ mode configurations + * + * LoRa IQ modes are mutually exclusives: a physical packet sent with standard IQ will not be received by a receiver + * configured with inverted IQ. + */ +typedef enum +{ + LR11XX_RADIO_LORA_IQ_STANDARD = 0x00, //!< IQ standard + LR11XX_RADIO_LORA_IQ_INVERTED = 0x01, //!< IQ inverted +} lr11xx_radio_lora_iq_t; + +/*! + * @brief Packet type values + */ +typedef enum +{ + LR11XX_RADIO_PKT_NONE = 0x00, //!< State after cold start, Wi-Fi or GNSS capture + LR11XX_RADIO_PKT_TYPE_GFSK = 0x01, //!< GFSK modulation + LR11XX_RADIO_PKT_TYPE_LORA = 0x02, //!< LoRa modulation + LR11XX_RADIO_PKT_TYPE_BPSK = 0x03, //!< BPSK modulation + LR11XX_RADIO_PKT_TYPE_LR_FHSS = 0x04, //!< LR-FHSS modulation + LR11XX_RADIO_PKT_TYPE_RTTOF = 0x05, //!< RTToF (Ranging) packet +} lr11xx_radio_pkt_type_t; + +/*! + * @brief Select power amplifier supply source + */ +typedef enum +{ + LR11XX_RADIO_PA_REG_SUPPLY_VREG = 0x00, //!< Power amplifier supplied by the main regulator + LR11XX_RADIO_PA_REG_SUPPLY_VBAT = 0x01 //!< Power amplifier supplied by the battery +} lr11xx_radio_pa_reg_supply_t; + +/*! + * @brief RX Duty Cycle Modes + */ +typedef enum +{ + LR11XX_RADIO_RX_DUTY_CYCLE_MODE_RX = 0x00, //!< LoRa/GFSK: Uses Rx for listening to packets + LR11XX_RADIO_RX_DUTY_CYCLE_MODE_CAD = 0x01, //!< Only in LoRa: Uses CAD to listen for over-the-air activity +} lr11xx_radio_rx_duty_cycle_mode_t; + +/*! + * @brief GFSK Bandwidth configurations + */ +typedef enum +{ + LR11XX_RADIO_GFSK_BW_4800 = 0x1F, //!< Bandwidth 4.8 kHz DSB + LR11XX_RADIO_GFSK_BW_5800 = 0x17, //!< Bandwidth 5.8 kHz DSB + LR11XX_RADIO_GFSK_BW_7300 = 0x0F, //!< Bandwidth 7.3 kHz DSB + LR11XX_RADIO_GFSK_BW_9700 = 0x1E, //!< Bandwidth 9.7 kHz DSB + LR11XX_RADIO_GFSK_BW_11700 = 0x16, //!< Bandwidth 11.7 kHz DSB + LR11XX_RADIO_GFSK_BW_14600 = 0x0E, //!< Bandwidth 14.6 kHz DSB + LR11XX_RADIO_GFSK_BW_19500 = 0x1D, //!< Bandwidth 19.5 kHz DSB + LR11XX_RADIO_GFSK_BW_23400 = 0x15, //!< Bandwidth 23.4 kHz DSB + LR11XX_RADIO_GFSK_BW_29300 = 0x0D, //!< Bandwidth 29.3 kHz DSB + LR11XX_RADIO_GFSK_BW_39000 = 0x1C, //!< Bandwidth 39.0 kHz DSB + LR11XX_RADIO_GFSK_BW_46900 = 0x14, //!< Bandwidth 46.9 kHz DSB + LR11XX_RADIO_GFSK_BW_58600 = 0x0C, //!< Bandwidth 58.6 kHz DSB + LR11XX_RADIO_GFSK_BW_78200 = 0x1B, //!< Bandwidth 78.2 kHz DSB + LR11XX_RADIO_GFSK_BW_93800 = 0x13, //!< Bandwidth 93.8 kHz DSB + LR11XX_RADIO_GFSK_BW_117300 = 0x0B, //!< Bandwidth 117.3 kHz DSB + LR11XX_RADIO_GFSK_BW_156200 = 0x1A, //!< Bandwidth 156.2 kHz DSB + LR11XX_RADIO_GFSK_BW_187200 = 0x12, //!< Bandwidth 187.2 kHz DSB + LR11XX_RADIO_GFSK_BW_234300 = 0x0A, //!< Bandwidth 232.3 kHz DSB + LR11XX_RADIO_GFSK_BW_312000 = 0x19, //!< Bandwidth 312.0 kHz DSB + LR11XX_RADIO_GFSK_BW_373600 = 0x11, //!< Bandwidth 373.6 kHz DSB + LR11XX_RADIO_GFSK_BW_467000 = 0x09 //!< Bandwidth 467.0 kHz DSB +} lr11xx_radio_gfsk_bw_t; + +/*! + * @brief Possible automatic actions when Channel Activity Detection operations terminate + * + * For RADIO_EXIT_MODE_CAD_RX, LR11XX enters RX mode on activity detected. The timeout value for this RX operation is + * defined as: + * + * \f$ 31.25us \times timeout \f$ + * + * With \f$ timeout \f$ defined in RadioCadParams_t::timeout + * + * If the CAD operation is negative with RADIO_CAD_EXIT_MODE_RX or if CAD operation is positive with + * RADIO_CAD_EXIT_MODE_TX, therefore the LR11XX enters Standby RC mode. + */ +typedef enum +{ + LR11XX_RADIO_CAD_EXIT_MODE_STANDBYRC = 0x00, //!< Enter standby RC mode after CAD operation + LR11XX_RADIO_CAD_EXIT_MODE_RX = 0x01, //!< Enter in RX mode if an activity is detected + LR11XX_RADIO_CAD_EXIT_MODE_TX = 0x10, //!< Enter in TX mode if no activity is detected +} lr11xx_radio_cad_exit_mode_t; + +/*! + * @brief Pulse shape configurations + */ +typedef enum +{ + LR11XX_RADIO_GFSK_PULSE_SHAPE_OFF = 0x00, //!< No filter applied + LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_03 = 0x08, //!< Gaussian BT 0.3 + LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_05 = 0x09, //!< Gaussian BT 0.5 + LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_07 = 0x0A, //!< Gaussian BT 0.7 + LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_1 = 0x0B //!< Gaussian BT 1.0 +} lr11xx_radio_gfsk_pulse_shape_t; + +/*! + * @brief BPSK pulse shape configurations + */ +typedef enum +{ + LR11XX_RADIO_DBPSK_PULSE_SHAPE = 0x16, //!< Double OSR / RRC / BT 0.7 +} lr11xx_radio_bpsk_pulse_shape_t; + +/*! + * @brief LR-FHSS bitrate configurations + */ +typedef enum +{ + LR11XX_RADIO_LR_FHSS_BITRATE_488_BPS = + ( int ) ( LR11XX_RADIO_LR_FHSS_BITRATE_DIVIDE_BY_256 + + LR11XX_RADIO_LR_FHSS_BITRATE_IN_256_BPS_STEPS ), //!< 488.28215 bps +} lr11xx_radio_lr_fhss_bitrate_t; + +/*! + * @brief LR-FHSS pulse shape configurations + */ +typedef enum +{ + LR11XX_RADIO_LR_FHSS_PULSE_SHAPE_BT_1 = 0x0B //!< Gaussian BT 1.0 +} lr11xx_radio_lr_fhss_pulse_shape_t; + +/*! + * @brief Channel Activity Detection parameters + * + * Parameters detPeak and detMin are to be used for tuning the sensitivity of Channel Activity Detection. It depends on + * Spreading Factor, Bandwidth and symbolNum. + * + * For detPeak, the 5 MSBits are encoding the integer part, the 3 LSBits are encoding 1/8 of the decimal part. For + * instance, \f$detPeak = 50\f$ (= 0x32) leads to a ratio being \f$6 + 2 * 1/8 = 6.25\f$. + * + * detMin is unit free and represents the ratio between the minimal power of a correlation peak and measurement gain + * that can be considered as a peak detection. It helps to avoid detection on noise. Authorized values a from 0 to 181. + */ +typedef struct lr11xx_radio_cad_params_s +{ + uint8_t cad_symb_nb; //!< Number of symbols used for CAD detection + uint8_t cad_detect_peak; //!< Ratio for CAD between correlator peak and average + //!< (Default 0x32) + uint8_t cad_detect_min; //!< Minimum power of the correlation peak to be + //!< considered as a positive CAD (Default 0x0A) + lr11xx_radio_cad_exit_mode_t cad_exit_mode; //!< Automated action on CAD completion + uint32_t cad_timeout; //!< Value used to compute timeout +} lr11xx_radio_cad_params_t; + +/*! + * @brief Status of GFSK received packet + */ +typedef struct lr11xx_radio_pkt_status_gfsk_s +{ + int8_t rssi_sync_in_dbm; //!< RSSI value latched on detection of the last received packet Sync Address + int8_t rssi_avg_in_dbm; //!< RSSI averaged over the payload of the last received packet + uint8_t rx_len_in_bytes; //!< Length of the last received packet [Bytes] + bool is_addr_err; //!< Address filtering status. Asserted if received packet address does not match node address + //!< nor broadcast address + bool is_crc_err; //!< CRC status of the current packet (applicable only in RX, with CRC enabled) + bool is_len_err; //!< Asserted when the length of last received packet is greater than the maximal length + //!< (applicable only in RX with variable length packet) + bool is_abort_err; //!< Asserted when the current packet has been aborted (applicable in RX and TX) + bool is_received; //!< Asserted when packet reception is done (applicable in RX) + bool is_sent; //!< Asserted when packet transmission is done (applicable in TX) +} lr11xx_radio_pkt_status_gfsk_t; + +/*! + * @brief Status of received packet + */ +typedef struct lr11xx_radio_pkt_status_lora_s +{ + int8_t rssi_pkt_in_dbm; //!< Average RSSI over last received packet. + int8_t snr_pkt_in_db; //!< SNR estimated on last received packet. + int8_t signal_rssi_pkt_in_dbm; //!< RSSI of last packet latched after +} lr11xx_radio_pkt_status_lora_t; + +/*! + * @brief Length and offset of received packet + */ +typedef struct lr11xx_radio_rx_buffer_status_s +{ + uint8_t pld_len_in_bytes; //!< Length of received packet [Bytes] + uint8_t buffer_start_pointer; //!< Offset in the reception buffer of + //!< first byte received [Bytes] +} lr11xx_radio_rx_buffer_status_t; + +/*! + * @brief GFSK packet statistic structure + */ +typedef struct lr11xx_radio_stats_gfsk_s +{ + uint16_t nb_pkt_received; //!< Total number of received packets + uint16_t nb_pkt_crc_error; //!< Total number of received packets with CRC error + uint16_t nb_pkt_len_error; //!< Total number of received packets with a length error +} lr11xx_radio_stats_gfsk_t; + +/*! + * @brief LoRa packet statistic structure + */ +typedef struct lr11xx_radio_stats_lora_s +{ + uint16_t nb_pkt_received; //!< Total number of received packets + uint16_t nb_pkt_crc_error; //!< Total number of received packets with CRC error + uint16_t nb_pkt_header_error; //!< Total number of packets with header error + uint16_t nb_pkt_falsesync; //!< Total number of false sync +} lr11xx_radio_stats_lora_t; + +/*! + * @brief Modulation configuration for GFSK packet + */ +typedef struct lr11xx_radio_mod_params_gfsk_s +{ + uint32_t br_in_bps; //!< GFSK bitrate [bit/s] + lr11xx_radio_gfsk_pulse_shape_t pulse_shape; //!< GFSK pulse shape + lr11xx_radio_gfsk_bw_t bw_dsb_param; //!< GFSK bandwidth + uint32_t fdev_in_hz; //!< GFSK frequency deviation [Hz] +} lr11xx_radio_mod_params_gfsk_t; + +/*! + * @brief Modulation configuration for BPSK packet + */ +typedef struct lr11xx_radio_mod_params_bpsk_s +{ + uint32_t br_in_bps; //!< BPSK bitrate [bit/s] + lr11xx_radio_bpsk_pulse_shape_t pulse_shape; //!< BPSK pulse shape +} lr11xx_radio_mod_params_bpsk_t; + +/*! + * @brief Modulation configuration for LoRa packet + */ +typedef struct lr11xx_radio_mod_params_lora_s +{ + lr11xx_radio_lora_sf_t sf; //!< LoRa spreading factor + lr11xx_radio_lora_bw_t bw; //!< LoRa bandwidth + lr11xx_radio_lora_cr_t cr; //!< LoRa coding rate + uint8_t ldro; //!< LoRa LDRO +} lr11xx_radio_mod_params_lora_t; + +/*! + * @brief Modulation configuration for LR-FHSS packets + */ +typedef struct lr11xx_radio_mod_params_lr_fhss_s +{ + lr11xx_radio_lr_fhss_bitrate_t br_in_bps; //!< LR-FHSS bitrate + lr11xx_radio_lr_fhss_pulse_shape_t pulse_shape; //!< LR-FHSS pulse shape +} lr11xx_radio_mod_params_lr_fhss_t; + +/*! + * @brief Packet parameter configuration for GFSK packets + */ +typedef struct lr11xx_radio_pkt_params_gfsk_s +{ + uint16_t preamble_len_in_bits; //!< GFSK Preamble length [bits] + lr11xx_radio_gfsk_preamble_detector_t preamble_detector; //!< GFSK Preamble detection configuration + uint8_t sync_word_len_in_bits; //!< GFSK Syncword length [bits] + lr11xx_radio_gfsk_address_filtering_t address_filtering; //!< GFSK Address filtering/comparison configuration + lr11xx_radio_gfsk_pkt_len_modes_t header_type; //!< GFSK Header type configuration + uint8_t pld_len_in_bytes; //!< GFSK Payload length [bytes] + lr11xx_radio_gfsk_crc_type_t crc_type; //!< GFSK CRC configuration + lr11xx_radio_gfsk_dc_free_t dc_free; //!< GFSK Whitening configuration +} lr11xx_radio_pkt_params_gfsk_t; + +/*! + * @brief Packet parameter configuration for BPSK packets + */ +typedef struct lr11xx_radio_pkt_params_bpsk_s +{ + uint8_t pld_len_in_bytes; //!< Payload length [bytes] + uint16_t ramp_up_delay; //!< Delay to fine tune ramp-up time, if non-zero + uint16_t ramp_down_delay; //!< Delay to fine tune ramp-down time, if non-zero + uint16_t pld_len_in_bits; //!< If non-zero, used to ramp down PA before end of a payload with length that is not a + //!< multiple of 8. pld_len_in_bits <= pld_len_in_bytes * 8 +} lr11xx_radio_pkt_params_bpsk_t; + +/*! + * @brief Packet parameter configuration for LoRa packets + */ +typedef struct lr11xx_radio_pkt_params_lora_s +{ + uint16_t preamble_len_in_symb; //!< LoRa Preamble length [symbols] + lr11xx_radio_lora_pkt_len_modes_t header_type; //!< LoRa Header type configuration + uint8_t pld_len_in_bytes; //!< LoRa Payload length [bytes] + lr11xx_radio_lora_crc_t crc; //!< LoRa CRC configuration + lr11xx_radio_lora_iq_t iq; //!< LoRa IQ configuration +} lr11xx_radio_pkt_params_lora_t; + +/*! + * @brief Configuration of Power Amplifier + * + * @ref pa_duty_cycle controls the duty cycle of Power Amplifier according to: + * \f$ dutycycle = 0.2 + 0.04 \times pa_duty_cycle \f$ + * It can be used to adapt the TX multi-band operation using a single-matching network. + * + * The allowed duty cycle values for LPA are from 0.2 to 0.48 (by step of 0.04). Therefore possible values for + * pa_duty_cycle go from 0 to 7. + * + * The allowed duty cycle values for HPA go from 0.2 to 0.36 (by step of 0.04). Therefore in this case, the possible + * values for pa_duty_cycle go from 0 to 4. + * + * @ref pa_hp_sel controls the number of slices for HPA according to: \f$ \#slices = pa_hp_sel + 1 \f$ + */ +typedef struct lr11xx_radio_pa_cfg_s +{ + lr11xx_radio_pa_selection_t pa_sel; //!< Power Amplifier selection + lr11xx_radio_pa_reg_supply_t pa_reg_supply; //!< Power Amplifier regulator supply source + uint8_t pa_duty_cycle; //!< Power Amplifier duty cycle (Default 0x04) + uint8_t pa_hp_sel; //!< Number of slices for HPA (Default 0x07) +} lr11xx_radio_pa_cfg_t; + +/*! + * @brief RSSI calibration table + */ +typedef struct lr11xx_radio_rssi_calibration_table_s +{ + struct + { + uint8_t g4; + uint8_t g5; + uint8_t g6; + uint8_t g7; + uint8_t g8; + uint8_t g9; + uint8_t g10; + uint8_t g11; + uint8_t g12; + uint8_t g13; + uint8_t g13hp1; + uint8_t g13hp2; + uint8_t g13hp3; + uint8_t g13hp4; + uint8_t g13hp5; + uint8_t g13hp6; + uint8_t g13hp7; + } gain_tune; //!< Used to set gain tune value for RSSI calibration + + int16_t gain_offset; //!< Used to set gain offset value for RSSI calibration +} lr11xx_radio_rssi_calibration_table_t; + +/*! + * @brief Values to use to setup LNA LF0 configuration + * + * LNA can be configured in either of the 3 modes: Single N, Single P or differential (which is default) + * + */ +typedef enum +{ + LR11XX_RADIO_LNA_MODE_SINGLE_RFI_N_LF0 = 1, //!< Use only RFI_N_LF0 antenna + LR11XX_RADIO_LNA_MODE_SINGLE_RFI_P_LF0 = 2, //!< Use only RFI_P_LF0 antenna + LR11XX_RADIO_LNA_MODE_DIFFERENTIAL_LF0 = 3 //!< Configure LNA LF0 in differential mode (default) +} lr11xx_radio_lna_mode_t; +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_RADIO_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_regmem.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_regmem.h new file mode 100755 index 0000000..866a65e --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_regmem.h @@ -0,0 +1,207 @@ +/*! + * @file lr11xx_regmem.h + * + * @brief Register/memory driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_REGMEM_H +#define LR11XX_REGMEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/*! + * @brief Maximum number of words that can be written to / read from a LR11XX chip with regmem32 commands + */ +#define LR11XX_REGMEM_MAX_WRITE_READ_WORDS 64 + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Write up to 64 words into register memory space of LR11XX. + * + * A word is 32-bit long. The writing operations write contiguously in register memory, starting at the address + * provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start writing operation + * @param [in] data The buffer of words to write into memory. Its size must be enough to contain length words. + * @param [in] length Number of words to write into memory + * + * @returns Operation status + * + * @see lr11xx_regmem_read_regmem32 + */ +lr11xx_status_t lr11xx_regmem_write_regmem32( const void* context, const uint32_t address, const uint32_t* buffer, + const uint8_t length ); + +/*! + * @brief Read up to 64 words into register memory space of LR11XX. + * + * A word is 32-bit long. The reading operations read contiguously from register memory, starting at the address + * provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start reading operation + * @param [in] length Number of words to read from memory + * @param [out] buffer Pointer to a words array to be filled with content from memory. Its size must be enough to + * contain at least length words. + * + * @returns Operation status + * + * @see lr11xx_regmem_write_regmem32 + */ +lr11xx_status_t lr11xx_regmem_read_regmem32( const void* context, const uint32_t address, uint32_t* buffer, + const uint8_t length ); + +/*! + * @brief Write bytes into register memory space of LR11XX. + * + * A byte is 8-bit long. The writing operations write contiguously in register memory, starting at the address provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start writing operation + * @param [in] data The buffer of bytes to write into memory. Its size must be enough to contain length bytes + * @param [in] length Number of bytes to write into memory + * + * @returns Operation status + * + * @see lr11xx_regmem_read_mem8 + */ +lr11xx_status_t lr11xx_regmem_write_mem8( const void* context, const uint32_t address, const uint8_t* buffer, + const uint8_t length ); + +/*! + * @brief Read bytes into register memory space of LR11XX. + * + * A byte is 8-bit long. The reading operations read contiguously from register memory, starting at the address + * provided. + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to start reading operation + * @param [in] length Number of bytes to read from memory + * @param [in] buffer Pointer to a byte array to be filled with content from memory. Its size must be enough to contain + * at least length bytes + * + * @returns Operation status + * + * @see lr11xx_regmem_write_mem8 + */ +lr11xx_status_t lr11xx_regmem_read_mem8( const void* context, const uint32_t address, uint8_t* buffer, + const uint8_t length ); + +/*! + * @brief Write bytes into radio TX buffer memory space of LR11XX. + * + * @param [in] context Chip implementation context + * @param [in] data The buffer of bytes to write into radio buffer. Its size must be enough to contain length bytes + * @param [in] length Number of bytes to write into radio buffer + * + * @returns Operation status + * + * @see lr11xx_regmem_read_buffer8 + */ +lr11xx_status_t lr11xx_regmem_write_buffer8( const void* context, const uint8_t* buffer, const uint8_t length ); + +/*! + * @brief Read bytes from radio RX buffer memory space of LR11XX. + * + * @param [in] context Chip implementation context + * @param [in] offset Memory offset to start reading + * @param [in] length Number of bytes to read from radio buffer + * @param [in] data Pointer to a byte array to be filled with content from radio buffer. Its size must be enough to + * contain at least length bytes + * + * @returns Operation status + * + * @see lr11xx_regmem_write_buffer8 + */ +lr11xx_status_t lr11xx_regmem_read_buffer8( const void* context, uint8_t* buffer, const uint8_t offset, + const uint8_t length ); + +/*! + * @brief Clear radio RX buffer + * + * Set to 0x00 all content of the radio RX buffer + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_regmem_clear_rxbuffer( const void* context ); + +/*! + * @brief Read-modify-write data at given register/memory address + * + * @param [in] context Chip implementation context + * @param [in] address The register memory address to be modified + * @param [in] mask The mask to be applied on read data + * @param [in] data The data to be written + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_regmem_write_regmem32_mask( const void* context, const uint32_t address, const uint32_t mask, + const uint32_t data ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_REGMEM_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_rttof.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_rttof.h new file mode 100755 index 0000000..535c721 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_rttof.h @@ -0,0 +1,201 @@ +/** + * @file lr11xx_rttof.h + * + * @brief Round-Trip Time of Flight (RTToF) driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_RTTOF_H +#define LR11XX_RTTOF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr11xx_rttof_types.h" +#include "lr11xx_radio_types.h" +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/** + * @brief Length in byte of the RTToF result + */ +#define LR11XX_RTTOF_RESULT_LENGTH ( 4 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTION PROTOTYPES --------------------------------------------- + */ + +/** + * @brief Set the RTToF address for this subordinate device. + * + * @param [in] context Chip implementation context + * @param [in] address 32-bit subordinate address (default is 0x00000019) + * @param [in] check_length Number of bytes to be checked when comparing the device's + * address with request address value contained in received + * RTToF frames (valid range 1..4, default is 4) + * + * @returns Operation status + * + * @note The address set by this function is only used in subordinate mode, that is, + * when receiving RTToF requests. While processing received request packets, + * the RTToF subordinate compares @p check_length bytes (LSB first) of + * the request address with its own address. Packets with non-matching request + * addresses are discarded. + */ +lr11xx_status_t lr11xx_rttof_set_address( const void* context, const uint32_t address, const uint8_t check_length ); + +/** + * @brief Set the RTToF address used for requests sent in manager mode. + * + * @param [in] context Chip implementation context + * @param [in] request_address 32-bit request address (default is 0x00000019) + * + * @returns Operation status + * + * @note The request address set by this function is only used in manager mode, + * that is, when sending RTToF requests. The @p request_address is copied + * into the corresponding field in the next RTToF request sent. + */ +lr11xx_status_t lr11xx_rttof_set_request_address( const void* context, const uint32_t request_address ); + +/** + * @brief Set the transceiver RX/TX delay indicator to be compensated during RTToF. + * + * The transceiver hardware induces a delay depending on the physical layer + * configuration (bandwidth, spreading factor). To achieve the desired RTToF + * accuracy, this delay needs to be compensated by a calibration value. + * + * @param [in] context Chip implementation context + * @param [in] delay_indicator Delay value corresponding to the used bandwidth and spreading factor + * + * @returns lr11xx_status_t Operation status + * + * @note The same delay_indicator value needs to be configured in both manager and subordinate devices. + */ +lr11xx_status_t lr11xx_rttof_set_rx_tx_delay_indicator( const void* context, const uint32_t delay_indicator ); + +/** + * @brief Configure RTToF specific parameters. + * + * It is recommended to always call this command when configuring the RTToF operation with @p nb_symbols = 15. + * This value balances the RTToF accuracy and power consumption. + * + * @param [in] context Chip implementation context + * @param [in] nb_symbols Number of symbols contained in responses sent by subordinates + * + * @returns lr11xx_status_t Operation status + * + * @note The RTToF parameters need to be configured in both manager and subordinate devices. + */ +lr11xx_status_t lr11xx_rttof_set_parameters( const void* context, const uint8_t nb_symbols ); + +/** + * @brief Get the RTToF result on the manager device. + * + * Retrieve the RTToF result item corresponding to the given item type @p type. + * + * @param [in] context Chip implementation context + * @param [in] type Result item type to be retrieved + * @param [out] result Result data buffer + * + * @returns lr11xx_status_t Operation status + * + * @note This function is only available on devices in manager mode after + * the RTToF is terminated. + */ +lr11xx_status_t lr11xx_rttof_get_raw_result( const void* context, const lr11xx_rttof_result_type_t type, + uint8_t result[LR11XX_RTTOF_RESULT_LENGTH] ); + +/** + * @brief Convert the raw distance result obtained from the device to a distance result [m]. + * + * This function is meaningful only to convert a RTToF result obtained by calling @p lr11xx_rttof_get_raw_result + * with type set to @p LR11XX_RTTOF_RESULT_TYPE_RAW + * + * @param [in] rttof_bw Bandwidth used during RTToF + * @param [in] raw_distance_buf Buffer containing the raw distance result + * + * @returns int32_t Distance result [m] + * + * @see lr11xx_rttof_get_raw_result + * + * @note The caller must ensure that the @p rttof_bw parameter is one of the supported ones, + * i.e., #LR11XX_RADIO_LORA_BW_125, #LR11XX_RADIO_LORA_BW_250, #LR11XX_RADIO_LORA_BW_500. + */ +int32_t lr11xx_rttof_distance_raw_to_meter( lr11xx_radio_lora_bw_t rttof_bw, + const uint8_t raw_distance_buf[LR11XX_RTTOF_RESULT_LENGTH] ); + +/** + * @brief Convert the raw RSSI result obtained from the device to an RSSI result. + * + * This function is meaningful only to convert a RTToF result obtained by calling @p lr11xx_rttof_get_raw_result + * with type set to @p LR11XX_RTTOF_RESULT_TYPE_RSSI + * + * @param [in] raw_rssi_buf Buffer containing the raw RSSI result + * + * @returns int8_t RSSI result [dBm] + * + * @see lr11xx_rttof_get_raw_result + */ +static inline int8_t lr11xx_rttof_rssi_raw_to_value( const uint8_t raw_rssi_buf[LR11XX_RTTOF_RESULT_LENGTH] ) +{ + // Only the last byte is meaningful + return -( int8_t )( raw_rssi_buf[3] >> 1 ); +} + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_RTTOF_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_rttof_types.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_rttof_types.h new file mode 100755 index 0000000..a046a47 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_rttof_types.h @@ -0,0 +1,82 @@ +/** + * @file lr11xx_rttof_types.h + * + * @brief Round-Trip Time of Flight (RTToF) driver types for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_RTTOF_TYPES_H +#define LR11XX_RTTOF_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief Round-Trip Time of Flight result types + */ +typedef enum lr11xx_rttof_result_type_e +{ + LR11XX_RTTOF_RESULT_TYPE_RAW = 0, ///< Raw distance result + LR11XX_RTTOF_RESULT_TYPE_RSSI, ///< RTToF RSSI +} lr11xx_rttof_result_type_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTION PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_RTTOF_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_system.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_system.h new file mode 100755 index 0000000..a4d00f6 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_system.h @@ -0,0 +1,594 @@ +/*! + * @file lr11xx_system.h + * + * @brief System driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_SYSTEM_H +#define LR11XX_SYSTEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_system_types.h" +#include "lr11xx_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/*! + * @brief Frequency step in MHz used to compute the image calibration parameter + * + * @see lr11xx_system_calibrate_image_in_mhz + */ +#define LR11XX_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ 4 + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Reset the radio + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_reset( const void* context ); + +/** + * @brief Wake the radio up from sleep mode. + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_wakeup( const void* context ); + +/** + * @brief Abort a blocking command. + * + * @param [in] context Chip implementation context. + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_abort_blocking_cmd( const void* context ); + +/*! + * @brief Return stat1, stat2, and irq_status + * + * @param [in] context Chip implementation context + * @param [out] stat1 Pointer to a variable for holding stat1. Can be NULL. + * @param [out] stat2 Pointer to a variable for holding stat2. Can be NULL. + * @param [out] irq_status Pointer to a variable for holding irq_status. Can be NULL. + * + * @returns Operation status + * + * @remark To simplify system integration, this function does not actually execute the GetStatus command, which would + * require bidirectional SPI communication. It obtains the stat1, stat2, and irq_status values by performing an ordinary + * SPI read (which is required to send null/NOP bytes on the MOSI line). This is possible since the LR11XX returns these + * values automatically whenever a read that does not directly follow a response-carrying command is performed. + * Unlike with the GetStatus command, however, the reset status information is NOT cleared by this command. The function + * @ref lr11xx_system_clear_reset_status_info may be used for this purpose when necessary. + */ +lr11xx_status_t lr11xx_system_get_status( const void* context, lr11xx_system_stat1_t* stat1, + lr11xx_system_stat2_t* stat2, lr11xx_system_irq_mask_t* irq_status ); + +/*! + * @brief Clear the reset status information stored in stat2 + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_clear_reset_status_info( const void* context ); + +/*! + * @brief Return irq_status + * + * @param [in] context Chip implementation context + * @param [out] irq_status irq_status status variable + * + * @returns Operation status + */ +static inline lr11xx_status_t lr11xx_system_get_irq_status( const void* context, lr11xx_system_irq_mask_t* irq_status ) +{ + return lr11xx_system_get_status( context, 0, 0, irq_status ); +} + +/*! + * @brief Return the version of the system (hardware and software) + * + * @param [in] context Chip implementation context + * @param [out] version Pointer to the structure holding the system version + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_get_version( const void* context, lr11xx_system_version_t* version ); + +/*! + * @brief Return the system errors + * + * Errors may be fixed following: + * - calibration error can be fixed by attempting another RC calibration; + * - XOsc related errors may be due to hardware problems, can be fixed by reset; + * - PLL lock related errors can be due to not-locked PLL, or by attempting to use an out-of-band frequency, can be + * fixed by executing a PLL calibration, or by using other frequencies. + * + * @param [in] context Chip implementation context + * @param [out] errors Pointer to a value holding error flags + * + * @returns Operation status + * + * @see lr11xx_system_calibrate, lr11xx_system_calibrate_image, lr11xx_system_clear_errors + */ +lr11xx_status_t lr11xx_system_get_errors( const void* context, uint16_t* errors ); + +/*! + * @brief Clear all error flags pending. + * + * This function cannot be used to clear flags individually. + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr11xx_system_get_errors + */ +lr11xx_status_t lr11xx_system_clear_errors( const void* context ); + +/*! + * @brief lr11xx_system_calibrate the requested blocks + * + * This function can be called in any mode of the chip. + * + * The chip will return to standby RC mode on exit. Potential calibration issues can be read out with + * lr11xx_system_get_errors command. + * + * @param [in] context Chip implementation context + * @param [in] calib_param Structure holding the reference to blocks to be calibrated + * + * @returns Operation status + * + * @see lr11xx_system_get_errors + */ +lr11xx_status_t lr11xx_system_calibrate( const void* context, const uint8_t calib_param ); + +/*! + * @brief Configure the regulator mode to be used in specific modes + * + * This function shall only be called in standby RC mode. + * + * The reg_mode parameter defines if the DC-DC converter is switched on in the following modes: STANDBY XOSC, FS, RX, TX + * and RX_CAPTURE. + * + * @param [in] context Chip implementation context + * @param [in] reg_mode Regulator mode configuration + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_set_reg_mode( const void* context, const lr11xx_system_reg_mode_t reg_mode ); + +/*! + * @brief Launch an image calibration valid for all frequencies inside an interval, in steps + * + * This function can be called in any mode of the chip. + * + * The chip will return to standby RC mode on exit. Potential calibration issues can be read out with + * lr11xx_system_get_errors command. + * + * The frequencies given in parameters are defined in 4MHz step (Eg. 900MHz corresponds to 0xE1). If freq1 = freq2, only + * one calibration is performed. + * + * @param [in] context Chip implementation context + * @param [in] freq1 Image calibration interval lower bound, in steps + * @param [in] freq2 Image calibration interval upper bound, in steps + * + * @remark freq1 must be less than or equal to freq2 + * + * @returns Operation status + * + * @see lr11xx_system_get_errors + */ +lr11xx_status_t lr11xx_system_calibrate_image( const void* context, const uint8_t freq1, const uint8_t freq2 ); + +/*! + * @brief Launch an image calibration valid for all frequencies inside an interval, in MHz + * + * @remark This function relies on @ref lr11xx_system_calibrate_image + * + * @param [in] context Chip implementation context + * @param [in] freq1_in_mhz Image calibration interval lower bound, in MHz + * @param [in] freq2_in_mhz Image calibration interval upper bound, in MHz + * + * @remark freq1 must be less than or equal to freq2 + * + * @returns Operation status + * + * @see lr11xx_system_calibrate_image + */ +lr11xx_status_t lr11xx_system_calibrate_image_in_mhz( const void* context, const uint16_t freq1_in_mhz, + const uint16_t freq2_in_mhz ); + +/*! + * @brief Set the RF switch configurations for each RF setup + * + * This function shall only be called in standby RC mode. + * + * By default, no DIO is used to control a RF switch. All DIOs are set in High-Z mode. + * + * @param [in] context Chip implementation context + * @param [in] rf_switch_cfg Pointer to a structure that holds the switches configuration + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_set_dio_as_rf_switch( const void* context, + const lr11xx_system_rfswitch_cfg_t* rf_switch_cfg ); + +/*! + * @brief Set which interrupt signals are redirected to the dedicated DIO pin + * + * By default, no interrupt signal is redirected. + * + * The dedicated DIO pin will remain asserted until all redirected interrupt signals are cleared with a call to + * lr11xx_system_clear_irq_status. + * + * @param [in] context Chip implementation context + * @param [in] irqs_to_enable_dio1 Variable that holds the interrupt mask for dio1 + * @param [in] irqs_to_enable_dio2 Variable that holds the interrupt mask for dio2 + * + * @returns Operation status + * + * @see lr11xx_system_clear_irq_status + */ +lr11xx_status_t lr11xx_system_set_dio_irq_params( const void* context, + const lr11xx_system_irq_mask_t irqs_to_enable_dio1, + const lr11xx_system_irq_mask_t irqs_to_enable_dio2 ); + +/*! + * @brief Clear requested bits in the internal pending interrupt register + * + * @param [in] context Chip implementation context + * @param [in] irqs_to_clear Variable that holds the interrupts to be cleared + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_clear_irq_status( const void* context, const lr11xx_system_irq_mask_t irqs_to_clear ); + +/** + * @brief This helper function clears any radio irq status flags that are set and returns the flags that were cleared. + * + * @param [in] context Chip implementation context. + * @param [out] irq Pointer to a variable for holding the system interrupt status. Can be NULL. + * + * @returns Operation status + * + * @see lr11xx_system_get_irq_status, lr11xx_system_clear_irq_status + */ +lr11xx_status_t lr11xx_system_get_and_clear_irq_status( const void* context, lr11xx_system_irq_mask_t* irq ); + +/*! + * @brief Defines which clock is used as Low Frequency (LF) clock + * + * @param [in] context Chip implementation context + * @param [in] lfclock_cfg Low frequency clock configuration + * @param [in] wait_for_32k_ready Tells the radio if it has to check if 32k source is ready before driving busy low + * + * @returns Operation status + * + * @see lr11xx_system_calibrate, lr11xx_system_calibrate_image + */ +lr11xx_status_t lr11xx_system_cfg_lfclk( const void* context, const lr11xx_system_lfclk_cfg_t lfclock_cfg, + const bool wait_for_32k_ready ); + +/*! + * @brief Enable and configure TCXO supply voltage and detection timeout + * + * This function shall only be called in standby RC mode. + * + * The timeout parameter is the maximum time the firmware waits for the TCXO to be ready. The timeout duration is given + * by: \f$ timeout\_duration\_us = timeout \times 30.52 \f$ + * + * The TCXO mode can be disabled by setting timeout parameter to 0. + * + * @param [in] context Chip implementation context + * @param [in] tune Supply voltage value + * @param [in] timeout Gating time before which the radio starts its Rx / Tx operation + * + * @returns Operation status + * + * @see lr11xx_system_calibrate, lr11xx_system_calibrate_image + */ +lr11xx_status_t lr11xx_system_set_tcxo_mode( const void* context, const lr11xx_system_tcxo_supply_voltage_t tune, + const uint32_t timeout ); + +/*! + * @brief Software reset of the chip. + * + * This function should be used to reboot the chip in a specified mode. Rebooting in flash mode presumes that the + * content in flash memory is not corrupted (i.e. the integrity check performed by the bootloader before executing the + * first instruction in flash is OK). + * + * @param [in] context Chip implementation context + * @param [in] stay_in_bootloader Selector to stay in bootloader or execute flash code after reboot. If true, the + * bootloader will not execute the flash code but activate SPI interface to allow firmware upgrade + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_reboot( const void* context, const bool stay_in_bootloader ); + +/*! + * @brief Returns the value of Vbat + * + * Vbat value (in V) is a function of Vana (typ. 1.35V) using the following + * formula: \f$ Vbat_{V} = (5 \times \frac{Vbat}{255} - 1) \times Vana \f$ + * + * @param [in] context Chip implementation context + * @param [out] vbat A pointer to the Vbat value + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_get_vbat( const void* context, uint8_t* vbat ); + +/*! + * @brief Returns the value of Temp + * + * The temperature (in °C) is a function of Vana (typ. 1.35V), Vbe25 (Vbe voltage @ 25°C, typ. 0.7295V) and VbeSlope + * (typ. -1.7mV/°C) using the following formula: + * \f$ Temperature_{°C} = (\frac{Temp(10:0)}{2047} \times Vana - Vbe25) \times \frac{1000}{VbeSlope} + 25 \f$ + * + * @remark If a TCXO is used, make sure to configure it with @ref lr11xx_system_set_tcxo_mode before calling this + * function + * + * @param [in] context Chip implementation context + * @param [out] temp A pointer to the Temp value + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_get_temp( const void* context, uint16_t* temp ); + +/*! + * @brief Set the device into Sleep or Deep Sleep Mode + * + * The sleep_cfg parameter defines in which sleep mode the device is put and if it wakes up after a given time on the + * RTC event. + * + * The sleep_time parameter is taken into account only when RtcTimeout = 1. It sets the sleep time in number of clock + * cycles: \f$ sleep\_time\_ms = sleep_time \times \frac{1}{32.768} \f$ + * + * @param [in] context Chip implementation context + * @param [in] sleep_cfg Sleep mode configuration + * @param [in] sleep_time Value of the RTC timeout (if RtcTimeout = 1) + * + * @returns Operation status + * + * @see lr11xx_system_set_standby, lr11xx_system_set_fs + */ +lr11xx_status_t lr11xx_system_set_sleep( const void* context, const lr11xx_system_sleep_cfg_t sleep_cfg, + const uint32_t sleep_time ); + +/*! + * @brief Set the device into the requested Standby mode + * + * @param [in] context Chip implementation context + * @param [in] standby_cfg Stand by mode configuration (RC or XOSC) + * + * @returns Operation status + * + * @see lr11xx_system_set_sleep, lr11xx_system_set_fs + */ +lr11xx_status_t lr11xx_system_set_standby( const void* context, const lr11xx_system_standby_cfg_t standby_cfg ); + +/*! + * @brief Set the device into Frequency Synthesis (FS) mode + * + * @param [in] context Chip implementation context + * + * @returns Operation status + * + * @see lr11xx_system_set_standby, lr11xx_system_set_sleep + */ +lr11xx_status_t lr11xx_system_set_fs( const void* context ); + +/*! + * @brief Erase an info page + * + * @param [in] context Chip implementation context + * @param [in] info_page_id Info page to be erased. Only LR11XX_SYSTEM_INFOPAGE_1 is allowed. + * + * @returns Operation status + * + * @see lr11xx_system_write_infopage, lr11xx_system_read_infopage + */ +lr11xx_status_t lr11xx_system_erase_infopage( const void* context, const lr11xx_system_infopage_id_t info_page_id ); + +/*! + * @brief Write data in an info page + * + * @param [in] context Chip implementation context + * @param [in] info_page_id Info page where data are written. Only LR11XX_SYSTEM_INFOPAGE_1 is allowed. + * @param [in] address Address within the info page (aligned on 32-bit data) + * @param [in] data Pointer to the data to write (data buffer shall be - at least - length words long) + * @param [in] length Number of 32-bit data to write (maximum value is 64) + * + * @returns Operation status + * + * @see lr11xx_system_erase_infopage, lr11xx_system_read_infopage + */ +lr11xx_status_t lr11xx_system_write_infopage( const void* context, const lr11xx_system_infopage_id_t info_page_id, + const uint16_t address, const uint32_t* data, const uint8_t length ); + +/*! + * @brief Read data from an info page + * + * It is possible to cross from page 0 to 1 if (address + length >= 512) + * + * @param [in] context Chip implementation context + * @param [in] info_page_id Info page where data are read + * @param [in] address Address within the info page (aligned on 32-bit data) + * @param [out] data Pointer to the data to read (data buffer shall be - at least - length words long) + * @param [in] length Number of 32-bit data to read (maximum value is 64) + * + * @returns Operation status + * + * @see lr11xx_system_erase_infopage, lr11xx_system_write_infopage + */ +lr11xx_status_t lr11xx_system_read_infopage( const void* context, const lr11xx_system_infopage_id_t info_page_id, + const uint16_t address, uint32_t* data, const uint8_t length ); + +/*! + * @brief Read and return the Unique Identifier of the LR11XX + * + * @param [in] context Chip implementation context + * @param [out] unique_identifier The buffer to be filled with the Unique Identifier of the LR11XX. It is up to the + * application to ensure unique_identifier is long enough to hold the unique identifier + * + * @returns Operation status + * + * @see LR11XX_SYSTEM_UID_LENGTH + */ +lr11xx_status_t lr11xx_system_read_uid( const void* context, lr11xx_system_uid_t unique_identifier ); + +/*! + * @brief Read and return the Join EUI of the LR11XX + * + * @param [in] context Chip implementation context + * @param [out] join_eui The buffer to be filled with Join EUI of the LR11XX. It is up to the application to ensure + * join_eui is long enough to hold the join EUI + * + * @returns Operation status + * + * @see LR11XX_SYSTEM_JOIN_EUI_LENGTH + */ +lr11xx_status_t lr11xx_system_read_join_eui( const void* context, lr11xx_system_join_eui_t join_eui ); + +/*! + * @brief Compute and return the PIN of the LR11XX based on factory default EUIs + * + * @remark Calling this command also triggers a derivation of network and application keys (available as @ref + * LR11XX_CRYPTO_KEYS_IDX_NWK_KEY and @ref LR11XX_CRYPTO_KEYS_IDX_APP_KEY) based on factory default EUIs + * + * @param [in] context Chip implementation context + * @param [out] pin The buffer to be filled with PIN of the LR11XX. It is up to the application to ensure pin is long + * enough to hold the PIN + * + * @returns Operation status + * + * @see LR11XX_SYSTEM_PIN_LENGTH + */ +lr11xx_status_t lr11xx_system_read_pin( const void* context, lr11xx_system_pin_t pin ); + +/*! + * @brief Compute and return the PIN of the LR11XX based on EUIs provided as parameters + * + * @remark Calling this command also triggers a derivation of network and application keys (available as @ref + * LR11XX_CRYPTO_KEYS_IDX_NWK_KEY and @ref LR11XX_CRYPTO_KEYS_IDX_APP_KEY) based on EUIs provided as parameters + * + * @param [in] context Chip implementation context + * @param [in] device_eui Custom Device EUI + * @param [in] join_eui Custom Join EUI + * @param [in] rfu Parameter RFU - shall be set to 0x00 + * @param [out] pin The buffer to be filled with PIN of the LR11XX. It is up to the application to ensure pin is long + * enough to hold the PIN + * + * @returns Operation status + * + * @see LR11XX_SYSTEM_PIN_LENGTH + */ +lr11xx_status_t lr11xx_system_read_pin_custom_eui( const void* context, lr11xx_system_uid_t device_eui, + lr11xx_system_join_eui_t join_eui, uint8_t rfu, + lr11xx_system_pin_t pin ); + +/*! + * @brief Read and return a 32-bit random number + * + * @remark Radio operating mode must be set into standby. + * + * @param [in] context Chip implementation context + * @param [out] random_number 32-bit random number + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_get_random_number( const void* context, uint32_t* random_number ); + +/*! + * @brief Enable the CRC on SPI transactions + * + * @remark This command shall always be sent with a CRC (to both enable and disable the feature). The function does not + * take care of this additional byte - which is under the responsibility of the underlying HAL functions + * + * @param [in] context Chip implementation context + * @param [in] enable_crc CRC + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_enable_spi_crc( const void* context, bool enable_crc ); + +/*! + * @brief Configure the GPIO drive in sleep mode + * + * @remark GPIO stands for RF switch and IRQ line DIOs + * + * @note This command is available from firmware version 0x0306 + * + * @param [in] context Chip implementation context + * @param [in] enable_drive GPIO drive configuration (true: enabled / false: disabled) + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_system_drive_dio_in_sleep_mode( const void* context, bool enable_drive ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_SYSTEM_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_system_types.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_system_types.h new file mode 100755 index 0000000..74cef85 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_system_types.h @@ -0,0 +1,349 @@ +/*! + * @file lr11xx_system_types.h + * + * @brief System driver types for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_SYSTEM_TYPES_H +#define LR11XX_SYSTEM_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/*! + * @brief Length in byte of the LR11XX version blob + */ +#define LR11XX_SYSTEM_VERSION_LENGTH ( 4 ) + +/*! + * @brief Length of the LR11XX Unique Identifier in bytes + * + * The LR11XX Unique Identifiers is an 8 byte long buffer + */ +#define LR11XX_SYSTEM_UID_LENGTH ( 8 ) +#define LR11XX_SYSTEM_JOIN_EUI_LENGTH ( 8 ) +#define LR11XX_SYSTEM_PIN_LENGTH ( 4 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief Fixed-length array to store a UID + */ +typedef uint8_t lr11xx_system_uid_t[LR11XX_SYSTEM_UID_LENGTH]; + +/** + * @brief Fixed-length array to store a joinEUI + */ +typedef uint8_t lr11xx_system_join_eui_t[LR11XX_SYSTEM_JOIN_EUI_LENGTH]; + +/** + * @brief Fixed-length array to store a PIN + */ +typedef uint8_t lr11xx_system_pin_t[LR11XX_SYSTEM_PIN_LENGTH]; + +/** + * @brief Type to store system interrupt flags + */ +typedef uint32_t lr11xx_system_irq_mask_t; + +/** + * @brief Interrupt flags + */ +enum lr11xx_system_irq_e +{ + LR11XX_SYSTEM_IRQ_NONE = ( 0 << 0 ), + LR11XX_SYSTEM_IRQ_TX_DONE = ( 1 << 2 ), + LR11XX_SYSTEM_IRQ_RX_DONE = ( 1 << 3 ), + LR11XX_SYSTEM_IRQ_PREAMBLE_DETECTED = ( 1 << 4 ), + LR11XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID = ( 1 << 5 ), + LR11XX_SYSTEM_IRQ_HEADER_ERROR = ( 1 << 6 ), + LR11XX_SYSTEM_IRQ_CRC_ERROR = ( 1 << 7 ), + LR11XX_SYSTEM_IRQ_CAD_DONE = ( 1 << 8 ), + LR11XX_SYSTEM_IRQ_CAD_DETECTED = ( 1 << 9 ), + LR11XX_SYSTEM_IRQ_TIMEOUT = ( 1 << 10 ), + LR11XX_SYSTEM_IRQ_LR_FHSS_INTRA_PKT_HOP = ( 1 << 11 ), + LR11XX_SYSTEM_IRQ_RTTOF_REQ_VALID = ( 1 << 14 ), + LR11XX_SYSTEM_IRQ_RTTOF_REQ_DISCARDED = ( 1 << 15 ), + LR11XX_SYSTEM_IRQ_RTTOF_RESP_DONE = ( 1 << 16 ), + LR11XX_SYSTEM_IRQ_RTTOF_EXCH_VALID = ( 1 << 17 ), + LR11XX_SYSTEM_IRQ_RTTOF_TIMEOUT = ( 1 << 18 ), + LR11XX_SYSTEM_IRQ_GNSS_SCAN_DONE = ( 1 << 19 ), + LR11XX_SYSTEM_IRQ_WIFI_SCAN_DONE = ( 1 << 20 ), + LR11XX_SYSTEM_IRQ_EOL = ( 1 << 21 ), + LR11XX_SYSTEM_IRQ_CMD_ERROR = ( 1 << 22 ), + LR11XX_SYSTEM_IRQ_ERROR = ( 1 << 23 ), + LR11XX_SYSTEM_IRQ_FSK_LEN_ERROR = ( 1 << 24 ), + LR11XX_SYSTEM_IRQ_FSK_ADDR_ERROR = ( 1 << 25 ), + LR11XX_SYSTEM_IRQ_LORA_RX_TIMESTAMP = + ( 1 << 27 ), //!< Available since firmware LR1110 0x0308 / LR1120 0x0102 / LR1121 0x0102 + LR11XX_SYSTEM_IRQ_ALL_MASK = + LR11XX_SYSTEM_IRQ_TX_DONE | LR11XX_SYSTEM_IRQ_RX_DONE | LR11XX_SYSTEM_IRQ_PREAMBLE_DETECTED | + LR11XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID | LR11XX_SYSTEM_IRQ_HEADER_ERROR | LR11XX_SYSTEM_IRQ_CRC_ERROR | + LR11XX_SYSTEM_IRQ_CAD_DONE | LR11XX_SYSTEM_IRQ_CAD_DETECTED | LR11XX_SYSTEM_IRQ_TIMEOUT | + LR11XX_SYSTEM_IRQ_LR_FHSS_INTRA_PKT_HOP | LR11XX_SYSTEM_IRQ_RTTOF_REQ_VALID | + LR11XX_SYSTEM_IRQ_RTTOF_REQ_DISCARDED | LR11XX_SYSTEM_IRQ_RTTOF_RESP_DONE | LR11XX_SYSTEM_IRQ_RTTOF_EXCH_VALID | + LR11XX_SYSTEM_IRQ_RTTOF_TIMEOUT | LR11XX_SYSTEM_IRQ_GNSS_SCAN_DONE | LR11XX_SYSTEM_IRQ_WIFI_SCAN_DONE | + LR11XX_SYSTEM_IRQ_EOL | LR11XX_SYSTEM_IRQ_CMD_ERROR | LR11XX_SYSTEM_IRQ_ERROR | + LR11XX_SYSTEM_IRQ_FSK_LEN_ERROR | LR11XX_SYSTEM_IRQ_FSK_ADDR_ERROR | LR11XX_SYSTEM_IRQ_LORA_RX_TIMESTAMP, +}; + +/** + * @brief Calibration flags + */ +enum lr11xx_system_calibration_e +{ + LR11XX_SYSTEM_CALIB_LF_RC_MASK = ( 1 << 0 ), + LR11XX_SYSTEM_CALIB_HF_RC_MASK = ( 1 << 1 ), + LR11XX_SYSTEM_CALIB_PLL_MASK = ( 1 << 2 ), + LR11XX_SYSTEM_CALIB_ADC_MASK = ( 1 << 3 ), + LR11XX_SYSTEM_CALIB_IMG_MASK = ( 1 << 4 ), + LR11XX_SYSTEM_CALIB_PLL_TX_MASK = ( 1 << 5 ), +}; + +typedef uint8_t lr11xx_system_cal_mask_t; + +/** + * @brief Error flags + */ +enum lr11xx_system_errors_e +{ + LR11XX_SYSTEM_ERRORS_LF_RC_CALIB_MASK = ( 1 << 0 ), + LR11XX_SYSTEM_ERRORS_HF_RC_CALIB_MASK = ( 1 << 1 ), + LR11XX_SYSTEM_ERRORS_ADC_CALIB_MASK = ( 1 << 2 ), + LR11XX_SYSTEM_ERRORS_PLL_CALIB_MASK = ( 1 << 3 ), + LR11XX_SYSTEM_ERRORS_IMG_CALIB_MASK = ( 1 << 4 ), + LR11XX_SYSTEM_ERRORS_HF_XOSC_START_MASK = ( 1 << 5 ), + LR11XX_SYSTEM_ERRORS_LF_XOSC_START_MASK = ( 1 << 6 ), + LR11XX_SYSTEM_ERRORS_PLL_LOCK_MASK = ( 1 << 7 ), +}; + +typedef uint16_t lr11xx_system_errors_t; + +/** + * @brief Chip modes + */ +typedef enum +{ + LR11XX_SYSTEM_CHIP_MODE_SLEEP = 0x00, + LR11XX_SYSTEM_CHIP_MODE_STBY_RC = 0x01, + LR11XX_SYSTEM_CHIP_MODE_STBY_XOSC = 0x02, + LR11XX_SYSTEM_CHIP_MODE_FS = 0x03, + LR11XX_SYSTEM_CHIP_MODE_RX = 0x04, + LR11XX_SYSTEM_CHIP_MODE_TX = 0x05, + LR11XX_SYSTEM_CHIP_MODE_LOC = 0x06, +} lr11xx_system_chip_modes_t; + +/** + * @brief Reset status + */ +typedef enum +{ + LR11XX_SYSTEM_RESET_STATUS_CLEARED = 0x00, + LR11XX_SYSTEM_RESET_STATUS_ANALOG = 0x01, + LR11XX_SYSTEM_RESET_STATUS_EXTERNAL = 0x02, + LR11XX_SYSTEM_RESET_STATUS_SYSTEM = 0x03, + LR11XX_SYSTEM_RESET_STATUS_WATCHDOG = 0x04, + LR11XX_SYSTEM_RESET_STATUS_IOCD_RESTART = 0x05, + LR11XX_SYSTEM_RESET_STATUS_RTC_RESTART = 0x06, +} lr11xx_system_reset_status_t; + +/** + * @brief Command status + */ +typedef enum +{ + LR11XX_SYSTEM_CMD_STATUS_FAIL = 0x00, + LR11XX_SYSTEM_CMD_STATUS_PERR = 0x01, + LR11XX_SYSTEM_CMD_STATUS_OK = 0x02, + LR11XX_SYSTEM_CMD_STATUS_DATA = 0x03, +} lr11xx_system_command_status_t; + +/** + * @brief Low-frequency clock modes + */ +typedef enum +{ + LR11XX_SYSTEM_LFCLK_RC = 0x00, //!< (Default) + LR11XX_SYSTEM_LFCLK_XTAL = 0x01, + LR11XX_SYSTEM_LFCLK_EXT = 0x02 +} lr11xx_system_lfclk_cfg_t; + +/** + * @brief Regulator modes + */ +typedef enum +{ + LR11XX_SYSTEM_REG_MODE_LDO = 0x00, //!< (Default) + LR11XX_SYSTEM_REG_MODE_DCDC = 0x01, +} lr11xx_system_reg_mode_t; + +/** + * @brief Info page ID + */ +typedef enum +{ + LR11XX_SYSTEM_INFOPAGE_0 = 0x00, //!< Info page #0 + LR11XX_SYSTEM_INFOPAGE_1 = 0x01, //!< Info page #1 +} lr11xx_system_infopage_id_t; + +/** + * @brief RF switch configuration pin + */ +enum lr11xx_system_rfswitch_cfg_pin_e +{ + LR11XX_SYSTEM_RFSW0_HIGH = ( 1 << 0 ), + LR11XX_SYSTEM_RFSW1_HIGH = ( 1 << 1 ), + LR11XX_SYSTEM_RFSW2_HIGH = ( 1 << 2 ), + LR11XX_SYSTEM_RFSW3_HIGH = ( 1 << 3 ), + LR11XX_SYSTEM_RFSW4_HIGH = ( 1 << 4 ), +}; + +/** + * @brief RF switch configuration structure definition + */ +typedef struct lr11xx_system_rfswitch_cfg_s +{ + uint8_t enable; + uint8_t standby; + uint8_t rx; + uint8_t tx; + uint8_t tx_hp; + uint8_t tx_hf; + uint8_t gnss; + uint8_t wifi; +} lr11xx_system_rfswitch_cfg_t; + +/** + * @brief Stand by configuration values + */ +typedef enum +{ + LR11XX_SYSTEM_STANDBY_CFG_RC = 0x00, + LR11XX_SYSTEM_STANDBY_CFG_XOSC = 0x01 +} lr11xx_system_standby_cfg_t; + +/** + * @brief TCXO supply voltage values + */ +typedef enum +{ + LR11XX_SYSTEM_TCXO_CTRL_1_6V = 0x00, //!< Supply voltage = 1.6v + LR11XX_SYSTEM_TCXO_CTRL_1_7V = 0x01, //!< Supply voltage = 1.7v + LR11XX_SYSTEM_TCXO_CTRL_1_8V = 0x02, //!< Supply voltage = 1.8v + LR11XX_SYSTEM_TCXO_CTRL_2_2V = 0x03, //!< Supply voltage = 2.2v + LR11XX_SYSTEM_TCXO_CTRL_2_4V = 0x04, //!< Supply voltage = 2.4v + LR11XX_SYSTEM_TCXO_CTRL_2_7V = 0x05, //!< Supply voltage = 2.7v + LR11XX_SYSTEM_TCXO_CTRL_3_0V = 0x06, //!< Supply voltage = 3.0v + LR11XX_SYSTEM_TCXO_CTRL_3_3V = 0x07, //!< Supply voltage = 3.3v +} lr11xx_system_tcxo_supply_voltage_t; + +/** + * @brief Status register 1 structure definition + */ +typedef struct lr11xx_system_stat1_s +{ + lr11xx_system_command_status_t command_status; + bool is_interrupt_active; +} lr11xx_system_stat1_t; + +/** + * @brief Status register 2 structure definition + */ +typedef struct lr11xx_system_stat2_s +{ + lr11xx_system_reset_status_t reset_status; + lr11xx_system_chip_modes_t chip_mode; + bool is_running_from_flash; +} lr11xx_system_stat2_t; + +/** + * @brief Chip type values + */ +typedef enum +{ + LR11XX_SYSTEM_VERSION_TYPE_LR1110 = 0x01, + LR11XX_SYSTEM_VERSION_TYPE_LR1120 = 0x02, + LR11XX_SYSTEM_VERSION_TYPE_LR1121 = 0x03, +} lr11xx_system_version_type_t; + +/** + * @brief Version structure definition + */ +typedef struct lr11xx_system_version_s +{ + uint8_t hw; + lr11xx_system_version_type_t type; + uint16_t fw; +} lr11xx_system_version_t; + +/** + * @brief Sleep configuration structure definition + */ +typedef struct lr11xx_system_sleep_cfg_s +{ + bool is_warm_start; + bool is_rtc_timeout; +} lr11xx_system_sleep_cfg_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_SYSTEM_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_types.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_types.h new file mode 100755 index 0000000..be52d7a --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_types.h @@ -0,0 +1,76 @@ +/*! + * @file lr11xx_types.h + * + * @brief Type definitions for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_TYPES_H +#define LR11XX_TYPES_H + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +#define LR11XX_CMD_LENGTH_MAX ( 512 ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief LR11XX status + */ +typedef enum lr11xx_status_e +{ + LR11XX_STATUS_OK = 0, + LR11XX_STATUS_ERROR = 3, +} lr11xx_status_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#endif // LR11XX_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_wifi.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_wifi.h new file mode 100755 index 0000000..63238e7 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_wifi.h @@ -0,0 +1,628 @@ +/*! + * @file lr11xx_wifi.h + * + * @brief Wi-Fi passive scan driver definition for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_WIFI_H +#define LR11XX_WIFI_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_regmem.h" +#include "lr11xx_wifi_types.h" +#include "lr11xx_types.h" +#include "lr11xx_system_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +#ifndef LR11XX_WIFI_N_RESULTS_MAX_PER_CHUNK +/*! + * @brief The number of results max to fetch per SPI communication with the chip + * + * This macro is used by the internals of the driver to size the internal + * buffers of the driver used in the *read results* functions. + * + * It can be defined externally at compile time, or just before including this file. + * + * Its value can be programmatically obtained at runtime by calling lr11xx_wifi_get_nb_results_max_per_chunk() function. + * + * Its default value is set to the maximum number of results saved by LR11XX chip. + * + * @warning Its value must be in the range [1,32] (inclusive). Defining out of this range leads to undefined behavior. + */ +#define LR11XX_WIFI_N_RESULTS_MAX_PER_CHUNK LR11XX_WIFI_MAX_RESULTS +#endif // LR11XX_WIFI_N_RESULTS_MAX_PER_CHUNK + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +/*! + * @brief Start a Wi-Fi passive scan operation + * + * During the complete passive scan operation, the LR11XX remains busy and cannot receive any commands. Using this + * command **DOES** reset the results already obtained by previous passive scan operations. + * + * The result can be read at the end of the passive scan issuing the command lr11xx_wifi_get_nb_results (to get the + * number of results to read) and lr11xx_wifi_read_basic_complete_results or + * lr11xx_wifi_read_basic_mac_type_channel_results to actually get the result bytes. + * + * @param [in] context Chip implementation context + * @param [in] signal_type The type of Wi-Fi Signals to scan for. If LR11XX_WIFI_TYPE_SCAN_B_G_N is selected, the LR11XX + * already starts by scanning all selected channels for Wi-Fi signals B. Then the LR11XX scans all selected channels for + * Wi-Fi signals G/N. + * @param [in] channels Mask of the Wi-Fi channels to scan + * @param [in] scan_mode Scan mode to execute + * @param [in] max_results The maximal number of results to gather. When this limit is reached, the passive scan + * automatically stop. Range of allowed values is [1:32]. Note that value 0 is forbidden. + * @param [in] nb_scan_per_channel The number of internal scan sequences per channel scanned. Range of accepted values + * is [1:255]. Note that value 0 is forbidden. + * @param [in] timeout_in_ms The maximal duration of a single preamble search. Expressed in ms. Range of allowed values + * is [1:65535]. Note that value 0 is forbidden. + * @param [in] abort_on_timeout If true, the beacon search jumps to next configured Wi-Fi channel (or stop if there is + * no more channel to scan) as soon as a search timeout is encountered + * + * @returns Operation status + * + * @see lr11xx_wifi_read_basic_complete_results, lr11xx_wifi_read_basic_mac_type_channel_results + */ +lr11xx_status_t lr11xx_wifi_scan( const void* context, const lr11xx_wifi_signal_type_scan_t signal_type, + const lr11xx_wifi_channel_mask_t channels, const lr11xx_wifi_mode_t scan_mode, + const uint8_t max_results, const uint8_t nb_scan_per_channel, + const uint16_t timeout_in_ms, const bool abort_on_timeout ); + +/*! + * @brief Start a Wi-Fi passive scan for country codes extraction + * + * This command starts a Wi-Fi passive scan operation for Beacons and Probe Responses on Wi-Fi type B only. It is to be + * used to extract the Country Code fields. + * + * During the passive scan, the results are filtered to keep only single MAC addresses. + * + * @param [in] context Chip implementation context + * @param [in] channels_mask Mask of the Wi-Fi channels to scan + * @param [in] nb_max_results The maximum number of country code to gather. When this limit is reached, the passive scan + * automatically stops. Maximal value is 32 + * @param [in] nb_scan_per_channel Maximal number of scan attempts per channel. Maximal value is 255 + * @param [in] timeout_in_ms The maximal duration of a single beacon search. Expressed in ms. Maximal value is 65535 ms + * @param [in] abort_on_timeout If true, the beacon search jumps to next configured Wi-Fi channel (or stop if there is + * no more channel to scan) as soon as a search timeout is encountered + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_search_country_code( const void* context, const lr11xx_wifi_channel_mask_t channels_mask, + const uint8_t nb_max_results, const uint8_t nb_scan_per_channel, + const uint16_t timeout_in_ms, const bool abort_on_timeout ); + +/*! + * @brief Start a Wi-Fi passive scan operation with duration stop conditions + * + * This passive scan API does not require the number of scan per channel, so + * that it searches for Wi-Fi signals until it finds one, or until the + * exhaustion of timeout_per_scan_ms or timeout_per_channel_ms. + * + * The maximal duration of a scan is determined by the number of channels to scan times the timeout_per_channel_ms + * configured. However, this duration may be exceeded depending on the crystal drift of the clock source and on the + * instant the last Wi-Fi signal is detected by the device. + * Therefore the maximal duration of a Wi-Fi scan with this API is provided by the following equations: + * + * For signal type being `LR11XX_WIFI_TYPE_SCAN_B`, `LR11XX_WIFI_TYPE_SCAN_G` or `LR11XX_WIFI_TYPE_SCAN_N`: + * + * \f$ T_{max} = N_{channel} \times ((1 + Xtal_{precision})timeout\_per\_channel + T_{offset} ) \f$ + * + * \f$ Xtal_{precision} \f$ depends on the crystal used as clock source. + * If the clock source is configured with 32kHz internal RC, then \f$ Xtal_{precision} = 1/100 \f$ + * + * \f$ T_{offset} \f$ depends on the \f$ signal\_type \f$ and the \f$scan\_mode\f$ selected: + * + * - LR11XX_WIFI_TYPE_SCAN_B: + * - if \f$scan\_mode != LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$: 2.31 ms + * - if \f$scan\_mode == LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$: 9.59 ms + * - LR11XX_WIFI_TYPE_SCAN_G: + * - if \f$scan\_mode != LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$: 52.55 ms + * - if \f$scan\_mode == LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$: N/A + * + * For signal type being `LR11XX_WIFI_TYPE_SCAN_B_G_N`: + * + * \f$ T_{max} = 2 \times N_{channel} \times (1 + Xtal_{precision})timeout\_per\_channel + T_{offset} \f$ + * + * \f$ T_{offset} \f$ depends on the \f$scan\_mode\f$ selected: + * - \f$scan\_mode != LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$: 54.86 ms + * - \f$scan\_mode == LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$: 9.59 ms. + * + * @note With \f$scan\_mode != LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$ the T_offset is actually the worst case of + * Wi-Fi type B and Wi-Fi type G/N. Moreover, the Wi-Fi types G and N are scanned within the same steps (it is not two + * different scans). So the T_offset is the addition of 2.31 + 52.55 = 54.86. + * + * @note With \f$scan\_mode == LR11XX\_WIFI\_SCAN\_MODE\_FULL\_BEACON\f$, only Wi-Fi types B can be scanned. So scans + * for Wi-Fi types G/N are silently discarded. Therefore the T_offset is the same as for scan with Wi-Fi type B. + * + * @param [in] context Chip implementation context + * @param [in] signal_type The type of Wi-Fi Signals to scan for. If LR11XX_WIFI_TYPE_SCAN_B_G_N is selected, the LR11XX + * already starts by scanning all selected channels for Wi-Fi signals B. Then the LR11XX scans all selected channels for + * Wi-Fi signals G/N. + * @param [in] channels Mask of the Wi-Fi channels to scan + * @param [in] scan_mode Scan mode to execute + * @param [in] max_results The maximal number of results to gather. When this + * limit is reached, the passive scan automatically stop. Maximal value is 32 + * @param [in] timeout_per_channel_ms The time to spend scanning one channel. Expressed in ms. Value 0 is forbidden and + * will result in the raise of WIFI_SCAN_DONE interrupt, with stat1.command_status being set to + * LR11XX_SYSTEM_CMD_STATUS_PERR + * @param [in] timeout_per_scan_ms The maximal time to spend in preamble detection for each single scan. The time spent + * on preamble search is reset at each new preamble search. If the time spent on preamble search reach this timeout, the + * scan on the current channel stops and start on next channel. If set to 0, the command will keep listening until + * exhaustion of timeout_per_channel_ms or until nb_max_results is reached. Expressed in ms. Range of allowed values is + * [0:65535]. + * + * @returns Operation status + * + * @see lr11xx_wifi_read_basic_results, lr11xx_wifi_read_extended_results + */ +lr11xx_status_t lr11xx_wifi_scan_time_limit( const void* context, const lr11xx_wifi_signal_type_scan_t signal_type, + const lr11xx_wifi_channel_mask_t channels, + const lr11xx_wifi_mode_t scan_mode, const uint8_t max_results, + const uint16_t timeout_per_channel_ms, + const uint16_t timeout_per_scan_ms ); + +/*! + * @brief Start a Wi-Fi passive scan for country codes extraction with duration stop conditions + * + * This command starts a Wi-Fi passive scan operation for Beacons and Probe Responses on Wi-Fi type B only. It is to be + * used to extract the Country Code fields. + * This passive scan API does not require the number of scan per channel, so that it searches for Wi-Fi signals until it + * finds one, or until the exhaustion of timeout_per_scan_ms or timeout_per_channel_ms. + * + * The maximal duration of a scan is determined by the number of channels to scan times the timeout_per_channel_ms + * configured. However, this duration may be exceeded depending on the crystal drift of the clock source and on the + * instant the last Wi-Fi signal is detected by the device. + * Therefore the maximal duration of a Wi-Fi scan with this API is provided by the following equation: + * + * \f$ T_{max} = N_{channel} \times ((1 + Xtal_{precision})timeout\_per\_channel + T_{offset} ) \f$ + * + * \f$ Xtal_{precision} \f$ depends on the crystal used as clock source. + * If the clock source is configured with 32kHz internal RC, then \f$ Xtal_{precision} = 1/100 \f$ + * + * \f$ T_{offset} \f$ is always the same: 9.59 ms. + * + * @param [in] context Chip implementation context + * @param [in] channels_mask Mask of the Wi-Fi channels to scan + * @param [in] nb_max_results The maximum number of country code to gather. When this limit is reached, the passive scan + * automatically stops. Maximal value is 32 + * @param [in] timeout_per_channel_ms The time to spend scanning one channel. Expressed in ms. Value 0 is forbidden and + * will result in the raise of WIFI_SCAN_DONE interrupt, with stat1.command_status being set to + * LR11XX_SYSTEM_CMD_STATUS_PERR + * @param [in] timeout_per_scan_ms The maximal time to spend in preamble detection for each single scan. The time spent + * on preamble search is reset at each new preamble search. If the time spent on preamble search reach this timeout, the + * scan on the current channel stops and start on next channel. If set to 0, the command will keep listening until + * exhaustion of timeout_per_channel_ms or until nb_max_results is reached. Expressed in ms. Range of allowed values is + * [0:65535]. + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_search_country_code_time_limit( const void* context, + const lr11xx_wifi_channel_mask_t channels_mask, + const uint8_t nb_max_results, + const uint16_t timeout_per_channel_ms, + const uint16_t timeout_per_scan_ms ); + +/*! + * @brief Returns the number of results currently available in LR11XX + * + * It can be called before lr11xx_wifi_read_basic_complete_results or lr11xx_wifi_read_basic_mac_type_channel_results to + * know the number of results. + * + * @param [in] context Chip implementation context + * @param [out] nb_results The number of results available in the LR11XX + * + * @returns Operation status + * + * @see lr11xx_wifi_read_basic_complete_results, lr11xx_wifi_read_basic_mac_type_channel_results + */ +lr11xx_status_t lr11xx_wifi_get_nb_results( const void* context, uint8_t* nb_results ); + +/*! + * @brief Read basic complete results + * + * This function can be used to fetch all results in a row, or one after the other. + * It corresponds to result format @ref ::LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE. + * + * An example of usage to fetch all results in a row is: + * \code{.cpp} + * uint8_t nb_results = 0; + * lr11xx_wifi_get_nb_results(&radio, &nb_results); + * lr11xx_wifi_basic_complete_result_t all_results[LR11XX_WIFI_MAX_RESULTS] = {0}; + * lr11xx_wifi_read_basic_complete_results(&radio, 0, nb_results, all_results); + * \endcode + * + * On the other hand, fetching result one after the other: + * \code{.cpp} + * uint8_t nb_results = 0; + * lr11xx_wifi_get_nb_results(&radio, &nb_results); + * lr11xx_wifi_basic_complete_result_t single_results = {0}; + * for(uint8_t index_result = 0; index_result < nb_results; index_result++){ + * lr11xx_wifi_read_basic_complete_results(&radio, index_result, 1, &single_results); + * // Do something with single_results + * } + * \endcode + * + * @remark This result fetching function **MUST** be used only if the scan function call was made with Scan Mode set to + * @ref ::LR11XX_WIFI_SCAN_MODE_BEACON or @ref ::LR11XX_WIFI_SCAN_MODE_BEACON_AND_PKT. + * Refer to @ref lr11xx_wifi_are_scan_mode_result_format_compatible to know which scan mode and result format are + * compatible. + * + * @param [in] radio Radio abstraction + * @param [in] start_result_index Result index from which starting to fetch the results + * @param [in] nb_results Number of results to fetch + * @param [out] results Pointer to an array of result structures to populate. It is up to the caller to ensure this + * array can hold at least nb_results elements. + * + * @returns Operation status + * + * @see lr11xx_wifi_are_scan_mode_result_format_compatible, lr11xx_wifi_read_basic_mac_type_channel_results, + * lr11xx_wifi_read_extended_full_results + */ +lr11xx_status_t lr11xx_wifi_read_basic_complete_results( const void* context, const uint8_t start_result_index, + const uint8_t nb_results, + lr11xx_wifi_basic_complete_result_t* results ); + +/*! + * @brief Read basic MAC, Wi-Fi type and channel results + * + * This function can be used to fetch all results in a row, or one after the other. + * It corresponds to result format @ref ::LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL. + * + * An example of usage to fetch all results in a row is: + * \code{.cpp} + * uint8_t nb_results = 0; + * lr11xx_wifi_get_nb_results(&radio, &nb_results); + * lr11xx_wifi_basic_mac_type_channel_result_t all_results[LR11XX_WIFI_MAX_RESULTS] = {0}; + * lr11xx_wifi_read_basic_mac_type_channel_results(&radio, 0, nb_results, all_results); + * \endcode + * + * On the other hand, fetching result one after the other: + * \code{.cpp} + * uint8_t nb_results = 0; + * lr11xx_wifi_get_nb_results(&radio, &nb_results); + * lr11xx_wifi_basic_mac_type_channel_result_t single_results = {0}; + * for(uint8_t index_result = 0; index_result < nb_results; index_result++){ + * lr11xx_wifi_read_basic_mac_type_channel_results(&radio, index_result, 1, &single_results); + * // Do something with single_results + * } + * \endcode + * + * @remark This result fetching function **MUST** be used only if the scan function call was made with Scan Mode set to + * @ref ::LR11XX_WIFI_SCAN_MODE_BEACON or @ref ::LR11XX_WIFI_SCAN_MODE_BEACON_AND_PKT. + * Refer to @ref lr11xx_wifi_are_scan_mode_result_format_compatible to know which scan mode and result format are + * compatible. + * + * @param [in] radio Radio abstraction + * @param [in] start_result_index Result index from which starting to fetch the results + * @param [in] nb_results Number of results to fetch + * @param [out] results Pointer to an array of result structures to populate. It is up to the caller to ensure this + * array can hold at least nb_results elements. + * + * @returns Operation status + * + * @see lr11xx_wifi_are_scan_mode_result_format_compatible, lr11xx_wifi_read_basic_complete_results, + * lr11xx_wifi_read_extended_full_results + */ +lr11xx_status_t lr11xx_wifi_read_basic_mac_type_channel_results( const void* context, const uint8_t start_result_index, + const uint8_t nb_results, + lr11xx_wifi_basic_mac_type_channel_result_t* results ); + +/*! + * @brief Read extended complete results + * + * This function can be used to fetch all results in a row, or one after the other. + * It corresponds to result format @ref ::LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL. + * + * An example of usage to fetch all results in a row is: + * \code{.cpp} + * uint8_t nb_results = 0; + * lr11xx_wifi_get_nb_results(&radio, &nb_results); + * lr11xx_wifi_extended_full_result_t all_results[LR11XX_WIFI_MAX_RESULTS] = {0}; + * lr11xx_wifi_read_extended_full_results(&radio, 0, nb_results, all_results); + * \endcode + * + * On the other hand, fetching result one after the other: + * \code{.cpp} + * uint8_t nb_results = 0; + * lr11xx_wifi_get_nb_results(&radio, &nb_results); + * lr11xx_wifi_extended_full_result_t single_results = {0}; + * for(uint8_t index_result = 0; index_result < nb_results; index_result++){ + * lr11xx_wifi_read_extended_full_results(&radio, index_result, 1, &single_results); + * // Do something with single_results + * } + * \endcode + * + * @remark This result fetching function **MUST** be used only if the scan function call was made with Scan Mode set to + * @ref ::LR11XX_WIFI_SCAN_MODE_FULL_BEACON. + * Refer to @ref lr11xx_wifi_are_scan_mode_result_format_compatible to know which scan mode and result format are + * compatible. + * + * @param [in] radio Radio abstraction + * @param [in] start_result_index Result index from which starting to fetch the results + * @param [in] nb_results Number of results to fetch + * @param [out] results Pointer to an array of result structures to populate. It is up to the caller to ensure this + * array can hold at least nb_results elements. + * + * @returns Operation status + * + * @see lr11xx_wifi_are_scan_mode_result_format_compatible, lr11xx_wifi_read_basic_complete_results, + * lr11xx_wifi_read_basic_mac_type_channel_results + */ +lr11xx_status_t lr11xx_wifi_read_extended_full_results( const void* radio, const uint8_t start_result_index, + const uint8_t nb_results, + lr11xx_wifi_extended_full_result_t* results ); + +/*! + * @brief Reset the internal counters of cumulative timing + * + * @param [in] context Chip implementation context + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_reset_cumulative_timing( const void* context ); + +/*! + * @brief Read the internal counters of cumulative timing + * + * @param [in] context Chip implementation context + * @param [out] timing A pointer to the cumulative timing structure to populate + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_read_cumulative_timing( const void* context, lr11xx_wifi_cumulative_timings_t* timing ); + +/*! + * @brief Get size of country code search results + * + * @param [in] context Chip implementation context + * @param [out] nb_country_code_results Number of country results to read + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_get_nb_country_code_results( const void* context, uint8_t* nb_country_code_results ); + +/*! + * @brief Read country code results + * + * The total number of country code results to read is obtained from a previous call to + * lr11xx_wifi_get_nb_country_code_results + * + * @param [in] context Chip implementation context + * @param [in] start_result_index The result index to start reading results from + * @param [in] nb_country_results Number of country code results to read + * @param [out] country_code_results An array of lr11xx_wifi_country_code_t to be filled. It is up to the application to + * ensure this array is big enough to hold nb_country_results elements + * + * @returns Operation status + * + * @see lr11xx_wifi_get_nb_country_code_results, lr11xx_wifi_search_country_code + */ +lr11xx_status_t lr11xx_wifi_read_country_code_results( const void* context, const uint8_t start_result_index, + const uint8_t nb_country_results, + lr11xx_wifi_country_code_t* country_code_results ); + +/*! + * @brief Configure the timestamp used to discriminate mobile access points from gateways. + * + * This filtering is based on the hypothesis that mobile access points have timestamp shorter than gateways. + * + * @param [in] context Chip implementation context + * @param [in] timestamp_in_s Timestamp value in second + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_cfg_timestamp_ap_phone( const void* context, uint32_t timestamp_in_s ); + +/*! + * @brief Get the internal wifi firmware version + * + * @param [in] context Chip implementation context + * @param [out] wifi_version The wifi version structure populated with version numbers + * + * @returns Operation status + */ +lr11xx_status_t lr11xx_wifi_read_version( const void* context, lr11xx_wifi_version_t* wifi_version ); + +/*! + * @brief Retreive channel information from channel info byte + * + * This method is to be called with on the WiFi channel info byte of a scan result. + * + * As the WiFi passive scan allows to get Access Point MAC address from Packet WiFi frames, it is possible that the + * frame does not comes from the Access Point, but from a device. In that case, the RSSI reported by LR11XX is the one + * of the frame received from the device and not from the Access Point. The rssi_validity flag allows to detect that + * case. + * + * It is possible for an Access Point to be a mobile AP, which is of low interest for location purpose. The LR11XX tries + * to detect mobile AP based on Access Point up time and set the flag mac_origin_estimation accordingly. + * + * @param [in] channel_info The channel info byte to retrieve channel information from. It is obtained from WiFi + * passive scan result + * @param [out] channel The channel of the scanned mac address + * @param [out] rssi_validity The validity of the scanned MAC address + * @param [out] mac_origin_estimation Indicates the estimation of MAC address origin by LR11XX + * + * @see lr11xx_wifi_read_basic_complete_results, lr11xx_wifi_read_basic_mac_type_channel_results, + * lr11xx_wifi_cfg_timestamp_ap_phone + */ +void lr11xx_wifi_parse_channel_info( const lr11xx_wifi_channel_info_byte_t channel_info, lr11xx_wifi_channel_t* channel, + bool* rssi_validity, lr11xx_wifi_mac_origin_t* mac_origin_estimation ); + +/*! + * @brief Helper method to retrieve channel from channel info byte + * + * @param [in] channel_info The chanel info byte from passive scan result + * + * @returns The channel of scanned MAC address + * + * @see lr11xx_wifi_parse_channel_info + */ +lr11xx_wifi_channel_t lr11xx_wifi_extract_channel_from_info_byte( const lr11xx_wifi_channel_info_byte_t channel_info ); + +/*! + * @brief Retrieve the Frame Type, Frame Subtype, To/From DS fields from a frame info byte + * + * This method is intended to be called on the channel info byte of a passive scan result structure. + * + * The from_ds/to_ds (Distribution Station) fields have the following meaning: + * + * + * + * + *
    to_ds value from_ds value Meaning
    False False Frame was between two Stations + *
    True False Frame was from Station to + * Access Point
    False True Frame was sent + * from Access Point or Distribution Stations
    True + * True Mesh network only, frame was between Stations
    + * + * @param [in] frame_type_info The frame info byte from passive scan result + * @param [out] frame_type The Frame Type of the received frame + * @param [out] frame_sub_type The Frame SubType of the frame received + * @param [out] to_ds to_ds field of the frame received + * @param [out] from_ds from_ds field of the frame received + */ +void lr11xx_wifi_parse_frame_type_info( const lr11xx_wifi_frame_type_info_byte_t frame_type_info, + lr11xx_wifi_frame_type_t* frame_type, + lr11xx_wifi_frame_sub_type_t* frame_sub_type, bool* to_ds, bool* from_ds ); + +/*! + * @brief Retrieve the data rate information from data rate info byte + * + * This method is intended to be called on a data rate info byte of a passive scan result structure. + * + * @param [in] data_rate_info The data rate info byte from a passive scan result + * @param [out] wifi_signal_type The wifi signal type of the scanned frame + * @param [out] wifi_data_rate The data rate of the scanned frame + */ +void lr11xx_wifi_parse_data_rate_info( const lr11xx_wifi_datarate_info_byte_t data_rate_info, + lr11xx_wifi_signal_type_result_t* wifi_signal_type, + lr11xx_wifi_datarate_t* wifi_data_rate ); + +/*! + * @brief Return the maximal number of results to read per SPI communication + * + * This function **DOES NOT** communicates with the LR11XX. It returns the driver maximal number of Wi-Fi results it can + * retrieve per SPI communication. + * + * @remark It is a driver limitation, not a LR11XX limitation, that avoid allocating temporary buffers of size too big + * when reading Wi-Fi passive scan results. + * + * @returns The maximal number of results to fetch per SPI calls + * + * @see LR11XX_WIFI_N_RESULTS_MAX_PER_CHUNK + */ +uint8_t lr11xx_wifi_get_nb_results_max_per_chunk( void ); + +/*! + * @brief Helper method to retrieve the signal type from data rate info byte + * + * @param [in] data_rate_info The data rate info byte from a passive scan result + * + * @returns The Signal Type of the scanned frame + */ +lr11xx_wifi_signal_type_result_t lr11xx_wifi_extract_signal_type_from_data_rate_info( + const lr11xx_wifi_datarate_info_byte_t data_rate_info ); + +/*! + * @brief Helper function to check if a buffer is a well-formed UTF-8 byte sequence + * + * @param [in] buffer The buffer holding the bytes to be analyzed + * @param [in] length The number of bytes in the buffer + * + * @returns The result of the check + */ +bool lr11xx_wifi_is_well_formed_utf8_byte_sequence( const uint8_t* buffer, const uint8_t length ); + +/*! + * @brief Check that Wi-Fi scan mode and result format are compatible + * + * The possible combination of Wi-Fi scan modes and result format are the following: + * + * + *
    Scan Mode Type/Sub-type selected Corresponding read result function + *
    @ref ::LR11XX_WIFI_SCAN_MODE_BEACON Management/Beacon and Management/Probe Response + * @ref lr11xx_wifi_read_basic_complete_results, @ref lr11xx_wifi_read_basic_mac_type_channel_results
    + * @ref ::LR11XX_WIFI_SCAN_MODE_BEACON_AND_PKT Some from Management, Control and Data Types
    + * @ref ::LR11XX_WIFI_SCAN_MODE_FULL_BEACON Management/Beacon and Management/Probe Response @ref + * lr11xx_wifi_read_extended_full_results
    @ref ::LR11XX_WIFI_SCAN_MODE_UNTIL_SSID Management/Beacon and + * Management/Probe Response - until SSID field + *
    + * + * @param scan_mode The scan mode used when calling the scan API + * @param result_format The result format used when calling the read result API + * @retval true The scan mode and result format are compatible + * @retval false The scan mode and result format are not compatible. + */ +bool lr11xx_wifi_are_scan_mode_result_format_compatible( lr11xx_wifi_mode_t scan_mode, + lr11xx_wifi_result_format_t result_format ); + +/** + * @brief Compute the power consumption in nAh based on the cumulative timing. + * + * @param [in] regulator The regulator used during last Wi-Fi passive scan + * @param [in] timing Cumulative timing structure to use for computation + * + * @returns Current consumption in nAh + */ +uint32_t lr11xx_wifi_get_consumption_nah( lr11xx_system_reg_mode_t regulator, lr11xx_wifi_cumulative_timings_t timing ); + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_WIFI_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr11xx_wifi_types.h b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_wifi_types.h new file mode 100755 index 0000000..44cc244 --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr11xx_wifi_types.h @@ -0,0 +1,410 @@ +/*! + * @file lr11xx_wifi_types.h + * + * @brief Wi-Fi passive scan driver types for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR11XX_WIFI_TYPES_H +#define LR11XX_WIFI_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +#define LR11XX_WIFI_MAC_ADDRESS_LENGTH ( 6 ) +#define LR11XX_WIFI_MAX_RESULTS ( 32 ) +#define LR11XX_WIFI_RESULT_SSID_LENGTH ( 32 ) +#define LR11XX_WIFI_MAX_COUNTRY_CODE ( 32 ) +#define LR11XX_WIFI_STR_COUNTRY_CODE_SIZE ( 2 ) + +#define LR11XX_WIFI_CHANNEL_1_POS ( 0U ) //!< Channel at frequency 2.412 GHz +#define LR11XX_WIFI_CHANNEL_1_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_1_POS ) +#define LR11XX_WIFI_CHANNEL_2_POS ( 1U ) //!< Channel at frequency 2.417 GHz +#define LR11XX_WIFI_CHANNEL_2_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_2_POS ) +#define LR11XX_WIFI_CHANNEL_3_POS ( 2U ) //!< Channel at frequency 2.422 GHz +#define LR11XX_WIFI_CHANNEL_3_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_3_POS ) +#define LR11XX_WIFI_CHANNEL_4_POS ( 3U ) //!< Channel at frequency 2.427 GHz +#define LR11XX_WIFI_CHANNEL_4_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_4_POS ) +#define LR11XX_WIFI_CHANNEL_5_POS ( 4U ) //!< Channel at frequency 2.432 GHz +#define LR11XX_WIFI_CHANNEL_5_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_5_POS ) +#define LR11XX_WIFI_CHANNEL_6_POS ( 5U ) //!< Channel at frequency 2.437 GHz +#define LR11XX_WIFI_CHANNEL_6_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_6_POS ) +#define LR11XX_WIFI_CHANNEL_7_POS ( 6U ) //!< Channel at frequency 2.442 GHz +#define LR11XX_WIFI_CHANNEL_7_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_7_POS ) +#define LR11XX_WIFI_CHANNEL_8_POS ( 7U ) //!< Channel at frequency 2.447 GHz +#define LR11XX_WIFI_CHANNEL_8_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_8_POS ) +#define LR11XX_WIFI_CHANNEL_9_POS ( 8U ) //!< Channel at frequency 2.452 GHz +#define LR11XX_WIFI_CHANNEL_9_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_9_POS ) +#define LR11XX_WIFI_CHANNEL_10_POS ( 9U ) //!< Channel at frequency 2.457 GHz +#define LR11XX_WIFI_CHANNEL_10_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_10_POS ) +#define LR11XX_WIFI_CHANNEL_11_POS ( 10U ) //!< Channel at frequency 2.462 GHz +#define LR11XX_WIFI_CHANNEL_11_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_11_POS ) +#define LR11XX_WIFI_CHANNEL_12_POS ( 11U ) //!< Channel at frequency 2.467 GHz +#define LR11XX_WIFI_CHANNEL_12_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_12_POS ) +#define LR11XX_WIFI_CHANNEL_13_POS ( 12U ) //!< Channel at frequency 2.472 GHz +#define LR11XX_WIFI_CHANNEL_13_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_13_POS ) +#define LR11XX_WIFI_CHANNEL_14_POS ( 13U ) //!< Channel at frequency 2.484 GHz +#define LR11XX_WIFI_CHANNEL_14_MASK ( 0x01UL << LR11XX_WIFI_CHANNEL_14_POS ) + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/*! + * @brief Type to store a Wi-Fi channel mask + */ +typedef uint16_t lr11xx_wifi_channel_mask_t; + +/*! + * @brief Type to store a Wi-Fi channel info byte + */ +typedef uint8_t lr11xx_wifi_channel_info_byte_t; + +/*! + * @brief Type to store a Wi-Fi datarate info byte + */ +typedef uint8_t lr11xx_wifi_datarate_info_byte_t; + +/*! + * @brief Type to store a Wi-Fi frame type info byte + */ +typedef uint8_t lr11xx_wifi_frame_type_info_byte_t; + +/*! + * @brief Type to store a Wi-Fi frame sub_type + */ +typedef uint8_t lr11xx_wifi_frame_sub_type_t; + +/*! + * @brief Wi-Fi FCS info byte + */ +typedef struct lr11xx_wifi_fcs_info_byte_s +{ + bool is_fcs_ok; //!< True if the LR11XX has checked the FCS and the check succeeded + bool is_fcs_checked; //!< True if the LR11XX has checked the FCS +} lr11xx_wifi_fcs_info_byte_t; + +/*! + * @brief Type to store a MAC address + */ +typedef uint8_t lr11xx_wifi_mac_address_t[LR11XX_WIFI_MAC_ADDRESS_LENGTH]; + +/*! + * @brief Type to store the Country Code + */ +typedef uint8_t lr11xx_wifi_country_code_str_t[LR11XX_WIFI_STR_COUNTRY_CODE_SIZE]; + +/*! + * @brief Wi-Fi Channels index + */ +typedef enum +{ + LR11XX_WIFI_NO_CHANNEL = 0x00, + LR11XX_WIFI_CHANNEL_1 = 0x01, //!< Channel at frequency 2.412 GHz + LR11XX_WIFI_CHANNEL_2 = 0x02, //!< Channel at frequency 2.417 GHz + LR11XX_WIFI_CHANNEL_3 = 0x03, //!< Channel at frequency 2.422 GHz + LR11XX_WIFI_CHANNEL_4 = 0x04, //!< Channel at frequency 2.427 GHz + LR11XX_WIFI_CHANNEL_5 = 0x05, //!< Channel at frequency 2.432 GHz + LR11XX_WIFI_CHANNEL_6 = 0x06, //!< Channel at frequency 2.437 GHz + LR11XX_WIFI_CHANNEL_7 = 0x07, //!< Channel at frequency 2.442 GHz + LR11XX_WIFI_CHANNEL_8 = 0x08, //!< Channel at frequency 2.447 GHz + LR11XX_WIFI_CHANNEL_9 = 0x09, //!< Channel at frequency 2.452 GHz + LR11XX_WIFI_CHANNEL_10 = 0x0A, //!< Channel at frequency 2.457 GHz + LR11XX_WIFI_CHANNEL_11 = 0x0B, //!< Channel at frequency 2.462 GHz + LR11XX_WIFI_CHANNEL_12 = 0x0C, //!< Channel at frequency 2.467 GHz + LR11XX_WIFI_CHANNEL_13 = 0x0D, //!< Channel at frequency 2.472 GHz + LR11XX_WIFI_CHANNEL_14 = 0x0E, //!< Channel at frequency 2.484 GHz + LR11XX_WIFI_ALL_CHANNELS = 0x0F, +} lr11xx_wifi_channel_t; + +/*! + * @brief WiFi theoretical Datarates + */ +typedef enum +{ + LR11XX_WIFI_DATARATE_1_MBPS = 1, + LR11XX_WIFI_DATARATE_2_MBPS = 2, + LR11XX_WIFI_DATARATE_6_MBPS = 3, + LR11XX_WIFI_DATARATE_9_MBPS = 4, + LR11XX_WIFI_DATARATE_12_MBPS = 5, + LR11XX_WIFI_DATARATE_18_MBPS = 6, + LR11XX_WIFI_DATARATE_24_MBPS = 7, + LR11XX_WIFI_DATARATE_36_MBPS = 8, + LR11XX_WIFI_DATARATE_48_MBPS = 9, + LR11XX_WIFI_DATARATE_54_MBPS = 10, + LR11XX_WIFI_DATARATE_6_5_MBPS = 11, + LR11XX_WIFI_DATARATE_13_MBPS = 12, + LR11XX_WIFI_DATARATE_19_5_MBPS = 13, + LR11XX_WIFI_DATARATE_26_MBPS = 14, + LR11XX_WIFI_DATARATE_39_MBPS = 15, + LR11XX_WIFI_DATARATE_52_MBPS = 16, + LR11XX_WIFI_DATARATE_58_MBPS = 17, + LR11XX_WIFI_DATARATE_65_MBPS = 18, + LR11XX_WIFI_DATARATE_7_2_MBPS = 19, + LR11XX_WIFI_DATARATE_14_4_MBPS = 20, + LR11XX_WIFI_DATARATE_21_7_MBPS = 21, + LR11XX_WIFI_DATARATE_28_9_MBPS = 22, + LR11XX_WIFI_DATARATE_43_3_MBPS = 23, + LR11XX_WIFI_DATARATE_57_8_MBPS = 24, + LR11XX_WIFI_DATARATE_65_2_MBPS = 25, + LR11XX_WIFI_DATARATE_72_2_MBPS = 26, +} lr11xx_wifi_datarate_t; + +/*! + * @brief WiFi Frame Types + */ +typedef enum +{ + LR11XX_WIFI_FRAME_TYPE_MANAGEMENT = 0x00, + LR11XX_WIFI_FRAME_TYPE_CONTROL = 0x01, + LR11XX_WIFI_FRAME_TYPE_DATA = 0x02, +} lr11xx_wifi_frame_type_t; + +/*! + * @brief The WiFi MAC address origin + * + * @see lr11xx_wifi_parse_channel_info for details about the MAC address origin estimation of the LR11XX + */ +typedef enum +{ + LR11XX_WIFI_ORIGIN_BEACON_FIX_AP = 1, //!< MAC address extracted from a packet coming from a fix Access Point + LR11XX_WIFI_ORIGIN_BEACON_MOBILE_AP = 2, //!< MAC address extracted from a packet coming from a mobile Access Point + LR11XX_WIFI_ORIGIN_UNKNOWN = 3, //!< Impossible to determine the origin of the packet the MAC is extracted from +} lr11xx_wifi_mac_origin_t; + +/*! + * @brief Wi-Fi signal type for passive scanning configuration + * + * Note it is not possible to configure the WiFi passive scanning to search Wi-Fi type N GreenField. Only Wi-Fi type N + * Mixed Mode can be scanned by LR11XX. + * + * @warning ::LR11XX_WIFI_TYPE_SCAN_G and ::LR11XX_WIFI_TYPE_SCAN_N configurations are implemented the same way, and + * both will scan Wi-Fi type G **AND** Wi-Fi type N. + */ +typedef enum +{ + LR11XX_WIFI_TYPE_SCAN_B = 0x01, //!< Wi-Fi B + LR11XX_WIFI_TYPE_SCAN_G = 0x02, //!< Wi-Fi G + LR11XX_WIFI_TYPE_SCAN_N = 0x03, //!< Wi-Fi N + LR11XX_WIFI_TYPE_SCAN_B_G_N = 0x04, //!< Wi-Fi B and Wi-Fi G/N +} lr11xx_wifi_signal_type_scan_t; + +/*! + * @brief Wi-Fi signal type for passive scan results + * + * Note that the Wi-Fi N detected is Wi-Fi N Mixed mode, and not GreenField. + */ +typedef enum +{ + LR11XX_WIFI_TYPE_RESULT_B = 0x01, //!< WiFi B + LR11XX_WIFI_TYPE_RESULT_G = 0x02, //!< WiFi G + LR11XX_WIFI_TYPE_RESULT_N = 0x03, //!< WiFi N +} lr11xx_wifi_signal_type_result_t; + +/*! + * @brief Wi-Fi scan mode + * + * When the LR11XX receives a Wi-Fi frame, it starts demodulating it. Depending on the scan mode selected, only some + * Wi-Fi frame type/sub-types are to be kept. The demodulation step is stopped as soon as the LR11XX detects the current + * Wi-Fi frame is not of the required type/sub-types. This saves scan time and consumption. + * + * A Wi-Fi frame is never completely demodulated. The ::LR11XX_WIFI_SCAN_MODE_FULL_BEACON uses a special configuration + * allowing to demodulate more fields (until Frame Check Sequence field), at a price of higher scan duration and higher + * consumption. + * + * @note Not all results formats are available depending on the scan mode selected. Refer to + * @ref lr11xx_wifi_are_scan_mode_result_format_compatible to know which result formats are available depending on scan + * mode selected. + * + * @see lr11xx_wifi_are_scan_mode_result_format_compatible + */ +typedef enum +{ + LR11XX_WIFI_SCAN_MODE_BEACON = + 1, //!< Exposes Beacons and Probe Responses Access Points frames until Period Beacon field (Basic result) + LR11XX_WIFI_SCAN_MODE_BEACON_AND_PKT = + 2, //!< Exposes some Management Access Points frames until Period Beacon field, and some other packets frame + //!< until third Mac Address field (Basic result) + LR11XX_WIFI_SCAN_MODE_FULL_BEACON = + 4, //!< Exposes Beacons and Probes Responses Access Points frames until Frame Check Sequence (FCS) field + //!< (Extended result). In this mode, only signal type LR11XX_WIFI_TYPE_SCAN_B is executed and other signal + //!< types are silently discarded. + LR11XX_WIFI_SCAN_MODE_UNTIL_SSID = 5, //!< Exposes Beacons and Probes Responses Access Points frames until the end + //!< of SSID field (Extended result) - available since firmware 0x0306 +} lr11xx_wifi_mode_t; + +/*! + * @brief Cumulative timings + * + * This structure is representing the cumulative time spent in the different modes of Wi-Fi passive scanning procedure. + * All timings are provided in [us]. + * */ +typedef struct lr11xx_wifi_cumulative_timings_s +{ + uint32_t rx_detection_us; //!< Cumulative time spent during NFE or TOA + uint32_t rx_correlation_us; //!< Cumulative time spent during preamble detection + uint32_t rx_capture_us; //!< Cumulative time spent during signal acquisition + uint32_t demodulation_us; //!< Cumulative time spent during software demodulation +} lr11xx_wifi_cumulative_timings_t; + +/*! + * @brief Basic complete result structure + * + * The beacon period is expressed in TU (Time Unit). 1 TU is 1024 microseconds. + */ +typedef struct lr11xx_wifi_basic_complete_result_s +{ + lr11xx_wifi_datarate_info_byte_t data_rate_info_byte; + lr11xx_wifi_channel_info_byte_t channel_info_byte; + int8_t rssi; + lr11xx_wifi_frame_type_info_byte_t frame_type_info_byte; + lr11xx_wifi_mac_address_t mac_address; + int16_t phi_offset; + uint64_t timestamp_us; //!< Indicate the up-time of the Access Point transmitting the Beacon [us] + uint16_t beacon_period_tu; +} lr11xx_wifi_basic_complete_result_t; + +/*! + * @brief Basic MAC, type, channel result structure + */ +typedef struct lr11xx_wifi_basic_mac_type_channel_result_s +{ + lr11xx_wifi_datarate_info_byte_t data_rate_info_byte; + lr11xx_wifi_channel_info_byte_t channel_info_byte; + int8_t rssi; + lr11xx_wifi_mac_address_t mac_address; +} lr11xx_wifi_basic_mac_type_channel_result_t; + +/*! + * @brief Extended full result structure + * + * @note The beacon period is expressed in TU (Time Unit). 1 TU is 1024 microseconds. + * + * @remark When used with @ref ::LR11XX_WIFI_SCAN_MODE_UNTIL_SSID, the following field are always set to 0: + * - field is_fcs_ok and is_fcs_checked in fcs_check_byte structure + * - current_channel + * - country_code + * - io_regulation + */ +typedef struct +{ + lr11xx_wifi_datarate_info_byte_t data_rate_info_byte; + lr11xx_wifi_channel_info_byte_t channel_info_byte; + int8_t rssi; + uint8_t rate; //!< Rate index + uint16_t service; //!< Service value + uint16_t length; //!< Length of MPDU (in microseconds for WiFi B, bytes for WiFi G) + uint16_t frame_control; //!< Frame Control structure + lr11xx_wifi_mac_address_t mac_address_1; + lr11xx_wifi_mac_address_t mac_address_2; + lr11xx_wifi_mac_address_t mac_address_3; + uint64_t timestamp_us; //!< Indicate the up-time of the Access Point + //!< transmitting the Beacon [us] + uint16_t beacon_period_tu; + uint16_t seq_control; //!< Sequence Control value + uint8_t ssid_bytes[LR11XX_WIFI_RESULT_SSID_LENGTH]; //!< Service Set + //!< IDentifier + lr11xx_wifi_channel_t current_channel; //!< Current channel indicated in the Wi-Fi frame + lr11xx_wifi_country_code_str_t country_code; //!< Country Code + uint8_t io_regulation; //!< Input Output Regulation + lr11xx_wifi_fcs_info_byte_t fcs_check_byte; //!< Frame Check Sequence info + int16_t phi_offset; +} lr11xx_wifi_extended_full_result_t; + +/*! + * @brief Wi-Fi scan result formats + * + * @note Result format to use depends on the scan mode selected when calling @ref lr11xx_wifi_scan or @ref + * lr11xx_wifi_scan_time_limit API. Refer to @ref lr11xx_wifi_are_scan_mode_result_format_compatible to know which + * result formats are available depending on scan mode selected. + * + * @see lr11xx_wifi_are_scan_mode_result_format_compatible + */ +typedef enum +{ + LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE, //!< Basic complete result format: @ref + //!< lr11xx_wifi_basic_complete_result_t + LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL, //!< Basic MAC/type/channel result format: @ref + //!< lr11xx_wifi_basic_mac_type_channel_result_t + LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL, //!< Extended full result format: @ref lr11xx_wifi_extended_full_result_t +} lr11xx_wifi_result_format_t; + +/*! + * @brief Wi-Fi country code structure + */ +typedef struct lr11xx_wifi_country_code_s +{ + lr11xx_wifi_country_code_str_t country_code; + uint8_t io_regulation; //!< Input Output Regulation + lr11xx_wifi_channel_info_byte_t channel_info_byte; + lr11xx_wifi_mac_address_t mac_address; +} lr11xx_wifi_country_code_t; + +/*! + * @brief Wi-Fi firmware version + */ +typedef struct lr11xx_wifi_version_s +{ + uint8_t major; + uint8_t minor; +} lr11xx_wifi_version_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#ifdef __cplusplus +} +#endif + +#endif // LR11XX_WIFI_TYPES_H + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/include/lr11xx_driver/lr_fhss_v1_base_types.h b/components/esp_lora_1121/include/lr11xx_driver/lr_fhss_v1_base_types.h new file mode 100755 index 0000000..c6afffd --- /dev/null +++ b/components/esp_lora_1121/include/lr11xx_driver/lr_fhss_v1_base_types.h @@ -0,0 +1,127 @@ +/** + * @file lr_fhss_v1_base_types.h + * + * @brief Radio-independent LR-FHSS base type definitions, version 1 + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR_FHSS_V1_BASE_TYPES_H__ +#define LR_FHSS_V1_BASE_TYPES_H__ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC MACROS ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC CONSTANTS -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC TYPES ------------------------------------------------------------ + */ + +/** + * @brief LR-FHSS modulation type + */ +typedef enum lr_fhss_v1_modulation_type_e +{ + LR_FHSS_V1_MODULATION_TYPE_GMSK_488 = 0, +} lr_fhss_v1_modulation_type_t; + +/** + * @brief LR-FHSS coding rate + */ +typedef enum lr_fhss_v1_cr_e +{ + LR_FHSS_V1_CR_5_6 = 0x00, + LR_FHSS_V1_CR_2_3 = 0x01, + LR_FHSS_V1_CR_1_2 = 0x02, + LR_FHSS_V1_CR_1_3 = 0x03, +} lr_fhss_v1_cr_t; + +/** + * @brief LR-FHSS grid + */ +typedef enum lr_fhss_v1_grid_e +{ + LR_FHSS_V1_GRID_25391_HZ = 0x00, + LR_FHSS_V1_GRID_3906_HZ = 0x01, +} lr_fhss_v1_grid_t; + +/** + * @brief LR-FHSS bandwidth + */ +typedef enum lr_fhss_v1_bw_e +{ + LR_FHSS_V1_BW_39063_HZ = 0x00, + LR_FHSS_V1_BW_85938_HZ = 0x01, + LR_FHSS_V1_BW_136719_HZ = 0x02, + LR_FHSS_V1_BW_183594_HZ = 0x03, + LR_FHSS_V1_BW_335938_HZ = 0x04, + LR_FHSS_V1_BW_386719_HZ = 0x05, + LR_FHSS_V1_BW_722656_HZ = 0x06, + LR_FHSS_V1_BW_773438_HZ = 0x07, + LR_FHSS_V1_BW_1523438_HZ = 0x08, + LR_FHSS_V1_BW_1574219_HZ = 0x09, +} lr_fhss_v1_bw_t; + +/** + * @brief LR-FHSS parameter structure + */ +typedef struct lr_fhss_v1_params_s +{ + const uint8_t* sync_word; /**< 4-byte sync word */ + lr_fhss_v1_modulation_type_t modulation_type; + lr_fhss_v1_cr_t cr; + lr_fhss_v1_grid_t grid; + lr_fhss_v1_bw_t bw; + bool enable_hopping; + uint8_t header_count; /**< Number of header blocks */ +} lr_fhss_v1_params_t; + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS PROTOTYPES --------------------------------------------- + */ + +#endif // LR_FHSS_V1_BASE_TYPES_H__ + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/esp_lora_1121.c b/components/esp_lora_1121/src/esp_lora_1121.c new file mode 100755 index 0000000..51efff6 --- /dev/null +++ b/components/esp_lora_1121/src/esp_lora_1121.c @@ -0,0 +1,112 @@ +#include "esp_lora_1121.h" + + +void lora_init_io_context(const void *context,int cs,int reset,int busy,int irq) +{ + ((lr1121_t *)context)->cs = cs; + ((lr1121_t *)context)->reset = reset; + ((lr1121_t *)context)->irq = irq; + ((lr1121_t *)context)->busy = busy; +} + +void lora_init_io(const void *context) +{ + //Set the output pin + gpio_config_t io_conf = {}; + io_conf.intr_type = GPIO_INTR_DISABLE; // Disable interrupts for this pin + if (((lr1121_t *)context)->cs >= 0) { + io_conf.pin_bit_mask = 1ULL << ((lr1121_t *)context)->cs; + if (((lr1121_t *)context)->reset >= 0) { + io_conf.pin_bit_mask |= 1ULL << ((lr1121_t *)context)->reset; // Select the GPIO pin using a bitmask + } + io_conf.mode = GPIO_MODE_INPUT_OUTPUT; // Set pin as input + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; // Enable internal pull-up resistor + gpio_config(&io_conf); // Apply the configuration + } + + //Set the input pin + io_conf.pin_bit_mask = 0; + if (((lr1121_t *)context)->busy >= 0) { + io_conf.pin_bit_mask |= 1ULL << ((lr1121_t *)context)->busy; + } + if (((lr1121_t *)context)->irq >= 0) { + io_conf.pin_bit_mask |= 1ULL << ((lr1121_t *)context)->irq; // Select the GPIO pin using a bitmask + } + if (io_conf.pin_bit_mask) { + io_conf.mode = GPIO_MODE_INPUT; // Set pin as input + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; // Enable internal pull-up resistor + gpio_config(&io_conf); // Apply the configuration + } + + if (((lr1121_t *)context)->cs >= 0) { + gpio_set_level(((lr1121_t *)context)->cs, 1); // Set the GPIO pin level + } + if (((lr1121_t *)context)->reset >= 0) { + gpio_set_level(((lr1121_t *)context)->reset, 1); // Set the GPIO pin level + } +} + +void lora_init_irq(const void *context, gpio_isr_t handler) +{ + if (handler == NULL || ((lr1121_t *)context)->irq < 0) { + return; + } + // Zero-initialize the GPIO configuration structure + gpio_config_t io_conf = {}; + io_conf.intr_type = GPIO_INTR_POSEDGE; // Trigger on negative edge (falling edge) + io_conf.mode = GPIO_MODE_INPUT; // Set pin as input mode + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; // Disable pull-down + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; // Enable pull-up resistor + if (((lr1121_t *)context)->irq < 0) { + return; + } + io_conf.pin_bit_mask = 1ULL << ((lr1121_t *)context)->irq; // Select the GPIO pin using a bitmask + + gpio_config(&io_conf); // Apply the configuration + + // Install the GPIO interrupt service if not already installed + gpio_install_isr_service(0); // Pass 0 for default ISR flags + + // Register the interrupt handler for the specified pin + gpio_isr_handler_add(((lr1121_t *)context)->irq, handler, (void *)((lr1121_t *)context)->irq); +} + +void lora_spi_init(const void* context, spi_device_handle_t spi) +{ + ((lr1121_t *)context)->spi = spi; +} + +void lora_spi_write_bytes(const void* context,const uint8_t *wirte,const uint16_t wirte_length) +{ + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length = wirte_length * 8; // Length is in bits + t.tx_buffer = wirte; + + ESP_ERROR_CHECK(spi_device_transmit(((lr1121_t *)context)->spi, &t)); +} + +void lora_spi_read_bytes(const void* context, uint8_t *read,const uint16_t read_length) +{ + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length = read_length * 8; // Length is in bits + t.rx_buffer = read; + + ESP_ERROR_CHECK(spi_device_transmit(((lr1121_t *)context)->spi, &t)); +} + + +lr1121_modem_response_code_t lr1121_modem_board_event_flush( const void* context ) +{ + lr1121_modem_response_code_t modem_response_code = LR1121_MODEM_RESPONSE_CODE_OK; + lr1121_modem_event_fields_t event_fields; + + do + { + modem_response_code = lr1121_modem_get_event( context, &event_fields ); + } while( modem_response_code != LR1121_MODEM_RESPONSE_CODE_NO_EVENT ); + + return modem_response_code; +} + diff --git a/components/esp_lora_1121/src/lr1121_common/lr1121_common.c b/components/esp_lora_1121/src/lr1121_common/lr1121_common.c new file mode 100755 index 0000000..542cb48 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_common/lr1121_common.c @@ -0,0 +1,837 @@ +#include "lr1121_common.h" +#include +#include +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + + static const lr11xx_radio_rssi_calibration_table_t smtc_shield_lr11xx_common_rssi_calibration_table_below_600mhz = { + .gain_offset = 0, + .gain_tune = { .g4 = 12, + .g5 = 12, + .g6 = 14, + .g7 = 0, + .g8 = 1, + .g9 = 3, + .g10 = 4, + .g11 = 4, + .g12 = 3, + .g13 = 6, + .g13hp1 = 6, + .g13hp2 = 6, + .g13hp3 = 6, + .g13hp4 = 6, + .g13hp5 = 6, + .g13hp6 = 6, + .g13hp7 = 6 }, +}; + +static const lr11xx_radio_rssi_calibration_table_t + smtc_shield_lr11xx_common_rssi_calibration_table_from_600mhz_to_2ghz = { + .gain_offset = 0, + .gain_tune = { .g4 = 2, + .g5 = 2, + .g6 = 2, + .g7 = 3, + .g8 = 3, + .g9 = 4, + .g10 = 5, + .g11 = 4, + .g12 = 4, + .g13 = 6, + .g13hp1 = 5, + .g13hp2 = 5, + .g13hp3 = 6, + .g13hp4 = 6, + .g13hp5 = 6, + .g13hp6 = 7, + .g13hp7 = 6 }, + }; + +static const lr11xx_radio_rssi_calibration_table_t smtc_shield_lr11xx_common_rssi_calibration_table_above_2ghz = { + .gain_offset = 2030, + .gain_tune = { .g4 = 6, + .g5 = 7, + .g6 = 6, + .g7 = 4, + .g8 = 3, + .g9 = 4, + .g10 = 14, + .g11 = 12, + .g12 = 14, + .g13 = 12, + .g13hp1 = 12, + .g13hp2 = 12, + .g13hp3 = 12, + .g13hp4 = 8, + .g13hp5 = 8, + .g13hp6 = 9, + .g13hp7 = 9 }, +}; + +const lr11xx_system_rfswitch_cfg_t smtc_shield_lr11xx_common_rf_switch_cfg = { + .enable = LR11XX_SYSTEM_RFSW0_HIGH | LR11XX_SYSTEM_RFSW1_HIGH , + .standby = 0, + .rx = LR11XX_SYSTEM_RFSW0_HIGH, + .tx = LR11XX_SYSTEM_RFSW1_HIGH, + .tx_hp = LR11XX_SYSTEM_RFSW1_HIGH, + .tx_hf = 0, + .gnss = 0, + .wifi = 0, +}; + +const smtc_shield_lr11xx_lfclk_cfg_t smtc_shield_lr11xx_common_lfclk_cfg = { + .lf_clk_cfg = LR11XX_SYSTEM_LFCLK_XTAL, + .wait_32k_ready = true, +}; + +const smtc_shield_lr11xx_pa_pwr_cfg_t smtc_shield_lr1121mb1gis_pa_pwr_cfg_table[SMTC_SHIELD_LR11XX_MAX_PWR - SMTC_SHIELD_LR11XX_MIN_PWR + 1] = { + { // Expected output power = -9dBm + .power = -9, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = -8dBm + .power = -8, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = -7dBm + .power = -7, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = -6dBm + .power = -6, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = -5dBm + .power = -5, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = -4dBm + .power = -4, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = -3dBm + .power = -3, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = -2dBm + .power = -2, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = -1dBm + .power = -1, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 0dBm + .power = 0, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 1dBm + .power = 1, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 2dBm + .power = 2, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 3dBm + .power = 3, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 4dBm + .power = 4, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 5dBm + .power = 5, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 6dBm + .power = 6, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 7dBm + .power = 7, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 8dBm + .power = 8, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 9dBm + .power = 9, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 10dBm + .power = 10, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 11dBm + .power = 11, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 12dBm + .power = 12, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 13dBm + .power = 13, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 14dBm + .power = 14, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 15dBm + .power = 15, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 16dBm + .power = 16, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 17dBm + .power = 17, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 18dBm + .power = 18, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 19dBm + .power = 19, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 20dBm + .power = 20, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 21dBm + .power = 21, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, + { // Expected output power = 22dBm + .power = 22, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HP, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VBAT, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x07, + }, + }, +}; + +const smtc_shield_lr11xx_pa_pwr_cfg_t smtc_shield_lr1121mb1gis_pa_pwr_hf_cfg_table[SMTC_SHIELD_LR112X_MAX_PWR_HF - SMTC_SHIELD_LR112X_MIN_PWR_HF + 1] = { + { // Expected output power = -18dBm + .power = -18, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -17dBm + .power = -17, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -16dBm + .power = -17, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -15dBm + .power = -16, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -14dBm + .power = -15, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -13dBm + .power = -14, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -12dBm + .power = -13, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -11dBm + .power = -12, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x05, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -10dBm + .power = -10, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -9dBm + .power = -9, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -8dBm + .power = -8, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -7dBm + .power = -7, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -6dBm + .power = -6, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -5dBm + .power = -5, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -4dBm + .power = -4, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -3dBm + .power = -3, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -2dBm + .power = -2, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x03, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = -1dBm + .power = -1, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x05, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 0dBm + .power = 0, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x04, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 1dBm + .power = 2, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 2dBm + .power = 2, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x00, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 3dBm + .power = 4, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 4dBm + .power = 5, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 5dBm + .power = 6, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 6dBm + .power = 7, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 7dBm + .power = 8, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 8dBm + .power = 9, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 9dBm + .power = 10, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 10dBm + .power = 11, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x05, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 11dBm + .power = 12, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x03, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 12dBm + .power = 13, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x07, + .pa_hp_sel = 0x00, + }, + }, + { // Expected output power = 13dBm + .power = 13, + .pa_config = { + .pa_sel = LR11XX_RADIO_PA_SEL_HF, + .pa_reg_supply = LR11XX_RADIO_PA_REG_SUPPLY_VREG, + .pa_duty_cycle = 0x00, + .pa_hp_sel = 0x00, + }, + }, +}; + + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC VARIABLES -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +const lr11xx_radio_rssi_calibration_table_t* smtc_shield_lr11xx_get_rssi_calibration_table( + const uint32_t rf_freq_in_hz ) +{ + if( rf_freq_in_hz < 600000000 ) + { + return &smtc_shield_lr11xx_common_rssi_calibration_table_below_600mhz; + } + else if( ( 600000000 <= rf_freq_in_hz ) && ( rf_freq_in_hz <= 2000000000 ) ) + { + return &smtc_shield_lr11xx_common_rssi_calibration_table_from_600mhz_to_2ghz; + } + else + { + return &smtc_shield_lr11xx_common_rssi_calibration_table_above_2ghz; + } +} + +const lr11xx_system_rfswitch_cfg_t* smtc_shield_lr11xx_common_get_rf_switch_cfg( void ) +{ + return &smtc_shield_lr11xx_common_rf_switch_cfg; +} + +lr11xx_system_reg_mode_t smtc_shield_lr11xx_common_get_reg_mode( void ) +{ + return LR11XX_SYSTEM_REG_MODE_DCDC; +} + +const smtc_shield_lr11xx_lfclk_cfg_t* smtc_shield_lr11xx_common_get_lfclk_cfg( void ) +{ + return &smtc_shield_lr11xx_common_lfclk_cfg; +} + +const smtc_shield_lr11xx_pa_pwr_cfg_t* smtc_shield_lr1121mb1gis_get_pa_pwr_cfg( const uint32_t rf_freq_in_hz, + int8_t expected_output_pwr_in_dbm ) +{ + if( ( SMTC_SHIELD_LR11XX_SUBGHZ_FREQ_MIN <= rf_freq_in_hz ) && + ( rf_freq_in_hz <= SMTC_SHIELD_LR11XX_SUBGHZ_FREQ_MAX ) ) + { + if( ( SMTC_SHIELD_LR11XX_MIN_PWR <= expected_output_pwr_in_dbm ) && + ( expected_output_pwr_in_dbm <= SMTC_SHIELD_LR11XX_MAX_PWR ) ) + { + return &( + smtc_shield_lr1121mb1gis_pa_pwr_cfg_table[expected_output_pwr_in_dbm - SMTC_SHIELD_LR11XX_MIN_PWR] ); + } + } + else if( ( ( SMTC_SHIELD_LR112X_2GHZ_FREQ_MIN <= rf_freq_in_hz ) && + ( rf_freq_in_hz <= SMTC_SHIELD_LR112X_2GHZ_FREQ_MAX ) ) || + ( ( SMTC_SHIELD_LR112X_2_4GHZ_FREQ_MIN <= rf_freq_in_hz ) && + ( rf_freq_in_hz <= SMTC_SHIELD_LR112X_2_4GHZ_FREQ_MAX ) ) ) + { + if( ( SMTC_SHIELD_LR112X_MIN_PWR_HF <= expected_output_pwr_in_dbm ) && + ( expected_output_pwr_in_dbm <= SMTC_SHIELD_LR112X_MAX_PWR_HF ) ) + { + return &( smtc_shield_lr1121mb1gis_pa_pwr_hf_cfg_table[expected_output_pwr_in_dbm - + SMTC_SHIELD_LR112X_MIN_PWR_HF] ); + } + } + + return NULL; +} + +/*! + * @brief A function to get the value for low data rate optimization setting + * + * @param [in] sf LoRa Spreading Factor + * @param [in] bw LoRa Bandwidth + */ +const uint8_t smtc_shield_lr11xx_common_compute_lora_ldro( const lr11xx_radio_lora_sf_t sf, const lr11xx_radio_lora_bw_t bw ) +{ + switch( bw ) + { + case LR11XX_RADIO_LORA_BW_500: + return 0; + + case LR11XX_RADIO_LORA_BW_250: + if( sf == LR11XX_RADIO_LORA_SF12 ) + { + return 1; + } + else + { + return 0; + } + + case LR11XX_RADIO_LORA_BW_800: + case LR11XX_RADIO_LORA_BW_400: + case LR11XX_RADIO_LORA_BW_200: + case LR11XX_RADIO_LORA_BW_125: + if( ( sf == LR11XX_RADIO_LORA_SF12 ) || ( sf == LR11XX_RADIO_LORA_SF11 ) ) + { + return 1; + } + else + { + return 0; + } + + case LR11XX_RADIO_LORA_BW_62: + if( ( sf == LR11XX_RADIO_LORA_SF12 ) || ( sf == LR11XX_RADIO_LORA_SF11 ) || ( sf == LR11XX_RADIO_LORA_SF10 ) ) + { + return 1; + } + else + { + return 0; + } + + case LR11XX_RADIO_LORA_BW_41: + if( ( sf == LR11XX_RADIO_LORA_SF12 ) || ( sf == LR11XX_RADIO_LORA_SF11 ) || ( sf == LR11XX_RADIO_LORA_SF10 ) || + ( sf == LR11XX_RADIO_LORA_SF9 ) ) + { + return 1; + } + else + { + return 0; + } + + case LR11XX_RADIO_LORA_BW_31: + case LR11XX_RADIO_LORA_BW_20: + case LR11XX_RADIO_LORA_BW_15: + case LR11XX_RADIO_LORA_BW_10: + // case LR11XX_RADIO_LORA_BW_7: + return 1; + + default: + return 0; + } +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_bsp.c b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_bsp.c new file mode 100755 index 0000000..a304d4d --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_bsp.c @@ -0,0 +1,472 @@ +/*! + * @file lr1121_modem_bsp.c + * + * @brief BSP driver implementation for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ +#include "lr1121_modem_bsp.h" +#include "lr1121_modem_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/*! + * @brief Power config block length + */ +#define LR1121_MODEM_OUTPUT_POWER_CONFIG_BLOCK_LENGTH ( 5 ) + +#define LR1121_MODEM_TX_POWER_CONSUMPTION_SIZE_BYTE ( 5 ) + +#define LR1121_MODEM_GET_TX_POWER_OFFSET_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_TX_POWER_OFFSET_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_GET_OUTPUT_POWER_CONFIG_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_OUTPUT_POWER_CONFIG_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_RF_OUTPUT_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_RF_OUTPUT_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_GET_CRYSTAL_ERROR_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_CRYSTAL_ERROR_CMD_LENGTH ( 3 + 4 ) +#define LR1121_MODEM_GET_XOSC_CAPA_TRIM_A_B_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_XOSC_CAPA_TRIM_A_B_CMD_LENGTH ( 3 + 2 ) +#define LR1121_MODEM_GET_TX_POWER_CONSUMPTION_UA_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_RX_POWER_CONSUMPTION_UA_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_RX_POWER_CONSUMPTION_UA_CMD_LENGTH ( 3 + 8 ) +#define LR1121_MODEM_SET_TX_POWER_CONSUMPTION_UA_CMD_LENGTH ( 3 ) + +#define LR1121_MODEM_MODEM_GET_OUTPUT_POWER_CONFIG_RBUFFER_LENGTH \ + ( LR1121_MODEM_NB_OUTPUT_POWER_CONFIG_BLOCKS * LR1121_MODEM_OUTPUT_POWER_CONFIG_BLOCK_LENGTH ) +#define LR1121_MODEM_GET_TX_POWER_CONSUMPTION_RBUFFER_LENGTH \ + ( LR1121_MODEM_NB_OUTPUT_POWER_CONFIG_BLOCKS * LR1121_MODEM_TX_POWER_CONSUMPTION_SIZE_BYTE ) +#define LR1121_MODEM_GET_RX_POWER_CONSUMPTION_RBUFFER_LENGTH ( 8 ) + +#define LR1121_MODEM_SET_TX_POWER_CONSUMPTION_UA_DBUFFER_MAX_LENGTH \ + ( LR1121_MODEM_NB_OUTPUT_POWER_CONFIG_BLOCKS * LR1121_MODEM_TX_POWER_CONSUMPTION_SIZE_BYTE ) +#define LR1121_MODEM_SET_TX_OUTPUT_POWER_CONFIGURATION_DBUFFER_MAX_LENGTH \ + ( LR1121_MODEM_OUTPUT_POWER_CONFIG_BLOCK_LENGTH * LR1121_MODEM_OUTPUT_POWER_CONFIG_BLOCK_LENGTH ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operation code command + */ +enum +{ + LR1121_MODEM_GET_TX_POWER_OFFSET_CMD = 0x00, + LR1121_MODEM_SET_TX_POWER_OFFSET_CMD = 0x01, + LR1121_MODEM_GET_OUTPUT_POWER_CONFIG_CMD = 0x02, + LR1121_MODEM_SET_OUTPUT_POWER_CONFIG_CMD = 0x03, + LR1121_MODEM_GET_RF_OUTPUT_CMD = 0x04, + LR1121_MODEM_SET_RF_OUTPUT_CMD = 0x05, + LR1121_MODEM_GET_CRYSTAL_ERROR_CMD = 0x06, + LR1121_MODEM_SET_CRYSTAL_ERROR_CMD = 0x07, + LR1121_MODEM_GET_XOSC_CAPA_TRIM_A_B = 0x08, + LR1121_MODEM_SET_XOSC_CAPA_TRIM_A_B = 0x09, + LR1121_MODEM_GET_TX_POWER_CONSUMPTION_UA = 0x0A, + LR1121_MODEM_SET_TX_POWER_CONSUMPTION_UA = 0x0B, + LR1121_MODEM_GET_LORA_RX_POWER_CONSUMPTION_UA = 0x0C, + LR1121_MODEM_SET_LORA_RX_POWER_CONSUMPTION_UA = 0x0D, + LR1121_MODEM_GET_GFSK_RX_POWER_CONSUMPTION_UA = 0x0E, + LR1121_MODEM_SET_GFSK_RX_POWER_CONSUMPTION_UA = 0x0F, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +static uint32_t lr1121_uint8_to_uint32( const uint8_t value[4] ); + +static lr1121_modem_response_code_t lr1121_modem_get_rx_power_consumption_ua( + const void* context, lr1121_modem_rx_power_consumption_t* rx_consumption, uint8_t opcode ); + +static lr1121_modem_response_code_t lr1121_modem_set_rx_power_consumption_ua( + const void* context, const lr1121_modem_rx_power_consumption_t* rx_consumption, uint8_t opcode ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr1121_modem_response_code_t lr1121_modem_get_tx_power_offset( const void* context, int8_t* tx_power_offset ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_TX_POWER_OFFSET_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_BSP >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_BSP, + LR1121_MODEM_GET_TX_POWER_OFFSET_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_TX_POWER_OFFSET_CMD_LENGTH, ( uint8_t* ) tx_power_offset, sizeof( int8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_set_tx_power_offset( const void* context, const int8_t tx_power_offset ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_TX_POWER_OFFSET_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_BSP >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_BSP, + LR1121_MODEM_SET_TX_POWER_OFFSET_CMD, + ( uint8_t ) tx_power_offset, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SET_TX_POWER_OFFSET_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_set_output_power_config( + const void* context, const lr1121_modem_output_power_config_t* output_power_configs, + uint8_t n_output_power_configs ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_OUTPUT_POWER_CONFIG_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_BSP >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_BSP, + LR1121_MODEM_SET_OUTPUT_POWER_CONFIG_CMD, + }; + + // Array of maximal size is allocated + uint8_t dbuffer[LR1121_MODEM_SET_TX_OUTPUT_POWER_CONFIGURATION_DBUFFER_MAX_LENGTH] = { 0 }; + + for( uint8_t index_power_config = 0; index_power_config < n_output_power_configs; index_power_config++ ) + { + const lr1121_modem_output_power_config_t local_power_config = output_power_configs[index_power_config]; + const uint8_t local_dbuffer_index = index_power_config * LR1121_MODEM_OUTPUT_POWER_CONFIG_BLOCK_LENGTH; + + dbuffer[local_dbuffer_index] = local_power_config.expected_power; + dbuffer[local_dbuffer_index + 1] = local_power_config.configured_power; + dbuffer[local_dbuffer_index + 2] = + ( uint8_t )( ( local_power_config.pa_supply & 0x0F ) | ( local_power_config.pa_sel << 4 ) ); + dbuffer[local_dbuffer_index + 3] = + ( ( local_power_config.pa_duty_cycle & 0x0F ) << 4 ) | ( local_power_config.pa_hp_sel & 0x0F ); + dbuffer[local_dbuffer_index + 4] = ( uint8_t ) local_power_config.pa_ramp_time; + } + + const uint8_t dbuffer_size = n_output_power_configs * LR1121_MODEM_OUTPUT_POWER_CONFIG_BLOCK_LENGTH; + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_OUTPUT_POWER_CONFIG_CMD_LENGTH, dbuffer, dbuffer_size ); +} + +lr1121_modem_response_code_t lr1121_modem_get_output_power_config( + const void* context, lr1121_modem_output_power_config_list_t output_power_config ) +{ + uint8_t rbuffer[LR1121_MODEM_MODEM_GET_OUTPUT_POWER_CONFIG_RBUFFER_LENGTH] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_OUTPUT_POWER_CONFIG_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_BSP >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_BSP, + LR1121_MODEM_GET_OUTPUT_POWER_CONFIG_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_OUTPUT_POWER_CONFIG_CMD_LENGTH, rbuffer, + LR1121_MODEM_MODEM_GET_OUTPUT_POWER_CONFIG_RBUFFER_LENGTH ); + + for( uint8_t i = 0; i < LR1121_MODEM_NB_OUTPUT_POWER_CONFIG_BLOCKS; i++ ) + { + const uint8_t local_rbuffer_index = i * LR1121_MODEM_OUTPUT_POWER_CONFIG_BLOCK_LENGTH; + + output_power_config[i].expected_power = rbuffer[local_rbuffer_index]; + output_power_config[i].configured_power = rbuffer[local_rbuffer_index + 1]; + output_power_config[i].pa_supply = ( lr1121_modem_pa_reg_supply_t )( rbuffer[local_rbuffer_index + 2] & 0x0F ); + output_power_config[i].pa_sel = + ( lr1121_modem_output_power_configuration_pa_sel_t )( ( rbuffer[local_rbuffer_index + 2] >> 4 ) & 0x0F ); + output_power_config[i].pa_duty_cycle = ( rbuffer[local_rbuffer_index + 3] & 0xF0 ) >> 4; + output_power_config[i].pa_hp_sel = rbuffer[local_rbuffer_index + 3] & 0x0F; + output_power_config[i].pa_ramp_time = ( lr1121_modem_ramp_time_t )( rbuffer[local_rbuffer_index + 4] ); + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_set_rf_output( const void* context, + const lr1121_modem_bsp_radio_pa_selection_t output ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_RF_OUTPUT_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_BSP >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_BSP, + LR1121_MODEM_SET_RF_OUTPUT_CMD, + ( uint8_t ) output, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SET_RF_OUTPUT_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_rf_output( const void* context, + const lr1121_modem_bsp_radio_pa_selection_t* output ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_RF_OUTPUT_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_BSP >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_BSP, + LR1121_MODEM_GET_RF_OUTPUT_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_RF_OUTPUT_CMD_LENGTH, ( uint8_t* ) output, sizeof( uint8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_get_crystal_error( const void* context, uint32_t* crystal_error_ppm ) +{ + uint8_t rbuffer[sizeof( uint32_t )] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_CRYSTAL_ERROR_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_BSP >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_BSP, + LR1121_MODEM_GET_CRYSTAL_ERROR_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_CRYSTAL_ERROR_CMD_LENGTH, rbuffer, sizeof( uint32_t ) ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *crystal_error_ppm = ( ( uint32_t ) rbuffer[0] << 24 ) + ( ( uint32_t ) rbuffer[1] << 16 ) + + ( ( uint32_t ) rbuffer[2] << 8 ) + ( ( uint32_t ) rbuffer[3] ); + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_set_crystal_error( const void* context, const uint32_t crystal_error_ppm ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_CRYSTAL_ERROR_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_BSP >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_BSP, + LR1121_MODEM_SET_CRYSTAL_ERROR_CMD, + ( uint8_t )( crystal_error_ppm >> 24 ), + ( uint8_t )( crystal_error_ppm >> 16 ), + ( uint8_t )( crystal_error_ppm >> 8 ), + ( uint8_t ) crystal_error_ppm, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SET_CRYSTAL_ERROR_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_xosc_capa_trim_a_b( const void* context, uint8_t* capa_trim_a, + uint8_t* capa_trim_b ) +{ + uint8_t rbuffer[sizeof( uint16_t )] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_XOSC_CAPA_TRIM_A_B_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_BSP >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_BSP, + LR1121_MODEM_GET_XOSC_CAPA_TRIM_A_B, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_XOSC_CAPA_TRIM_A_B_CMD_LENGTH, rbuffer, sizeof( uint16_t ) ); + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *capa_trim_a = rbuffer[0]; + *capa_trim_b = rbuffer[1]; + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_set_xosc_capa_trim_a_b( const void* context, const uint8_t capa_trim_a, + const uint8_t capa_trim_b ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_XOSC_CAPA_TRIM_A_B_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_BSP >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_BSP, + LR1121_MODEM_SET_XOSC_CAPA_TRIM_A_B, + capa_trim_a, + capa_trim_b, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_XOSC_CAPA_TRIM_A_B_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_tx_power_consumption_ua( + const void* context, lr1121_modem_tx_power_consumption_list_t consumption_per_power ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_TX_POWER_CONSUMPTION_UA_CMD_LENGTH] = { + ( LR1121_MODEM_GROUP_ID_BSP >> 8 ) & 0xFF, + LR1121_MODEM_GROUP_ID_BSP & 0xFF, + LR1121_MODEM_GET_TX_POWER_CONSUMPTION_UA, + }; + + uint8_t rbuffer[LR1121_MODEM_GET_TX_POWER_CONSUMPTION_RBUFFER_LENGTH] = { 0 }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_TX_POWER_CONSUMPTION_UA_CMD_LENGTH, rbuffer, + LR1121_MODEM_GET_TX_POWER_CONSUMPTION_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + for( uint8_t index_power = 0; index_power < LR1121_MODEM_NB_OUTPUT_POWER_CONFIG_BLOCKS; index_power++ ) + { + lr1121_modem_tx_power_consumption_value_t* local_value = &consumption_per_power[index_power]; + local_value->tx_power_dbm = rbuffer[index_power * 5]; + local_value->consumed_power_ua = + ( rbuffer[index_power * 5 + 1] << 24 ) + ( rbuffer[index_power * 5 + 2] << 16 ) + + ( rbuffer[index_power * 5 + 3] << 8 ) + ( rbuffer[index_power * 5 + 4] << 0 ); + } + } + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_set_tx_power_consumption_ua( + const void* context, const lr1121_modem_tx_power_consumption_value_t* consumption_per_power, + uint8_t n_consumption_per_power ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_TX_POWER_CONSUMPTION_UA_CMD_LENGTH] = { + ( LR1121_MODEM_GROUP_ID_BSP >> 8 ) & 0xFF, + LR1121_MODEM_GROUP_ID_BSP & 0xFF, + LR1121_MODEM_SET_TX_POWER_CONSUMPTION_UA, + }; + + // Array of maximal size is allocated + uint8_t dbuffer[LR1121_MODEM_SET_TX_POWER_CONSUMPTION_UA_DBUFFER_MAX_LENGTH] = { 0 }; + + for( uint8_t power_table_index = 0; power_table_index < n_consumption_per_power; power_table_index++ ) + { + const lr1121_modem_tx_power_consumption_value_t local_consumption_value = + consumption_per_power[power_table_index]; + const uint8_t local_dbuffer_index = power_table_index * LR1121_MODEM_TX_POWER_CONSUMPTION_SIZE_BYTE; + + dbuffer[local_dbuffer_index] = local_consumption_value.tx_power_dbm; + dbuffer[local_dbuffer_index + 1] = ( uint8_t )( local_consumption_value.consumed_power_ua >> 24 ); + dbuffer[local_dbuffer_index + 2] = ( uint8_t )( local_consumption_value.consumed_power_ua >> 16 ); + dbuffer[local_dbuffer_index + 3] = ( uint8_t )( local_consumption_value.consumed_power_ua >> 8 ); + dbuffer[local_dbuffer_index + 4] = ( uint8_t ) local_consumption_value.consumed_power_ua; + } + + // Number of meaningful bytes from dbuffer to transmit to the chip + const uint8_t dbuffer_size = n_consumption_per_power * LR1121_MODEM_TX_POWER_CONSUMPTION_SIZE_BYTE; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_TX_POWER_CONSUMPTION_UA_CMD_LENGTH, dbuffer, dbuffer_size ); +} + +lr1121_modem_response_code_t lr1121_modem_get_lora_rx_power_consumption_ua( + const void* context, lr1121_modem_rx_power_consumption_t* rx_consumption ) +{ + return lr1121_modem_get_rx_power_consumption_ua( context, rx_consumption, + LR1121_MODEM_GET_LORA_RX_POWER_CONSUMPTION_UA ); +} + +lr1121_modem_response_code_t lr1121_modem_set_lora_rx_power_consumption_ua( + const void* context, const lr1121_modem_rx_power_consumption_t* rx_consumption ) +{ + return lr1121_modem_set_rx_power_consumption_ua( context, rx_consumption, + LR1121_MODEM_SET_LORA_RX_POWER_CONSUMPTION_UA ); +} + +lr1121_modem_response_code_t lr1121_modem_get_gfsk_rx_power_consumption_ua( + const void* context, lr1121_modem_rx_power_consumption_t* rx_consumption ) +{ + return lr1121_modem_get_rx_power_consumption_ua( context, rx_consumption, + LR1121_MODEM_GET_GFSK_RX_POWER_CONSUMPTION_UA ); +} + +lr1121_modem_response_code_t lr1121_modem_set_gfsk_rx_power_consumption_ua( + const void* context, const lr1121_modem_rx_power_consumption_t* rx_consumption ) +{ + return lr1121_modem_set_rx_power_consumption_ua( context, rx_consumption, + LR1121_MODEM_SET_GFSK_RX_POWER_CONSUMPTION_UA ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +uint32_t lr1121_uint8_to_uint32( const uint8_t value[4] ) +{ + return ( ( ( uint32_t ) value[0] ) << 24 ) + ( ( ( uint32_t ) value[1] ) << 16 ) + + ( ( ( uint32_t ) value[2] ) << 8 ) + ( ( ( uint32_t ) value[3] ) ); +} + +lr1121_modem_response_code_t lr1121_modem_get_rx_power_consumption_ua( + const void* context, lr1121_modem_rx_power_consumption_t* rx_consumption, uint8_t opcode ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_RX_POWER_CONSUMPTION_UA_CMD_LENGTH] = { + ( LR1121_MODEM_GROUP_ID_BSP >> 8 ) & 0xFF, + LR1121_MODEM_GROUP_ID_BSP & 0xFF, + opcode, + }; + + uint8_t rbuffer[LR1121_MODEM_GET_RX_POWER_CONSUMPTION_RBUFFER_LENGTH] = { 0 }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_RX_POWER_CONSUMPTION_UA_CMD_LENGTH, rbuffer, + LR1121_MODEM_GET_RX_POWER_CONSUMPTION_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + rx_consumption->consumption_rx_boosted_off_ua = lr1121_uint8_to_uint32( rbuffer ); + rx_consumption->consumption_rx_boosted_on_ua = lr1121_uint8_to_uint32( rbuffer + 4 ); + } + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_set_rx_power_consumption_ua( + const void* context, const lr1121_modem_rx_power_consumption_t* rx_consumption, uint8_t opcode ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_RX_POWER_CONSUMPTION_UA_CMD_LENGTH] = { + ( LR1121_MODEM_GROUP_ID_BSP >> 8 ) & 0xFF, + LR1121_MODEM_GROUP_ID_BSP & 0xFF, + opcode, + ( uint8_t )( rx_consumption->consumption_rx_boosted_off_ua >> 24 ), + ( uint8_t )( rx_consumption->consumption_rx_boosted_off_ua >> 16 ), + ( uint8_t )( rx_consumption->consumption_rx_boosted_off_ua >> 8 ), + ( uint8_t ) rx_consumption->consumption_rx_boosted_off_ua, + ( uint8_t )( rx_consumption->consumption_rx_boosted_on_ua >> 24 ), + ( uint8_t )( rx_consumption->consumption_rx_boosted_on_ua >> 16 ), + ( uint8_t )( rx_consumption->consumption_rx_boosted_on_ua >> 8 ), + ( uint8_t ) rx_consumption->consumption_rx_boosted_on_ua, + }; + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_RX_POWER_CONSUMPTION_UA_CMD_LENGTH, 0, 0 ); +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_driver_version.c b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_driver_version.c new file mode 100755 index 0000000..bb1b298 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_driver_version.c @@ -0,0 +1,83 @@ +/** + * @file lr1121_modem_driver_version.c + * + * @brief Implementation of the get version function + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr1121_modem_driver_version.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +const char* lr1121_modem_driver_version_get_version_string( void ) +{ + return ( const char* ) LR1121_MODEM_DRIVER_VERSION; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_helper.c b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_helper.c new file mode 100755 index 0000000..a04d7a2 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_helper.c @@ -0,0 +1,170 @@ +/*! + * @file lr1121_modem_helper.c + * + * @brief helper functions implementation for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr1121_modem_helper.h" +#include "lr1121_modem_common.h" +#include "lr1121_modem_modem.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr1121_modem_helper_status_t lr1121_modem_helper_get_event_data( const void* context, + lr1121_modem_event_t* modem_event ) +{ + lr1121_modem_helper_status_t status = LR1121_MODEM_HELPER_STATUS_ERROR; + lr1121_modem_event_fields_t event_fields = { 0 }; + + const lr1121_modem_response_code_t modem_response_code = lr1121_modem_get_event( context, &event_fields ); + + if( modem_response_code == LR1121_MODEM_RESPONSE_CODE_OK ) + { + status = LR1121_MODEM_HELPER_STATUS_OK; + modem_event->event_type = event_fields.event_type; + modem_event->missed_events = event_fields.missed_events_count; + + switch( modem_event->event_type ) + { + case LR1121_MODEM_LORAWAN_EVENT_RESET: + modem_event->event_data.reset.count = event_fields.data; + break; + case LR1121_MODEM_LORAWAN_EVENT_TX_DONE: + modem_event->event_data.txdone.status = ( lr1121_modem_tx_done_event_t )( event_fields.data >> 8 ); + break; + case LR1121_MODEM_LORAWAN_EVENT_LINK_CHECK: + modem_event->event_data.link_check.status = ( lr1121_modem_link_check_event_t )( event_fields.data >> 8 ); + break; + case LR1121_MODEM_LORAWAN_EVENT_LORAWAN_MAC_TIME: + modem_event->event_data.mac_time.status = ( lr1121_modem_mac_time_event_t )( event_fields.data >> 8 ); + break; + case LR1121_MODEM_LORAWAN_EVENT_CLASS_B_PING_SLOT_INFO: + modem_event->event_data.ping_slot_info.status = + ( lr1121_modem_class_b_ping_slot_info_t )( event_fields.data >> 8 ); + break; + case LR1121_MODEM_LORAWAN_EVENT_CLASS_B_STATUS: + modem_event->event_data.ping_slot_status.status = + ( lr1121_modem_class_b_ping_slot_status_t )( event_fields.data >> 8 ); + break; + case LR1121_MODEM_LORAWAN_EVENT_NEW_MULTICAST_SESSION_CLASS_C: + { + modem_event->event_data.new_multicast_class_c_groupid.mc_group_id = ( uint8_t )( event_fields.data >> 8 ); + break; + } + case LR1121_MODEM_LORAWAN_EVENT_NEW_MULTICAST_SESSION_CLASS_B: + { + modem_event->event_data.new_multicast_class_b_groupid.mc_group_id = ( uint8_t )( event_fields.data >> 8 ); + break; + } + case LR1121_MODEM_LORAWAN_EVENT_RELAY_TX_DYNAMIC: + { + modem_event->event_data.relay_tx_dynamic_status.status = + ( lr1121_modem_relay_tx_dynamic_status_t )( event_fields.data >> 8 ); + break; + } + case LR1121_MODEM_LORAWAN_EVENT_RELAY_TX_MODE: + { + modem_event->event_data.relay_tx_mode_status.status = + ( lr1121_modem_relay_tx_mode_status_t )( event_fields.data >> 8 ); + break; + } + case LR1121_MODEM_LORAWAN_EVENT_RELAY_TX_SYNC: + { + modem_event->event_data.relay_tx_sync_status.status = + ( lr1121_modem_relay_tx_sync_status_t )( event_fields.data >> 8 ); + break; + } + case LR1121_MODEM_LORAWAN_EVENT_FUOTA_DONE: + { + modem_event->event_data.fuota_status.status = + ( lr1121_modem_fuota_status_t )( event_fields.data >> 8 ) & 0x00FF; + break; + } + case LR1121_MODEM_LORAWAN_EVENT_TEST_MODE: + { + modem_event->event_data.test_mode_status.status = + ( lr1121_modem_test_mode_status_t )( event_fields.data >> 8 ) & 0x00FF; + break; + } + case LR1121_MODEM_LORAWAN_EVENT_REGIONAL_DUTY_CYCLE: + { + modem_event->event_data.regional_duty_cycle_status.status = + ( lr1121_modem_regional_duty_cycle_status_t )( ( uint8_t )( event_fields.data >> 8 ) ); + break; + } + default: + break; + } + } + + return status; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_lorawan.c b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_lorawan.c new file mode 100755 index 0000000..c1f746c --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_lorawan.c @@ -0,0 +1,1401 @@ +/*! + * @file lr1121_modem_lorawan.c + * + * @brief LoRaWAN driver implementation for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ +#include "lr1121_modem_lorawan.h" +#include "lr1121_modem_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR1121_MODEM_GET_LORAWAN_VERSION_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_DEV_EUI_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_DEV_EUI_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_JOIN_EUI_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_JOIN_EUI_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_NWK_KEY_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_APP_KEY_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_DERIVE_KEYS_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_CLASS_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_CLASS_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_GET_REGION_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_REGION_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_JOIN_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_LEAVE_NETWORK_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_NEXT_TX_MAX_PAYLOAD_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_REQUEST_TX_CMD_LENGTH ( 3 + 2 ) +#define LR1121_MODEM_REQUEST_EMPTY_TX_CMD_LENGTH ( 3 + 3 ) +#define LR1121_MODEM_EMERGENCY_TX_CMD_LENGTH ( 3 + 2 ) +#define LR1121_MODEM_GET_DOWNLINK_DATA_SIZE_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_DOWNLINK_DATA_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_DOWNLINK_METADATA_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_LOST_CONNECTION_COUNTER_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_NETWORK_TYPE_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_NETWORK_TYPE_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_GET_CERTIFICATION_MODE_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_CERTIFICATION_MODE_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_GET_DUTY_CYCLE_STATUS_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_AVAILABLE_DATA_RATE_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_ADR_PROFILE_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_ADR_PROFILE_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_SET_JOIN_DATA_RATE_DISTRIBUTION_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_NB_TRANS_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_NB_TRANS_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_GET_ADR_ACK_LIMIT_DELAY_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_ADR_ACK_LIMIT_DELAY_CMD_LENGTH ( 3 + 2 ) +#define LR1121_MODEM_GET_LBT_STATE_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_LBT_STATE_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_GET_LBT_PARAMS_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_LBT_PARAMS_CMD_LENGTH ( 3 + 10 ) +#define LR1121_MODEM_GET_CSMA_STATE_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_CSMA_STATE_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_GET_CSMA_PARAMS_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_CSMA_PARAMS_CMD_LENGTH ( 3 + 3 ) +#define LR1121_MODEM_LORAWAN_MAC_REQUEST_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_GET_LORAWAN_MAC_TIME_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_LINK_CHECK_DATA_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_BATTERY_LEVEL_CMD_LENGTH ( 3 + 2 ) +#define LR1121_MODEM_GET_CLASS_B_PING_SLOT_PERIODICITY_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_CLASS_B_PING_SLOT_PERIODICITY_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_GET_MULTICAST_GROUP_CONFIG_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_SET_MULTICAST_GROUP_CONFIG_CMD_LENGTH ( 3 + 37 ) +#define LR1121_MODEM_START_SESSION_MULTICAST_CLASS_C_CMD_LENGTH ( 3 + 6 ) +#define LR1121_MODEM_GET_MULTICAST_CLASS_C_SESSION_STATUS_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_STOP_SESSION_MULTICAST_CLASS_C_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_STOP_ALL_SESSION_MULTICAST_CLASS_C_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_START_SESSION_MULTICAST_CLASS_B_CMD_LENGTH ( 3 + 7 ) +#define LR1121_MODEM_GET_MULTICAST_CLASS_B_SESSION_STATUS_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_STOP_SESSION_MULTICAST_CLASS_B_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_STOP_ALL_SESSION_MULTICAST_CLASS_B_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_START_ALC_SYNC_SERVICE_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_STOP_ALC_SYNC_SERVICE_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_ALC_SYNC_GET_TIME_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_ALC_SYNC_TRIG_REQUEST_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_FUOTA_GET_FILE_SIZE_CRC_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_FUOTA_GET_FILE_FRAGMENT_CMD_LENGTH ( 3 + 8 ) + +#define LR1121_MODEM_GET_LORAWAN_VERSION_RBUFFER_LENGTH ( 8 ) +#define LR1121_MODEM_DEV_EUI_BUFFER_LENGTH ( 8 ) +#define LR1121_MODEM_JOIN_EUI_BUFFER_LENGTH ( 8 ) +#define LR1121_MODEM_GET_DOWNLINK_DATA_SIZE_RBUFFER_LENGTH ( 2 ) +#define LR1121_MODEM_DOWNLINK_METADATA_RBUFFER_LENGTH ( 11 ) +#define LR1121_MODEM_LBT_PARAMS_RBUFFER_LENGTH ( 10 ) +#define LR1121_MODEM_CSMA_PARAMS_RBUFFER_LENGTH ( 3 ) +#define LR1121_MODEM_GET_LORAWAN_MAC_TIME_RBUFFER_LENGTH ( 8 ) +#define LR1121_MODEM_GET_LOST_CONNECTION_COUNTER_RBUFFER_LENGTH ( 6 ) +#define LR1121_MODEM_GET_MULTICAST_GROUP_CONFIG_RBUFFER_LENGTH ( 4 ) +#define LR1121_MODEM_GET_MULTICAST_CLASS_C_SESSION_STATUS_RBUFFER_LENGTH ( 6 ) +#define LR1121_MODEM_GET_MULTICAST_CLASS_B_SESSION_STATUS_RBUFFER_LENGTH ( 8 ) +#define LR1121_MODEM_ALC_SYNC_GET_TIME_RBUFFER_LENGTH ( 4 ) +#define LR1121_MODEM_FUOTA_GET_FILE_SIZE_CRC_RBUFFER_LENGTH ( 8 ) + +#define LR1121_MODEM_NWKSKEY_LENGTH ( 16 ) +#define LR1121_MODEM_APPSKEY_LENGTH ( 16 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operation code command + */ +enum +{ + LR1121_MODEM_GET_LORAWAN_VERSION_CMD = 0x00, + LR1121_MODEM_GET_DEV_EUI_CMD = 0x01, + LR1121_MODEM_SET_DEV_EUI_CMD = 0x02, + LR1121_MODEM_GET_JOIN_EUI_CMD = 0x03, + LR1121_MODEM_SET_JOIN_EUI_CMD = 0x04, + LR1121_MODEM_SET_NWK_KEY_CMD = 0x05, + LR1121_MODEM_SET_APP_KEY_CMD = 0x06, + LR1121_MODEM_DERIVE_KEYS_CMD = 0x07, + LR1121_MODEM_GET_CLASS_CMD = 0x08, + LR1121_MODEM_SET_CLASS_CMD = 0x09, + LR1121_MODEM_GET_REGION_CMD = 0x0B, + LR1121_MODEM_SET_REGION_CMD = 0x0C, + LR1121_MODEM_JOIN_CMD = 0x0D, + LR1121_MODEM_LEAVE_NETWORK_CMD = 0x0E, + LR1121_MODEM_GET_NEXT_TX_MAX_PAYLOAD_CMD = 0x11, + LR1121_MODEM_REQUEST_TX_CMD = 0x12, + LR1121_MODEM_REQUEST_EMPTY_TX_CMD = 0x13, + LR1121_MODEM_EMERGENCY_TX_CMD = 0x14, + LR1121_MODEM_GET_DOWNLINK_DATA_SIZE_CMD = 0x15, + LR1121_MODEM_GET_DOWNLINK_DATA_CMD = 0x16, + LR1121_MODEM_GET_DOWNLINK_METADATA_CMD = 0x17, + LR1121_MODEM_GET_LOST_CONNECTION_COUNTER_CMD = 0x18, + LR1121_MODEM_GET_NETWORK_TYPE_CMD = 0x19, + LR1121_MODEM_SET_NETWORK_TYPE_CMD = 0x1A, + LR1121_MODEM_GET_CERTIFICATION_MODE_CMD = 0x1B, + LR1121_MODEM_SET_CERTIFICATION_MODE_CMD = 0x1C, + LR1121_MODEM_GET_DUTY_CYCLE_STATUS_CMD = 0x1D, + LR1121_MODEM_GET_AVAILABLE_DATA_RATE_CMD = 0x1F, + LR1121_MODEM_GET_ADR_PROFILE_CMD = 0x20, + LR1121_MODEM_SET_ADR_PROFILE_CMD = 0x21, + LR1121_MODEM_SET_JOIN_DATA_RATE_DISTRIBUTION_CMD = 0x22, + LR1121_MODEM_GET_NB_TRANS_CMD = 0x23, + LR1121_MODEM_SET_NB_TRANS_CMD = 0x24, + LR1121_MODEM_GET_ADR_ACK_LIMIT_DELAY_CMD = 0x25, + LR1121_MODEM_SET_ADR_ACK_LIMIT_DELAY_CMD = 0x26, + LR1121_MODEM_GET_LBT_STATE_CMD = 0x27, + LR1121_MODEM_SET_LBT_STATE_CMD = 0x28, + LR1121_MODEM_GET_LBT_PARAMS_CMD = 0x29, + LR1121_MODEM_SET_LBT_PARAMS_CMD = 0x2A, + LR1121_MODEM_GET_CSMA_STATE_CMD = 0x2B, + LR1121_MODEM_SET_CSMA_STATE_CMD = 0x2C, + LR1121_MODEM_GET_CSMA_PARAMS_CMD = 0x2D, + LR1121_MODEM_SET_CSMA_PARAMS_CMD = 0x2E, + LR1121_MODEM_LORAWAN_MAC_REQUEST_CMD = 0x2F, + LR1121_MODEM_GET_LORAWAN_MAC_TIME_CMD = 0x30, + LR1121_MODEM_GET_LINK_CHECK_DATA_CMD = 0x31, + LR1121_MODEM_SET_BATTERY_LEVEL_CMD = 0x32, + LR1121_MODEM_GET_CLASS_B_PING_SLOT_PERIODICITY_CMD = 0x34, + LR1121_MODEM_SET_CLASS_B_PING_SLOT_PERIODICITY_CMD = 0x35, + LR1121_MODEM_GET_MULTICAST_GROUP_CONFIG_CMD = 0x36, + LR1121_MODEM_SET_MULTICAST_GROUP_CONFIG_CMD = 0x37, + LR1121_MODEM_START_SESSION_MULTICAST_CLASS_C_CMD = 0x38, + LR1121_MODEM_GET_MULTICAST_CLASS_C_SESSION_STATUS_CMD = 0x39, + LR1121_MODEM_STOP_SESSION_MULTICAST_CLASS_C_CMD = 0x3A, + LR1121_MODEM_STOP_ALL_SESSION_MULTICAST_CLASS_C_CMD = 0x3B, + LR1121_MODEM_START_SESSION_MULTICAST_CLASS_B_CMD = 0x3C, + LR1121_MODEM_GET_MULTICAST_CLASS_B_SESSION_STATUS_CMD = 0x3D, + LR1121_MODEM_STOP_SESSION_MULTICAST_CLASS_B_CMD = 0x3E, + LR1121_MODEM_STOP_ALL_SESSION_MULTICAST_CLASS_B_CMD = 0x3F, + LR1121_MODEM_START_ALC_SYNC_SERVICE_CMD = 0x42, + LR1121_MODEM_STOP_ALC_SYNC_SERVICE_CMD = 0x43, + LR1121_MODEM_ALC_SYNC_GET_TIME_CMD = 0x44, + LR1121_MODEM_ALC_SYNC_TRIG_REQUEST_CMD = 0X45, + LR1121_MODEM_FUOTA_GET_FILE_SIZE_CRC_CMD = 0x46, + LR1121_MODEM_FUOTA_GET_FILE_FRAGMENT_CMD = 0x47, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/** + * @brief Helper function that convert an array of uint8_t into a uint32_t single value + * + * @warning It is up to the caller to ensure that value points to an array of at least sizeof(uint32_t) elements. + * + * @param [in] value Array of uint8_t to be translated into a uint32_t + * + * @returns 32-bit value + */ +static uint32_t lr1121_uint8_to_uint32( const uint8_t value[4] ); + +/** + * @brief Compute CRC32 + * + * @param [in,out] pcrc Pointer to the CRC. Used as initial value and as output value + * @param buf The buffer to compute the CRC on. It is up to the caller to ensure it is at least @ref len byte long + * @param len Length of buffer to compute the CRC on + */ +static void lr1121_modem_fuota_crc32( uint32_t* pcrc, const uint8_t* buf, uint32_t len ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr1121_modem_response_code_t lr1121_modem_get_lorawan_version( const void* context, + lr1121_modem_lorawan_version_t* lorawan_version ) +{ + uint8_t rbuffer[LR1121_MODEM_GET_LORAWAN_VERSION_RBUFFER_LENGTH] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_LORAWAN_VERSION_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_LORAWAN_VERSION_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_LORAWAN_VERSION_CMD_LENGTH, rbuffer, + LR1121_MODEM_GET_LORAWAN_VERSION_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + lorawan_version->lorawan_major = rbuffer[0]; + lorawan_version->lorawan_minor = rbuffer[1]; + lorawan_version->lorawan_patch = rbuffer[2]; + lorawan_version->lorawan_revision = rbuffer[3]; + lorawan_version->rp_major = rbuffer[4]; + lorawan_version->rp_minor = rbuffer[5]; + lorawan_version->rp_patch = rbuffer[6]; + lorawan_version->rp_revision = rbuffer[7]; + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_get_dev_eui( const void* context, lr1121_modem_dev_eui_t dev_eui ) +{ + uint8_t rbuffer[LR1121_MODEM_DEV_EUI_BUFFER_LENGTH] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_DEV_EUI_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_DEV_EUI_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_DEV_EUI_CMD_LENGTH, rbuffer, LR1121_MODEM_DEV_EUI_BUFFER_LENGTH ); + + for( uint8_t i = 0; i < LR1121_MODEM_DEV_EUI_BUFFER_LENGTH; i++ ) + { + dev_eui[i] = rbuffer[i]; + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_set_dev_eui( const void* context, const lr1121_modem_dev_eui_t dev_eui ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_DEV_EUI_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_DEV_EUI_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_DEV_EUI_CMD_LENGTH, dev_eui, LR1121_MODEM_DEV_EUI_BUFFER_LENGTH ); +} + +lr1121_modem_response_code_t lr1121_modem_get_join_eui( const void* context, lr1121_modem_join_eui_t join_eui ) +{ + uint8_t rbuffer[LR1121_MODEM_JOIN_EUI_BUFFER_LENGTH] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_JOIN_EUI_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_JOIN_EUI_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_JOIN_EUI_CMD_LENGTH, rbuffer, LR1121_MODEM_JOIN_EUI_BUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + for( uint8_t i = 0; i < LR1121_MODEM_JOIN_EUI_BUFFER_LENGTH; i++ ) + { + join_eui[i] = rbuffer[i]; + } + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_set_join_eui( const void* context, const lr1121_modem_join_eui_t join_eui ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_JOIN_EUI_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_JOIN_EUI_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_JOIN_EUI_CMD_LENGTH, join_eui, LR1121_MODEM_JOIN_EUI_BUFFER_LENGTH ); +} + +lr1121_modem_response_code_t lr1121_modem_set_nwk_key( const void* context, const lr1121_modem_nwk_key_t nwk_key ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_NWK_KEY_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_NWK_KEY_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_NWK_KEY_CMD_LENGTH, nwk_key, LR1121_MODEM_NWK_KEY_LENGTH ); +} + +lr1121_modem_response_code_t lr1121_modem_set_app_key( const void* context, const lr1121_modem_app_key_t app_key ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_APP_KEY_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_APP_KEY_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_APP_KEY_CMD_LENGTH, app_key, LR1121_MODEM_APP_KEY_LENGTH ); +} + +lr1121_modem_response_code_t lr1121_modem_derive_keys( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_DERIVE_KEYS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_DERIVE_KEYS_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_DERIVE_KEYS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_class( const void* context, lr1121_modem_classes_t* modem_class ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_CLASS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_CLASS_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( context, cbuffer, LR1121_MODEM_GET_CLASS_CMD_LENGTH, + ( uint8_t* ) modem_class, sizeof( uint8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_set_class( const void* context, const lr1121_modem_classes_t modem_class ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_CLASS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_CLASS_CMD, + ( uint8_t ) modem_class, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, LR1121_MODEM_SET_CLASS_CMD_LENGTH, + 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_region( const void* context, lr1121_modem_regions_t* region ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_REGION_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_REGION_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( context, cbuffer, LR1121_MODEM_GET_REGION_CMD_LENGTH, + ( uint8_t* ) region, sizeof( uint8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_set_region( const void* context, const lr1121_modem_regions_t region ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_REGION_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_REGION_CMD, + ( uint8_t ) region, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SET_REGION_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_join( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_JOIN_CMD_LENGTH] = { + ( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ) & 0xFF, + LR1121_MODEM_GROUP_ID_LORAWAN & 0xFF, + LR1121_MODEM_JOIN_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, LR1121_MODEM_JOIN_CMD_LENGTH, 0, + 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_leave_network( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_LEAVE_NETWORK_CMD_LENGTH] = { + ( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ) & 0xFF, + LR1121_MODEM_GROUP_ID_LORAWAN & 0xFF, + LR1121_MODEM_LEAVE_NETWORK_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_LEAVE_NETWORK_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_next_tx_max_payload( const void* context, uint8_t* tx_max_payload ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_NEXT_TX_MAX_PAYLOAD_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_NEXT_TX_MAX_PAYLOAD_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_NEXT_TX_MAX_PAYLOAD_CMD_LENGTH, tx_max_payload, sizeof( uint8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_request_tx( const void* context, const uint8_t port, + const lr1121_modem_uplink_type_t uplink_type, const uint8_t* data, + const uint8_t length ) +{ + const uint8_t cbuffer[LR1121_MODEM_REQUEST_TX_CMD_LENGTH] = { + ( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ) & 0xFF, + LR1121_MODEM_GROUP_ID_LORAWAN & 0xFF, + LR1121_MODEM_REQUEST_TX_CMD, + port, + ( uint8_t ) uplink_type, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_REQUEST_TX_CMD_LENGTH, data, length ); +} + +lr1121_modem_response_code_t lr1121_modem_request_empty_tx( const void* context, bool is_fport_populated, + const uint8_t port, + const lr1121_modem_uplink_type_t uplink_type ) +{ + const uint8_t cbuffer[LR1121_MODEM_REQUEST_EMPTY_TX_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_REQUEST_EMPTY_TX_CMD, + ( is_fport_populated ? 0x01 : 0x00 ), + port, + ( uint8_t ) uplink_type, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_REQUEST_EMPTY_TX_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_emergency_tx( const void* context, const uint8_t port, + const lr1121_modem_uplink_type_t uplink_type, + const uint8_t* data, const uint8_t length ) +{ + const uint8_t cbuffer[LR1121_MODEM_EMERGENCY_TX_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_EMERGENCY_TX_CMD, + port, + ( uint8_t ) uplink_type, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_EMERGENCY_TX_CMD_LENGTH, data, length ); +} + +lr1121_modem_response_code_t lr1121_modem_get_downlink_data_size( const void* context, uint8_t* downlink_data_size, + uint8_t* remaining_downlinks ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_DOWNLINK_DATA_SIZE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_DOWNLINK_DATA_SIZE_CMD, + }; + + uint8_t rbuffer[LR1121_MODEM_GET_DOWNLINK_DATA_SIZE_RBUFFER_LENGTH] = { 0x00 }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_DOWNLINK_DATA_SIZE_CMD_LENGTH, rbuffer, + LR1121_MODEM_GET_DOWNLINK_DATA_SIZE_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *downlink_data_size = rbuffer[0]; + *remaining_downlinks = rbuffer[1]; + } + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_get_downlink_data( const void* context, uint8_t* buffer, + uint8_t downlink_data_size ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_DOWNLINK_DATA_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_DOWNLINK_DATA_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_DOWNLINK_DATA_CMD_LENGTH, buffer, downlink_data_size ); +} + +lr1121_modem_response_code_t lr1121_modem_get_downlink_metadata( const void* context, + lr1121_modem_downlink_metadata_t* metadata ) +{ + uint8_t rbuffer[LR1121_MODEM_DOWNLINK_METADATA_RBUFFER_LENGTH] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_DOWNLINK_METADATA_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_DOWNLINK_METADATA_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_DOWNLINK_METADATA_CMD_LENGTH, rbuffer, + LR1121_MODEM_DOWNLINK_METADATA_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + metadata->stack_id = rbuffer[0]; + metadata->rssi = ( int16_t )( ( int8_t ) rbuffer[1] - 64 ); + metadata->snr_integer = ( ( int8_t ) rbuffer[2] ) >> 2; + metadata->snr_quarter = rbuffer[2] & 0x03; + metadata->window = ( lr1121_modem_downlink_window_t ) rbuffer[3]; + metadata->fport = rbuffer[4]; + metadata->fpending_bit = rbuffer[5]; + metadata->frequency_hz = lr1121_uint8_to_uint32( rbuffer + 6 ); + metadata->datarate = rbuffer[10]; + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_get_lost_connection_counter( const void* context, + uint16_t* lost_connection_counter, + uint32_t* lost_connection_since_sec ) +{ + uint8_t rbuffer[LR1121_MODEM_GET_LOST_CONNECTION_COUNTER_RBUFFER_LENGTH] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_LOST_CONNECTION_COUNTER_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_LOST_CONNECTION_COUNTER_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_LOST_CONNECTION_COUNTER_CMD_LENGTH, rbuffer, + LR1121_MODEM_GET_LOST_CONNECTION_COUNTER_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *lost_connection_counter = ( ( uint16_t ) rbuffer[0] << 8 ) + ( ( uint16_t ) rbuffer[1] ); + *lost_connection_since_sec = lr1121_uint8_to_uint32( rbuffer + 2 ); + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_get_network_type( const void* context, + lr1121_modem_network_type_t* network_type ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_NETWORK_TYPE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_NETWORK_TYPE_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_NETWORK_TYPE_CMD_LENGTH, ( uint8_t* ) network_type, sizeof( uint8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_set_network_type( const void* context, + const lr1121_modem_network_type_t network_type ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_NETWORK_TYPE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_NETWORK_TYPE_CMD, + ( uint8_t ) network_type, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SET_NETWORK_TYPE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_certification_mode( const void* context, + lr1121_modem_certification_mode_t* enable ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_CERTIFICATION_MODE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_CERTIFICATION_MODE_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_CERTIFICATION_MODE_CMD_LENGTH, ( uint8_t* ) enable, sizeof( uint8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_set_certification_mode( const void* context, + const lr1121_modem_certification_mode_t enable ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_CERTIFICATION_MODE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_CERTIFICATION_MODE_CMD, + ( uint8_t ) enable, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_CERTIFICATION_MODE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_duty_cycle_status( const void* context, int32_t* duty_cycle ) +{ + uint8_t rbuffer[sizeof( int32_t )] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_DUTY_CYCLE_STATUS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_DUTY_CYCLE_STATUS_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_DUTY_CYCLE_STATUS_CMD_LENGTH, rbuffer, sizeof( int32_t ) ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *duty_cycle = lr1121_uint8_to_uint32( rbuffer ); + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_get_available_data_rate( const void* context, uint16_t* available_data_rate ) +{ + uint8_t rbuffer[sizeof( uint16_t )] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_AVAILABLE_DATA_RATE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_AVAILABLE_DATA_RATE_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_AVAILABLE_DATA_RATE_CMD_LENGTH, rbuffer, sizeof( uint16_t ) ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *available_data_rate = ( ( uint16_t ) rbuffer[0] << 8 ) + rbuffer[1]; + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_get_adr_profile( const void* context, + lr1121_modem_adr_profiles_t* adr_profile ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_ADR_PROFILE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_ADR_PROFILE_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_ADR_PROFILE_CMD_LENGTH, ( uint8_t* ) adr_profile, sizeof( uint8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_set_adr_profile( const void* context, + const lr1121_modem_adr_profiles_t adr_profile, + const uint8_t* adr_custom_list ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_ADR_PROFILE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_ADR_PROFILE_CMD, + ( uint8_t ) adr_profile, + }; + + const uint8_t data_length = ( adr_profile == LR1121_MODEM_ADR_PROFILE_CUSTOM ) ? 16 : 0; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_ADR_PROFILE_CMD_LENGTH, adr_custom_list, data_length ); +} + +lr1121_modem_response_code_t lr1121_modem_set_join_data_rate_distribution( + const void* context, const uint8_t distribution[LR1121_MODEM_DATARATE_DISTRIBUTION_LENGTH] ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_JOIN_DATA_RATE_DISTRIBUTION_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_JOIN_DATA_RATE_DISTRIBUTION_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_JOIN_DATA_RATE_DISTRIBUTION_CMD_LENGTH, distribution, + LR1121_MODEM_DATARATE_DISTRIBUTION_LENGTH ); +} + +lr1121_modem_response_code_t lr1121_modem_get_nb_trans( const void* context, uint8_t* nb_trans ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_NB_TRANS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_NB_TRANS_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_NB_TRANS_CMD_LENGTH, ( uint8_t* ) nb_trans, sizeof( uint8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_set_nb_trans( const void* context, const uint8_t nb_trans ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_NB_TRANS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_NB_TRANS_CMD, + nb_trans, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SET_NB_TRANS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_adr_ack_limit_delay( const void* context, uint8_t* limit, uint8_t* delay ) +{ + uint8_t rbuffer[sizeof( uint16_t )] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_ADR_ACK_LIMIT_DELAY_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_ADR_ACK_LIMIT_DELAY_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_ADR_ACK_LIMIT_DELAY_CMD_LENGTH, rbuffer, sizeof( uint16_t ) ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *limit = rbuffer[0]; + *delay = rbuffer[1]; + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_set_adr_ack_limit_delay( const void* context, const uint8_t limit, + const uint8_t delay ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_ADR_ACK_LIMIT_DELAY_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_ADR_ACK_LIMIT_DELAY_CMD, + limit, + delay, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_ADR_ACK_LIMIT_DELAY_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_lbt_state( const void* context, lr1121_modem_lbt_mode_t* enable ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_LBT_STATE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_LBT_STATE_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_LBT_STATE_CMD_LENGTH, ( uint8_t* ) enable, sizeof( uint8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_set_lbt_state( const void* context, const lr1121_modem_lbt_mode_t enable ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_LBT_STATE_CMD_LENGTH] = { + ( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ) & 0xFF, + LR1121_MODEM_GROUP_ID_LORAWAN & 0xFF, + LR1121_MODEM_SET_LBT_STATE_CMD, + enable, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SET_LBT_STATE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_set_lbt_params( const void* context, const uint32_t duration, + const int16_t threshold, const uint32_t bandwidth ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_LBT_PARAMS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_LBT_PARAMS_CMD, + ( uint8_t )( duration >> 24 ), + ( uint8_t )( duration >> 16 ), + ( uint8_t )( duration >> 8 ), + ( uint8_t )( duration ), + ( uint8_t )( threshold >> 8 ), + ( uint8_t )( threshold ), + ( uint8_t )( bandwidth >> 24 ), + ( uint8_t )( bandwidth >> 16 ), + ( uint8_t )( bandwidth >> 8 ), + ( uint8_t )( bandwidth ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SET_LBT_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_lbt_params( const void* context, uint32_t* duration, int16_t* threshold, + uint32_t* bandwidth ) +{ + uint8_t rbuffer[LR1121_MODEM_LBT_PARAMS_RBUFFER_LENGTH] = { 0 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_LBT_PARAMS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_LBT_PARAMS_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_LBT_PARAMS_CMD_LENGTH, rbuffer, LR1121_MODEM_LBT_PARAMS_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *duration = lr1121_uint8_to_uint32( rbuffer ); + + *threshold = ( ( uint32_t ) rbuffer[4] << 8 ) + ( ( uint32_t ) rbuffer[5] ); + + *bandwidth = lr1121_uint8_to_uint32( rbuffer + 6 ); + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_get_csma_state( const void* context, lr1121_modem_csma_mode_t* enable ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_CSMA_STATE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_CSMA_STATE_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_CSMA_STATE_CMD_LENGTH, ( uint8_t* ) enable, sizeof( uint8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_set_csma_state( const void* context, const lr1121_modem_csma_mode_t enable ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_CSMA_STATE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_CSMA_STATE_CMD, + enable, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SET_CSMA_STATE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_set_csma_params( const void* context, const uint8_t max_channel_change, + const lr1121_modem_csma_backoff_mode_t backoff_enable, + const uint8_t nb_backoff_max ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_CSMA_PARAMS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_CSMA_PARAMS_CMD, + max_channel_change, + ( uint8_t ) backoff_enable, + nb_backoff_max, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SET_CSMA_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_csma_params( const void* context, uint8_t* max_channel_change, + uint8_t* backoff_enable, uint8_t* nb_backoff_max ) +{ + uint8_t rbuffer[LR1121_MODEM_CSMA_PARAMS_RBUFFER_LENGTH] = { 0 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_CSMA_PARAMS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_CSMA_PARAMS_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_CSMA_PARAMS_CMD_LENGTH, rbuffer, LR1121_MODEM_CSMA_PARAMS_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *max_channel_change = rbuffer[0]; + *backoff_enable = rbuffer[1]; + *nb_backoff_max = rbuffer[2]; + } + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_mac_request_tx( const void* context, + const lr1121_modem_mac_request_bitmask_t mac_request ) +{ + const uint8_t cbuffer[LR1121_MODEM_LORAWAN_MAC_REQUEST_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_LORAWAN_MAC_REQUEST_CMD, + ( uint8_t ) mac_request, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_LORAWAN_MAC_REQUEST_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_lorawan_mac_time( const void* context, uint32_t* gps_time_sec, + uint32_t* gps_fractionnal_sec ) +{ + uint8_t rbuffer[LR1121_MODEM_GET_LORAWAN_MAC_TIME_RBUFFER_LENGTH] = { 0 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_LORAWAN_MAC_TIME_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_LORAWAN_MAC_TIME_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_LORAWAN_MAC_TIME_CMD_LENGTH, rbuffer, + LR1121_MODEM_GET_LORAWAN_MAC_TIME_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *gps_time_sec = lr1121_uint8_to_uint32( rbuffer ); + *gps_fractionnal_sec = lr1121_uint8_to_uint32( rbuffer + 4 ); + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_get_link_check_data( const void* context, uint8_t* margin, + uint8_t* gateway_count ) +{ + uint8_t rbuffer[sizeof( uint16_t )] = { 0 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_LINK_CHECK_DATA_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_LINK_CHECK_DATA_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_LINK_CHECK_DATA_CMD_LENGTH, rbuffer, sizeof( uint16_t ) ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *margin = rbuffer[0]; + *gateway_count = rbuffer[1]; + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_set_battery_level( + const void* context, const lr1121_modem_battery_level_source_value_t modem_vs_user, uint8_t value ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_BATTERY_LEVEL_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_BATTERY_LEVEL_CMD, + ( uint8_t ) modem_vs_user, + value, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SET_BATTERY_LEVEL_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_class_b_ping_slot_periodicity( + const void* context, lr1121_modem_class_b_ping_slot_t* ping_slot_periodicity ) + +{ + const uint8_t cbuffer[LR1121_MODEM_GET_CLASS_B_PING_SLOT_PERIODICITY_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_CLASS_B_PING_SLOT_PERIODICITY_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_LINK_CHECK_DATA_CMD_LENGTH, ( uint8_t* ) ping_slot_periodicity, + sizeof( uint8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_set_class_b_ping_slot_periodicity( + const void* context, const lr1121_modem_class_b_ping_slot_t ping_slot_periodicity ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_CLASS_B_PING_SLOT_PERIODICITY_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_SET_CLASS_B_PING_SLOT_PERIODICITY_CMD, + ( uint8_t ) ping_slot_periodicity, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_CLASS_B_PING_SLOT_PERIODICITY_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_multicast_group_config( const void* context, const uint8_t mc_group_id, + uint32_t* mc_group_address ) +{ + uint8_t rbuffer[LR1121_MODEM_GET_MULTICAST_GROUP_CONFIG_RBUFFER_LENGTH] = { 0 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_MULTICAST_GROUP_CONFIG_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_MULTICAST_GROUP_CONFIG_CMD, + mc_group_id, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_MULTICAST_GROUP_CONFIG_CMD_LENGTH, rbuffer, + LR1121_MODEM_GET_MULTICAST_GROUP_CONFIG_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *mc_group_address = lr1121_uint8_to_uint32( rbuffer ); + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_set_multicast_group_config( const void* context, const uint8_t mc_group_id, + const uint32_t mc_group_address, + const uint8_t* mc_nwkskey, + const uint8_t* mc_appskey ) +{ + uint8_t cbuffer[LR1121_MODEM_SET_MULTICAST_GROUP_CONFIG_CMD_LENGTH]; + + cbuffer[0] = ( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ) & 0xFF; + cbuffer[1] = LR1121_MODEM_GROUP_ID_LORAWAN & 0xFF; + cbuffer[2] = LR1121_MODEM_SET_MULTICAST_GROUP_CONFIG_CMD; + + cbuffer[3] = mc_group_id; + + cbuffer[4] = mc_group_address >> 24; + cbuffer[5] = mc_group_address >> 16; + cbuffer[6] = mc_group_address >> 8; + cbuffer[7] = mc_group_address; + + for( uint8_t i = 0; i < LR1121_MODEM_NWKSKEY_LENGTH; i++ ) + { + cbuffer[8 + i] = mc_nwkskey[i]; + } + + for( uint8_t i = 0; i < LR1121_MODEM_APPSKEY_LENGTH; i++ ) + { + cbuffer[24 + i] = mc_appskey[i]; + } + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SET_MULTICAST_GROUP_CONFIG_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_start_session_multicast_class_c( const void* context, + const uint8_t mc_group_id, + const uint32_t downlink_frequency, + const uint8_t downlink_data_rate ) +{ + const uint8_t cbuffer[LR1121_MODEM_START_SESSION_MULTICAST_CLASS_C_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_START_SESSION_MULTICAST_CLASS_C_CMD, + mc_group_id, + downlink_frequency >> 24, + downlink_frequency >> 16, + downlink_frequency >> 8, + downlink_frequency, + downlink_data_rate, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_START_SESSION_MULTICAST_CLASS_C_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_multicast_class_c_session_status( + const void* context, const uint8_t mc_group_id, + lr1121_modem_multicast_class_c_status_t* lr1121_modem_multicast_class_c_status ) +{ + uint8_t rbuffer[LR1121_MODEM_GET_MULTICAST_CLASS_C_SESSION_STATUS_RBUFFER_LENGTH] = { 0 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_MULTICAST_CLASS_C_SESSION_STATUS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_MULTICAST_CLASS_C_SESSION_STATUS_CMD, + mc_group_id, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_MULTICAST_CLASS_C_SESSION_STATUS_CMD_LENGTH, rbuffer, + LR1121_MODEM_GET_MULTICAST_CLASS_C_SESSION_STATUS_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + lr1121_modem_multicast_class_c_status->is_session_started = rbuffer[0]; + lr1121_modem_multicast_class_c_status->downlink_frequency = lr1121_uint8_to_uint32( rbuffer + 1 ); + lr1121_modem_multicast_class_c_status->downlink_datarate = rbuffer[5]; + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_stop_session_multicast_class_c( const void* context, + const uint8_t mc_group_id ) +{ + const uint8_t cbuffer[LR1121_MODEM_STOP_SESSION_MULTICAST_CLASS_C_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_STOP_SESSION_MULTICAST_CLASS_C_CMD, + mc_group_id, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_STOP_SESSION_MULTICAST_CLASS_C_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_stop_all_session_multicast_class_c( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_STOP_ALL_SESSION_MULTICAST_CLASS_C_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_STOP_ALL_SESSION_MULTICAST_CLASS_C_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_STOP_ALL_SESSION_MULTICAST_CLASS_C_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_start_session_multicast_class_b( + const void* context, const uint8_t mc_group_id, const uint32_t downlink_frequency, const uint8_t downlink_data_rate, + const lr1121_modem_class_b_ping_slot_t ping_slot ) +{ + const uint8_t cbuffer[LR1121_MODEM_START_SESSION_MULTICAST_CLASS_B_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_START_SESSION_MULTICAST_CLASS_B_CMD, + mc_group_id, + downlink_frequency >> 24, + downlink_frequency >> 16, + downlink_frequency >> 8, + downlink_frequency, + downlink_data_rate, + ping_slot, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_START_SESSION_MULTICAST_CLASS_B_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_multicast_class_b_session_status( + const void* context, const uint8_t mc_group_id, + lr1121_modem_multicast_class_b_status_t* lr1121_modem_multicast_class_b_status ) +{ + uint8_t rbuffer[LR1121_MODEM_GET_MULTICAST_CLASS_B_SESSION_STATUS_RBUFFER_LENGTH] = { 0 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_MULTICAST_CLASS_B_SESSION_STATUS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_GET_MULTICAST_CLASS_B_SESSION_STATUS_CMD, + mc_group_id, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_MULTICAST_CLASS_B_SESSION_STATUS_CMD_LENGTH, rbuffer, + LR1121_MODEM_GET_MULTICAST_CLASS_B_SESSION_STATUS_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + lr1121_modem_multicast_class_b_status->is_session_started = rbuffer[0]; + lr1121_modem_multicast_class_b_status->downlink_frequency = lr1121_uint8_to_uint32( rbuffer + 1 ); + lr1121_modem_multicast_class_b_status->downlink_datarate = rbuffer[5]; + lr1121_modem_multicast_class_b_status->is_session_waiting_for_beacon = rbuffer[6]; + lr1121_modem_multicast_class_b_status->ping_slot_periodicity = rbuffer[7]; + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_stop_session_multicast_class_b( const void* context, + const uint8_t mc_group_id ) +{ + const uint8_t cbuffer[LR1121_MODEM_STOP_SESSION_MULTICAST_CLASS_B_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_STOP_SESSION_MULTICAST_CLASS_B_CMD, + mc_group_id, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_STOP_SESSION_MULTICAST_CLASS_B_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_stop_all_session_multicast_class_b( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_STOP_ALL_SESSION_MULTICAST_CLASS_B_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_STOP_ALL_SESSION_MULTICAST_CLASS_B_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_STOP_ALL_SESSION_MULTICAST_CLASS_B_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_alc_sync_start_service( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_START_ALC_SYNC_SERVICE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_START_ALC_SYNC_SERVICE_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_START_ALC_SYNC_SERVICE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_alc_sync_stop_service( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_STOP_ALC_SYNC_SERVICE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_STOP_ALC_SYNC_SERVICE_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_STOP_ALC_SYNC_SERVICE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_alc_sync_get_time( const void* context, uint32_t* alc_sync_epoch_time ) +{ + uint8_t rbuffer[LR1121_MODEM_ALC_SYNC_GET_TIME_RBUFFER_LENGTH] = { 0 }; + + const uint8_t cbuffer[LR1121_MODEM_ALC_SYNC_GET_TIME_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_ALC_SYNC_GET_TIME_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_ALC_SYNC_GET_TIME_CMD_LENGTH, rbuffer, + LR1121_MODEM_ALC_SYNC_GET_TIME_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *alc_sync_epoch_time = lr1121_uint8_to_uint32( rbuffer ); + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_alc_sync_trig_request( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_ALC_SYNC_TRIG_REQUEST_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_ALC_SYNC_TRIG_REQUEST_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_ALC_SYNC_TRIG_REQUEST_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_fuota_get_file_size_crc( const void* context, uint32_t* file_size, + uint32_t* file_crc ) +{ + uint8_t rbuffer[LR1121_MODEM_FUOTA_GET_FILE_SIZE_CRC_RBUFFER_LENGTH] = { 0 }; + + const uint8_t cbuffer[LR1121_MODEM_FUOTA_GET_FILE_SIZE_CRC_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_FUOTA_GET_FILE_SIZE_CRC_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_FUOTA_GET_FILE_SIZE_CRC_CMD_LENGTH, rbuffer, + LR1121_MODEM_FUOTA_GET_FILE_SIZE_CRC_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *file_size = lr1121_uint8_to_uint32( rbuffer ); + *file_crc = lr1121_uint8_to_uint32( rbuffer + 4 ); + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_fuota_read_file_fragment( const void* context, uint32_t base_address, + uint16_t fragment_size, uint8_t* fragment ) +{ + const uint8_t cbuffer[LR1121_MODEM_FUOTA_GET_FILE_FRAGMENT_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_LORAWAN >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_LORAWAN, + LR1121_MODEM_FUOTA_GET_FILE_FRAGMENT_CMD, + ( uint8_t )( base_address >> 24 ), + ( uint8_t )( base_address >> 16 ), + ( uint8_t )( base_address >> 8 ), + ( uint8_t ) base_address, + 0x00, + 0x00, + ( uint8_t )( fragment_size >> 8 ), + ( uint8_t ) fragment_size, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_FUOTA_GET_FILE_FRAGMENT_CMD_LENGTH, fragment, fragment_size ); +} + +bool lr1121_modem_fuota_check_crc( const uint8_t* file, uint32_t file_size, uint32_t expected_crc ) +{ + uint32_t crc = 0; // This is used as both initial CRC value, and computed CRC value. It is important here to be + // init'd to 0 for the CRC computation + lr1121_modem_fuota_crc32( &crc, file, file_size ); + return ( crc == expected_crc ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +uint32_t lr1121_uint8_to_uint32( const uint8_t value[4] ) +{ + return ( ( ( uint32_t ) value[0] ) << 24 ) + ( ( ( uint32_t ) value[1] ) << 16 ) + + ( ( ( uint32_t ) value[2] ) << 8 ) + ( ( ( uint32_t ) value[3] ) ); +} + +void lr1121_modem_fuota_crc32( uint32_t* pcrc, const uint8_t* buf, uint32_t len ) +{ + uint32_t crc = ~( *pcrc ); + while( len-- != 0 ) + { + const uint32_t byte = *buf++; + crc = crc ^ byte; + for( uint8_t i = 0; i < 8; i++ ) + { + const uint32_t mask = -( crc & 1 ); + crc = ( crc >> 1 ) ^ ( 0xEDB88320 & mask ); + } + } + *pcrc = ~crc; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_lr_fhss.c b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_lr_fhss.c new file mode 100755 index 0000000..7dbf678 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_lr_fhss.c @@ -0,0 +1,226 @@ +/*! + * @file lr1121_modem_lr_fhss.c + * + * @brief LR_FHSS driver implementation for LR1121 + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr1121_modem_lr_fhss.h" +#include "lr1121_modem_hal.h" +#include "lr1121_modem_radio.h" +#include "lr1121_modem_radio_types.h" +#include "lr_fhss_v1_base_types.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +#define LR1121_MODEM_LR_FHSS_BUILD_FRAME_LENGTH ( 2 + 9 ) +#define LR1121_MODEM_LR_FHSS_HEADER_BITS ( 114 ) +#define LR1121_MODEM_LR_FHSS_FRAG_BITS ( 48 ) +#define LR1121_MODEM_LR_FHSS_BLOCK_PREAMBLE_BITS ( 2 ) +#define LR1121_MODEM_LR_FHSS_BLOCK_BITS ( LR1121_MODEM_LR_FHSS_FRAG_BITS + LR1121_MODEM_LR_FHSS_BLOCK_PREAMBLE_BITS ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for radio-related operations + */ +enum +{ + LR1121_MODEM_LR_FHSS_BUILD_FRAME_OC = 0x022C, +}; + +/*! + * @brief Hopping enable/disabled enumerations for \ref lr1121_modem_lr_fhss_build_frame + */ +typedef enum +{ + LR1121_MODEM_LR_FHSS_HOPPING_DISABLE = 0x00, + LR1121_MODEM_LR_FHSS_HOPPING_ENABLE = 0x01, +} lr1121_modem_lr_fhss_hopping_configuration_t; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Get the bit count and block count for a LR-FHSS frame + * + * @param [in] params Parameter structure + * @param [in] payload_length Length of physical payload, in bytes + * + * @returns Length of physical payload, in bits + */ + +static uint16_t lr1121_modem_lr_fhss_get_nb_bits( const lr_fhss_v1_params_t* params, uint16_t payload_length ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr1121_modem_response_code_t lr1121_modem_lr_fhss_init( const void* context ) +{ + const lr1121_modem_response_code_t set_packet_type_status = + lr1121_modem_radio_set_pkt_type( context, LR1121_MODEM_RADIO_PKT_TYPE_LR_FHSS ); + if( set_packet_type_status != LR1121_MODEM_RESPONSE_CODE_OK ) + { + return set_packet_type_status; + } + + const lr1121_modem_radio_mod_params_lr_fhss_t mod_lr_fhss = { + .br_in_bps = LR1121_MODEM_RADIO_LR_FHSS_BITRATE_488_BPS, + .pulse_shape = LR1121_MODEM_RADIO_LR_FHSS_PULSE_SHAPE_BT_1, + }; + + const lr1121_modem_response_code_t set_modulation_param_status = + lr1121_modem_radio_set_lr_fhss_mod_params( context, &mod_lr_fhss ); + return set_modulation_param_status; +} + +uint16_t lr1121_modem_lr_fhss_get_bit_delay_in_us( const lr1121_modem_lr_fhss_params_t* params, + uint16_t payload_length ) +{ + const uint16_t nb_bits = lr1121_modem_lr_fhss_get_nb_bits( &( params->lr_fhss_params ), payload_length ); + + const uint8_t nb_padding_bits = 1 + ( ( 32768 - nb_bits ) & 0x07 ); + + return 1600 + nb_padding_bits * 2048; +} + +lr1121_modem_response_code_t lr1121_modem_lr_fhss_build_frame( const void* context, + const lr1121_modem_lr_fhss_params_t* lr_fhss_params, + uint16_t hop_sequence_id, const uint8_t* payload, + uint8_t payload_length ) +{ + // Since the build_frame command is last, it is possible to check status through stat1 + + lr1121_modem_response_code_t status = + lr1121_modem_radio_set_lr_fhss_sync_word( context, lr_fhss_params->lr_fhss_params.sync_word ); + if( status != LR1121_MODEM_RESPONSE_CODE_OK ) + { + return status; + } + + const uint8_t cbuffer[LR1121_MODEM_LR_FHSS_BUILD_FRAME_LENGTH] = { + ( uint8_t )( LR1121_MODEM_LR_FHSS_BUILD_FRAME_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_LR_FHSS_BUILD_FRAME_OC >> 0 ), + ( uint8_t ) lr_fhss_params->lr_fhss_params.header_count, + ( uint8_t ) lr_fhss_params->lr_fhss_params.cr, + ( uint8_t ) lr_fhss_params->lr_fhss_params.modulation_type, + ( uint8_t ) lr_fhss_params->lr_fhss_params.grid, + ( uint8_t )( lr_fhss_params->lr_fhss_params.enable_hopping ? LR1121_MODEM_LR_FHSS_HOPPING_ENABLE + : LR1121_MODEM_LR_FHSS_HOPPING_DISABLE ), + ( uint8_t ) lr_fhss_params->lr_fhss_params.bw, + ( uint8_t )( hop_sequence_id >> 8 ), + ( uint8_t )( hop_sequence_id >> 0 ), + ( uint8_t ) lr_fhss_params->device_offset, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_LR_FHSS_BUILD_FRAME_LENGTH, payload, payload_length ); +} + +uint32_t lr1121_modem_lr_fhss_get_time_on_air_in_ms( const lr1121_modem_lr_fhss_params_t* params, + uint16_t payload_length ) +{ + // Multiply by 1000 / 488.28125, or equivalently 256/125, rounding up + return ( ( lr1121_modem_lr_fhss_get_nb_bits( ¶ms->lr_fhss_params, payload_length ) << 8 ) + 124 ) / 125; +} + +unsigned int lr1121_modem_lr_fhss_get_hop_sequence_count( const lr1121_modem_lr_fhss_params_t* lr_fhss_params ) +{ + if( ( lr_fhss_params->lr_fhss_params.grid == LR_FHSS_V1_GRID_25391_HZ ) || + ( ( lr_fhss_params->lr_fhss_params.grid == LR_FHSS_V1_GRID_3906_HZ ) && + ( lr_fhss_params->lr_fhss_params.bw < LR_FHSS_V1_BW_335938_HZ ) ) ) + { + return 384; + } + return 512; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION --------------------------------------------- + */ + +uint16_t lr1121_modem_lr_fhss_get_nb_bits( const lr_fhss_v1_params_t* params, uint16_t payload_length ) +{ + uint16_t length_bits = ( payload_length + 2 ) * 8 + 6; + switch( params->cr ) + { + case LR_FHSS_V1_CR_5_6: + length_bits = ( ( length_bits * 6 ) + 4 ) / 5; + break; + + case LR_FHSS_V1_CR_2_3: + length_bits = length_bits * 3 / 2; + break; + + case LR_FHSS_V1_CR_1_2: + length_bits = length_bits * 2; + break; + + case LR_FHSS_V1_CR_1_3: + length_bits = length_bits * 3; + break; + } + + uint16_t payload_bits = ( length_bits / LR1121_MODEM_LR_FHSS_FRAG_BITS ) * LR1121_MODEM_LR_FHSS_BLOCK_BITS; + uint16_t last_block_bits = length_bits % LR1121_MODEM_LR_FHSS_FRAG_BITS; + if( last_block_bits > 0 ) + { + payload_bits += last_block_bits + 2; + } + + return LR1121_MODEM_LR_FHSS_HEADER_BITS * params->header_count + payload_bits; +} diff --git a/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_modem.c b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_modem.c new file mode 100755 index 0000000..13abaaf --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_modem.c @@ -0,0 +1,672 @@ +/*! + * @file lr1121_modem_modem.c + * + * @brief modem driver implementation for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ +#include "lr1121_modem_modem.h" +#include "lr1121_modem_common.h" +#include "lr1121_modem_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR1121_MODEM_FACTORY_RESET_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_VERSION_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_STATUS_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_CHARGE_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_RESET_CHARGE_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_EVENT_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_SUSPEND_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_GET_SUSPEND_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SET_ALARM_TIMER_CMD_LENGTH ( 3 + 4 ) +#define LR1121_MODEM_CLEAR_ALARM_TIMER_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_ALARM_REMAINING_TIME_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_GET_CRASHLOG_CMD_LENGTH ( 3 ) + +#define LR1121_MODEM_TEST_MODE_TST_START_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_TEST_MODE_TST_NOP_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_TEST_MODE_TST_TX_LORA_CMD_LENGTH ( 3 + 25 ) +#define LR1121_MODEM_TEST_MODE_TST_TX_FSK_CMD_LENGTH ( 3 + 15 ) +#define LR1121_MODEM_TEST_MODE_TST_TX_LR_FHSS_CMD_LENGTH ( 3 + 19 ) +#define LR1121_MODEM_TEST_MODE_TST_TX_CONT_CMD_LENGTH ( 3 + 10 ) +#define LR1121_MODEM_TEST_MODE_TST_CW_CMD_LENGTH ( 3 + 6 ) +#define LR1121_MODEM_TEST_MODE_TST_RX_LORA_CONT_CMD_LENGTH ( 3 + 8 ) +#define LR1121_MODEM_TEST_MODE_TST_RX_FSK_CONT_CMD_LENGTH ( 3 + 5 ) +#define LR1121_MODEM_TEST_MODE_TST_READ_PKT_COUNTER_RX_CONT_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_TEST_MODE_TST_RSSI_SUBGHZ_CMD_LENGTH ( 3 + 11 ) +#define LR1121_MODEM_TEST_MODE_TST_RADIO_RST_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_TEST_MODE_TST_EXIT_CMD_LENGTH ( 3 + 1 ) +#define LR1121_MODEM_TEST_MODE_TST_TX_SINGLE_PREAM_CMD_LENGTH ( 3 + 12 ) +#define LR1121_MODEM_TEST_MODE_READ_RSSI_CMD_LENGTH ( 3 + 1 ) + +#define LR1121_MODEM_GET_CHARGE_RBUFFER_LENGTH ( 320 ) +#define LR1121_MODEM_GET_CRASHLOG_RBUFFER_LENGTH ( 243 ) +#define LR1121_MODEM_GET_VERSION_RBUFFER_LENGTH ( 9 ) +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operation code command + */ +enum +{ + LR1121_MODEM_FACTORY_RESET_CMD = 0x00, + LR1121_MODEM_GET_VERSION_CMD = 0x01, + LR1121_MODEM_GET_STATUS_CMD = 0x02, + LR1121_MODEM_GET_CHARGE_CMD = 0x03, + LR1121_MODEM_GET_EVENT_CMD = 0x04, + LR1121_MODEM_TEST_CMD = 0x05, + LR1121_MODEM_GET_SUSPEND_MODEM_COM_CMD = 0x06, + LR1121_MODEM_SET_SUSPEND_MODEM_COM_CMD = 0x07, + LR1121_MODEM_SET_ALARM_TIMER_CMD = 0x08, + LR1121_MODEM_CLEAR_ALARM_TIMER_CMD = 0x09, + LR1121_MODEM_GET_ALARM_REMAINING_TIME_CMD = 0x0A, + LR1121_MODEM_GET_CRASHLOG_CMD = 0x0B, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/*! + * @brief Radio test mode type + */ +typedef enum +{ + LR1121_MODEM_TEST_MODE_TST_START = 0x00, + LR1121_MODEM_TEST_MODE_TST_EXIT = 0x01, + LR1121_MODEM_TEST_MODE_TST_NOP = 0x02, + LR1121_MODEM_TEST_MODE_TST_TX_LORA = 0x03, + LR1121_MODEM_TEST_MODE_TST_TX_FSK = 0x04, + LR1121_MODEM_TEST_MODE_TST_TX_LR_FHSS = 0x05, + LR1121_MODEM_TEST_MODE_TST_TX_CW = 0x06, + LR1121_MODEM_TEST_MODE_TST_RX_LORA_CONT = 0x07, + LR1121_MODEM_TEST_MODE_TST_RX_FSK_CONT = 0x08, + LR1121_MODEM_TEST_MODE_TST_READ_RX_PKT_COUNTER_RX_CONT = 0x09, + LR1121_MODEM_TEST_MODE_TST_RSSI_SUBGHZ = 0x0A, + LR1121_MODEM_TEST_MODE_READ_RSSI = 0x0B, + LR1121_MODEM_TEST_MODE_TST_RADIO_RST = 0x0E, + LR1121_MODEM_TEST_MODE_TST_READ_REGISTER = 0x0F, + LR1121_MODEM_TEST_MODE_TST_WRITE_REGISTER = 0x10, +} lr1121_modem_test_mode_t; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Helper function that convert an array of uint8_t into a uint32_t single value + * + * @warning It is up to the caller to ensure that value points to an array of at least sizeof(uint32_t) elements. + * + * @param [in] value Array of uint8_t to be translated into a uint32_t + * + * @returns 32-bit value + */ +static uint32_t lr1121_uint8_to_uint32( const uint8_t value[4] ); + +static void lr1121_parse_charge_hook_id( const uint8_t* buffer, uint8_t hook_id, lr1121_modem_consumption_details_t* hook ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr1121_modem_response_code_t lr1121_modem_factory_reset( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_FACTORY_RESET_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_FACTORY_RESET_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write_without_rc( + context, cbuffer, LR1121_MODEM_FACTORY_RESET_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_modem_version( const void* context, + lr1121_modem_version_t* modem_version ) +{ + uint8_t rbuffer[LR1121_MODEM_GET_VERSION_RBUFFER_LENGTH] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_VERSION_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_GET_VERSION_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_VERSION_CMD_LENGTH, rbuffer, LR1121_MODEM_GET_VERSION_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + modem_version->use_case = rbuffer[0]; + modem_version->modem_major = rbuffer[1]; + modem_version->modem_minor = rbuffer[2]; + modem_version->modem_patch = rbuffer[3]; + modem_version->lbm_major = rbuffer[5]; + modem_version->lbm_minor = rbuffer[6]; + modem_version->lbm_patch = rbuffer[7]; + } + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_get_status( const void* context, + lr1121_modem_lorawan_status_bitmask_t* status ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_STATUS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_GET_STATUS_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( context, cbuffer, LR1121_MODEM_GET_STATUS_CMD_LENGTH, + ( uint8_t* ) status, sizeof( uint8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_get_charge( const void* context, lr1121_modem_charge_t* charge ) +{ + uint8_t rbuffer[LR1121_MODEM_GET_CHARGE_RBUFFER_LENGTH] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_CHARGE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_GET_CHARGE_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_CHARGE_CMD_LENGTH, rbuffer, LR1121_MODEM_GET_CHARGE_RBUFFER_LENGTH ); + + lr1121_parse_charge_hook_id( rbuffer, 0, &charge->suspend ); + lr1121_parse_charge_hook_id( rbuffer, 1, &charge->class_b_beacon ); + lr1121_parse_charge_hook_id( rbuffer, 2, &charge->lr1mac_stack ); + lr1121_parse_charge_hook_id( rbuffer, 3, &charge->lbt ); + lr1121_parse_charge_hook_id( rbuffer, 4, &charge->cad ); + lr1121_parse_charge_hook_id( rbuffer, 5, &charge->class_b_ping_slot ); + lr1121_parse_charge_hook_id( rbuffer, 6, &charge->test_mode ); + lr1121_parse_charge_hook_id( rbuffer, 7, &charge->direct_rp_access ); + lr1121_parse_charge_hook_id( rbuffer, 8, &charge->relay_tx ); + lr1121_parse_charge_hook_id( rbuffer, 9, &charge->class_c ); + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_get_event( const void* context, lr1121_modem_event_fields_t* event_fields ) +{ + uint8_t rbuffer[sizeof( uint32_t )] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_EVENT_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_GET_EVENT_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_EVENT_CMD_LENGTH, rbuffer, sizeof( uint32_t ) ); + + event_fields->event_type = ( lr1121_modem_lorawan_event_type_t ) rbuffer[0]; + event_fields->missed_events_count = rbuffer[1]; + event_fields->data = ( ( uint16_t ) rbuffer[2] << 8 ) + ( ( uint16_t ) rbuffer[3] ); + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_set_suspend( const void* context, const lr1121_modem_suspend_t suspend ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_SUSPEND_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_SET_SUSPEND_MODEM_COM_CMD, + suspend, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SET_SUSPEND_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_suspend( const void* context, lr1121_modem_suspend_t* suspend ) +{ + const uint8_t cbuffer[LR1121_MODEM_GET_SUSPEND_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_GET_SUSPEND_MODEM_COM_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_SUSPEND_CMD_LENGTH, ( uint8_t* ) suspend, sizeof( uint8_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_set_alarm_timer( const void* context, uint32_t seconds ) +{ + const uint8_t cbuffer[LR1121_MODEM_SET_ALARM_TIMER_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_SET_ALARM_TIMER_CMD, + ( uint8_t )( seconds >> 24 ), + ( uint8_t )( seconds >> 16 ), + ( uint8_t )( seconds >> 8 ), + ( uint8_t ) seconds, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SET_ALARM_TIMER_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_clear_alarm_timer( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_CLEAR_ALARM_TIMER_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_CLEAR_ALARM_TIMER_CMD, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_CLEAR_ALARM_TIMER_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_get_alarm_remaining_time( const void* context, uint32_t* remaining_time ) +{ + uint8_t rbuffer[sizeof( uint32_t )] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_ALARM_REMAINING_TIME_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_GET_ALARM_REMAINING_TIME_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_ALARM_REMAINING_TIME_CMD_LENGTH, rbuffer, sizeof( uint32_t ) ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *remaining_time = ( ( uint32_t ) rbuffer[0] << 24 ) + ( ( uint32_t ) rbuffer[1] << 16 ) + + ( ( uint32_t ) rbuffer[2] << 8 ) + ( ( uint32_t ) rbuffer[3] ); + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_get_crashlog( const void* context, lr1121_modem_crashlog_status_t* status, + uint8_t* crashlog ) +{ + uint8_t rbuffer[LR1121_MODEM_GET_CRASHLOG_RBUFFER_LENGTH] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_GET_CRASHLOG_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_GET_CRASHLOG_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_GET_CRASHLOG_CMD_LENGTH, rbuffer, LR1121_MODEM_GET_CRASHLOG_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *status = ( lr1121_modem_crashlog_status_t ) rbuffer[0]; + + for( uint8_t i = 0; i < LR1121_MODEM_GET_CRASHLOG_RBUFFER_LENGTH - 1; i++ ) + { + crashlog[i] = rbuffer[1 + i]; + } + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_test_mode_start( const void* context ) +{ + const uint8_t test_msg[8] = { 'T', 'E', 'S', 'T', 'T', 'E', 'S', 'T' }; + + const uint8_t cbuffer[LR1121_MODEM_TEST_MODE_TST_START_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_TEST_CMD, + LR1121_MODEM_TEST_MODE_TST_START, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_TEST_MODE_TST_START_CMD_LENGTH, test_msg, 8 ); +} + +lr1121_modem_response_code_t lr1121_modem_test_exit( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_TEST_MODE_TST_EXIT_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_TEST_CMD, + LR1121_MODEM_TEST_MODE_TST_EXIT, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_TEST_MODE_TST_EXIT_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_test_nop( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_TEST_MODE_TST_NOP_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_TEST_CMD, + LR1121_MODEM_TEST_MODE_TST_NOP, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_TEST_MODE_TST_NOP_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_test_tx_lora( + const void* context, uint32_t frequency, int8_t tx_power, uint8_t payload_length, + lr1121_modem_tst_mode_lora_sf_t sf, lr1121_modem_tst_mode_lora_bw_t bw, lr1121_modem_tst_mode_lora_cr_t cr, + bool is_iq_inverted, bool is_crc_enabled, lr1121_modem_tst_mode_lora_packet_header_mode_t header_mode, + uint32_t preamble_length, uint32_t number_of_tx, uint32_t delay_ms ) +{ + const uint8_t cbuffer[LR1121_MODEM_TEST_MODE_TST_TX_LORA_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_TEST_CMD, + LR1121_MODEM_TEST_MODE_TST_TX_LORA, + ( uint8_t )( ( frequency & 0xFF000000 ) >> 24 ), + ( uint8_t )( ( frequency & 0x00FF0000 ) >> 16 ), + ( uint8_t )( ( frequency & 0x0000FF00 ) >> 8 ), + ( uint8_t )( frequency & 0x000000FF ), + ( uint8_t ) tx_power, + payload_length, + ( uint8_t ) sf, + ( uint8_t ) bw, + ( uint8_t ) cr, + ( uint8_t )( is_iq_inverted ? 0x01 : 0x00 ), + ( uint8_t )( is_crc_enabled ? 0x01 : 0x00 ), + ( uint8_t ) header_mode, + ( uint8_t )( preamble_length >> 24 ), + ( uint8_t )( preamble_length >> 16 ), + ( uint8_t )( preamble_length >> 8 ), + ( uint8_t ) preamble_length, + ( uint8_t )( number_of_tx >> 24 ), + ( uint8_t )( number_of_tx >> 16 ), + ( uint8_t )( number_of_tx >> 8 ), + ( uint8_t ) number_of_tx, + ( uint8_t )( delay_ms >> 24 ), + ( uint8_t )( delay_ms >> 16 ), + ( uint8_t )( delay_ms >> 8 ), + ( uint8_t ) delay_ms, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_TEST_MODE_TST_TX_LORA_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_test_tx_fsk( const void* context, uint32_t frequency, int8_t tx_power, + uint8_t payload_length, uint32_t number_of_tx, + uint32_t delay_ms ) +{ + const uint8_t cbuffer[LR1121_MODEM_TEST_MODE_TST_TX_FSK_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_TEST_CMD, + LR1121_MODEM_TEST_MODE_TST_TX_FSK, + ( uint8_t )( ( frequency & 0xFF000000 ) >> 24 ), + ( uint8_t )( ( frequency & 0x00FF0000 ) >> 16 ), + ( uint8_t )( ( frequency & 0x0000FF00 ) >> 8 ), + ( uint8_t )( frequency & 0x000000FF ), + ( uint8_t ) tx_power, + payload_length, + ( uint8_t )( number_of_tx >> 24 ), + ( uint8_t )( number_of_tx >> 16 ), + ( uint8_t )( number_of_tx >> 8 ), + ( uint8_t ) number_of_tx, + ( uint8_t )( delay_ms >> 24 ), + ( uint8_t )( delay_ms >> 16 ), + ( uint8_t )( delay_ms >> 8 ), + ( uint8_t ) delay_ms, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_TEST_MODE_TST_TX_FSK_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_test_tx_lr_fhss( const void* context, uint32_t frequency, int8_t tx_power, + uint8_t payload_length, + lr1121_modem_tst_mode_lr_fhss_grid_t grid, + lr1121_modem_tst_mode_lr_fhss_bw_t bw, + lr1121_modem_tst_mode_lr_fhss_cr_t cr, uint32_t number_of_tx, + uint32_t delay_ms, bool is_hopping_enabled ) +{ + const uint8_t cbuffer[LR1121_MODEM_TEST_MODE_TST_TX_LR_FHSS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_TEST_CMD, + LR1121_MODEM_TEST_MODE_TST_TX_LR_FHSS, + ( uint8_t )( ( frequency & 0xFF000000 ) >> 24 ), + ( uint8_t )( ( frequency & 0x00FF0000 ) >> 16 ), + ( uint8_t )( ( frequency & 0x0000FF00 ) >> 8 ), + ( uint8_t )( frequency & 0x000000FF ), + ( uint8_t ) tx_power, + payload_length, + ( uint8_t ) grid, + ( uint8_t ) bw, + ( uint8_t ) cr, + ( uint8_t )( number_of_tx >> 24 ), + ( uint8_t )( number_of_tx >> 16 ), + ( uint8_t )( number_of_tx >> 8 ), + ( uint8_t ) number_of_tx, + ( uint8_t )( delay_ms >> 24 ), + ( uint8_t )( delay_ms >> 16 ), + ( uint8_t )( delay_ms >> 8 ), + ( uint8_t ) delay_ms, + is_hopping_enabled ? 0x01 : 0x00, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_TEST_MODE_TST_TX_LR_FHSS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_test_tx_cw( const void* context, uint32_t frequency, int8_t tx_power ) +{ + const uint8_t cbuffer[LR1121_MODEM_TEST_MODE_TST_CW_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_TEST_CMD, + LR1121_MODEM_TEST_MODE_TST_TX_CW, + ( uint8_t )( ( frequency & 0xFF000000 ) >> 24 ), + ( uint8_t )( ( frequency & 0x00FF0000 ) >> 16 ), + ( uint8_t )( ( frequency & 0x0000FF00 ) >> 8 ), + ( uint8_t )( frequency & 0x000000FF ), + ( uint8_t ) tx_power, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_TEST_MODE_TST_CW_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_test_rx_lora_cont( const void* context, uint32_t frequency, + lr1121_modem_tst_mode_lora_sf_t sf, + lr1121_modem_tst_mode_lora_bw_t bw, + lr1121_modem_tst_mode_lora_cr_t cr ) +{ + const uint8_t cbuffer[LR1121_MODEM_TEST_MODE_TST_RX_LORA_CONT_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_TEST_CMD, + LR1121_MODEM_TEST_MODE_TST_RX_LORA_CONT, + ( uint8_t )( ( frequency & 0xFF000000 ) >> 24 ), + ( uint8_t )( ( frequency & 0x00FF0000 ) >> 16 ), + ( uint8_t )( ( frequency & 0x0000FF00 ) >> 8 ), + ( uint8_t )( frequency & 0x000000FF ), + ( uint8_t ) sf, + ( uint8_t ) bw, + ( uint8_t ) cr, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_TEST_MODE_TST_RX_LORA_CONT_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_test_rx_fsk_cont( const void* context, uint32_t frequency ) +{ + const uint8_t cbuffer[LR1121_MODEM_TEST_MODE_TST_RX_FSK_CONT_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_TEST_CMD, + LR1121_MODEM_TEST_MODE_TST_RX_FSK_CONT, + ( uint8_t )( ( frequency & 0xFF000000 ) >> 24 ), + ( uint8_t )( ( frequency & 0x00FF0000 ) >> 16 ), + ( uint8_t )( ( frequency & 0x0000FF00 ) >> 8 ), + ( uint8_t )( frequency & 0x000000FF ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_TEST_MODE_TST_RX_FSK_CONT_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_test_read_packet_counter_rx_cont( const void* context, + uint32_t* rx_packet_counter ) +{ + const uint8_t cbuffer[LR1121_MODEM_TEST_MODE_TST_READ_PKT_COUNTER_RX_CONT_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_TEST_CMD, + LR1121_MODEM_TEST_MODE_TST_READ_RX_PKT_COUNTER_RX_CONT, + }; + + uint8_t rbuffer[sizeof( uint32_t )] = { 0 }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_TEST_MODE_TST_READ_PKT_COUNTER_RX_CONT_CMD_LENGTH, rbuffer, sizeof( uint32_t ) ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *rx_packet_counter = ( ( uint32_t ) rbuffer[0] << 24 ) + ( ( uint32_t ) rbuffer[1] << 16 ) + + ( ( uint32_t ) rbuffer[2] << 8 ) + ( ( uint32_t ) rbuffer[3] ); + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_test_rssi_subghz( const void* context, uint32_t frequency, uint16_t time_ms, + uint32_t bw_hz ) +{ + const uint8_t cbuffer[LR1121_MODEM_TEST_MODE_TST_RSSI_SUBGHZ_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_TEST_CMD, + LR1121_MODEM_TEST_MODE_TST_RSSI_SUBGHZ, + ( uint8_t )( ( frequency & 0xFF000000 ) >> 24 ), + ( uint8_t )( ( frequency & 0x00FF0000 ) >> 16 ), + ( uint8_t )( ( frequency & 0x0000FF00 ) >> 8 ), + ( uint8_t )( frequency & 0x000000FF ), + ( uint8_t )( time_ms >> 8 ), + ( uint8_t )( time_ms ), + ( uint8_t )( ( bw_hz & 0xFF000000 ) >> 24 ), + ( uint8_t )( ( bw_hz & 0x00FF0000 ) >> 16 ), + ( uint8_t )( ( bw_hz & 0x0000FF00 ) >> 8 ), + ( uint8_t )( bw_hz & 0x000000FF ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_TEST_MODE_TST_RSSI_SUBGHZ_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_test_read_rssi( const void* context, int16_t* rssi ) +{ + uint8_t rbuffer[sizeof( uint8_t )] = { 0x00 }; + + const uint8_t cbuffer[LR1121_MODEM_TEST_MODE_READ_RSSI_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_TEST_CMD, + LR1121_MODEM_TEST_MODE_READ_RSSI, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_TEST_MODE_READ_RSSI_CMD_LENGTH, rbuffer, sizeof( uint8_t ) ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *rssi = ( int16_t )( ( int8_t ) rbuffer[0] - 64 ); + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_test_radio_rst( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_TEST_MODE_TST_RADIO_RST_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_MODEM >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_MODEM, + LR1121_MODEM_TEST_CMD, + LR1121_MODEM_TEST_MODE_TST_RADIO_RST, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_TEST_MODE_TST_RADIO_RST_CMD_LENGTH, 0, 0 ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +uint32_t lr1121_uint8_to_uint32( const uint8_t value[4] ) +{ + return ( ( ( uint32_t ) value[0] ) << 24 ) + ( ( ( uint32_t ) value[1] ) << 16 ) + + ( ( ( uint32_t ) value[2] ) << 8 ) + ( ( ( uint32_t ) value[3] ) ); +} + +void lr1121_parse_charge_hook_id( const uint8_t* buffer, uint8_t hook_id, lr1121_modem_consumption_details_t* hook ) +{ + hook->tx_last_toa_ms = lr1121_uint8_to_uint32( buffer + 32 * hook_id + 0 ); + hook->rx_last_toa_ms = lr1121_uint8_to_uint32( buffer + 32 * hook_id + 4 ); + hook->tx_cumulated_toa_ms = lr1121_uint8_to_uint32( buffer + 32 * hook_id + 8 ); + hook->rx_cumulated_toa_ms = lr1121_uint8_to_uint32( buffer + 32 * hook_id + 12 ); + hook->none_consumption_ms = lr1121_uint8_to_uint32( buffer + 32 * hook_id + 16 ); + hook->tx_consumption_ma = lr1121_uint8_to_uint32( buffer + 32 * hook_id + 20 ); + hook->rx_consumption_ma = lr1121_uint8_to_uint32( buffer + 32 * hook_id + 24 ); + hook->none_consumption_ma = lr1121_uint8_to_uint32( buffer + 32 * hook_id + 28 ); +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_radio.c b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_radio.c new file mode 100755 index 0000000..063d20a --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_radio.c @@ -0,0 +1,1303 @@ +/*! + * @file lr1121_modem_radio.c + * + * @brief Radio driver implementation for LR1121 + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr1121_modem_radio.h" +#include "lr1121_modem_regmem.h" +#include "lr1121_modem_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR1121_MODEM_RADIO_RESET_STATS_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_RADIO_GET_STATS_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_RADIO_GET_PKT_TYPE_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_RADIO_GET_RXBUFFER_STATUS_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_RADIO_GET_PKT_STATUS_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_RADIO_GET_RSSI_INST_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_RADIO_SET_GFSK_SYNC_WORD_CMD_LENGTH ( 2 + LR1121_MODEM_RADIO_GFSK_SYNC_WORD_LENGTH ) +#define LR1121_MODEM_RADIO_SET_LORA_PUBLIC_NETWORK_CMD_LENGTH ( 2 + 8 ) +#define LR1121_MODEM_RADIO_SET_LR_FHSS_SYNC_WORD_LENGTH ( 2 + 0 ) +#define LR1121_MODEM_RADIO_SET_RX_CMD_LENGTH ( 2 + 3 ) +#define LR1121_MODEM_RADIO_SET_TX_CMD_LENGTH ( 2 + 3 ) +#define LR1121_MODEM_RADIO_SET_RF_FREQUENCY_CMD_LENGTH ( 2 + 4 ) +#define LR1121_MODEM_RADIO_SET_AUTO_TX_RX_CMD_LENGTH ( 2 + 7 ) +#define LR1121_MODEM_RADIO_SET_CAD_PARAMS_CMD_LENGTH ( 2 + 7 ) +#define LR1121_MODEM_RADIO_SET_PKT_TYPE_CMD_LENGTH ( 2 + 1 ) +#define LR1121_MODEM_RADIO_SET_MODULATION_PARAMS_GFSK_CMD_LENGTH ( 2 + 10 ) +#define LR1121_MODEM_RADIO_SET_MODULATION_PARAMS_BPSK_CMD_LENGTH ( 2 + 5 ) +#define LR1121_MODEM_RADIO_SET_MODULATION_PARAMS_LORA_CMD_LENGTH ( 2 + 4 ) +#define LR1121_MODEM_RADIO_SET_MODULATION_PARAMS_LR_FHSS_CMD_LENGTH ( 2 + 5 ) +#define LR1121_MODEM_RADIO_SET_PKT_PARAM_GFSK_CMD_LENGTH ( 2 + 9 ) +#define LR1121_MODEM_RADIO_SET_PKT_PARAM_BPSK_CMD_LENGTH ( 2 + 7 ) +#define LR1121_MODEM_RADIO_SET_PKT_PARAM_LORA_CMD_LENGTH ( 2 + 6 ) +#define LR1121_MODEM_RADIO_SET_TX_PARAMS_CMD_LENGTH ( 2 + 2 ) +#define LR1121_MODEM_RADIO_SET_PKT_ADDRESS_CMD_LENGTH ( 2 + 2 ) +#define LR1121_MODEM_RADIO_SET_RX_TX_FALLBACK_MODE_CMD_LENGTH ( 2 + 1 ) +#define LR1121_MODEM_RADIO_SET_RX_DUTY_CYCLE_MODE_CMD_LENGTH ( 2 + 7 ) +#define LR1121_MODEM_RADIO_SET_PA_CFG_CMD_LENGTH ( 2 + 4 ) +#define LR1121_MODEM_RADIO_STOP_TIMEOUT_ON_PREAMBLE_CMD_LENGTH ( 2 + 1 ) +#define LR1121_MODEM_RADIO_SET_CAD_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_RADIO_SET_TX_CW_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_RADIO_SET_TX_INFINITE_PREAMBLE_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_RADIO_SET_LORA_SYNC_TIMEOUT_CMD_LENGTH ( 2 + 1 ) +#define LR1121_MODEM_RADIO_SET_LORA_SYNC_TIMEOUT_EXT_CMD_LENGTH ( 2 + 2 ) +#define LR1121_MODEM_RADIO_SET_GFSK_CRC_PARAMS_CMD_LENGTH ( 2 + 8 ) +#define LR1121_MODEM_RADIO_SET_GFSK_WHITENING_CMD_LENGTH ( 2 + 2 ) +#define LR1121_MODEM_RADIO_SET_RX_BOOSTED_LENGTH ( 2 + 1 ) +#define LR1121_MODEM_RADIO_SET_RSSI_CALIBRATION_LENGTH ( 2 + 11 ) +#define LR1121_MODEM_RADIO_SET_LORA_SYNC_WORD_CMD_LENGTH ( 2 + 1 ) +#define LR1121_MODEM_RADIO_GET_LORA_RX_INFO_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_RADIO_CFG_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_CMD_LENGTH ( 2 + 1 ) +#define LR1121_MODEM_RADIO_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_SEND_CMD_LENGTH ( 2 + 1 ) + +/** + * @brief Internal RTC frequency + */ +#define LR1121_MODEM_RTC_FREQ_IN_HZ 32768UL + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for radio-related operations + */ +enum +{ + LR1121_MODEM_RADIO_RESET_STATS_OC = 0x0200, + LR1121_MODEM_RADIO_GET_STATS_OC = 0x0201, + LR1121_MODEM_RADIO_GET_PKT_TYPE_OC = 0x0202, + LR1121_MODEM_RADIO_GET_RXBUFFER_STATUS_OC = 0x0203, + LR1121_MODEM_RADIO_GET_PKT_STATUS_OC = 0x0204, + LR1121_MODEM_RADIO_GET_RSSI_INST_OC = 0x0205, + LR1121_MODEM_RADIO_SET_GFSK_SYNC_WORD_OC = 0x0206, + LR1121_MODEM_RADIO_SET_LORA_PUBLIC_NETWORK_OC = 0x0208, + LR1121_MODEM_RADIO_SET_RX_OC = 0x0209, + LR1121_MODEM_RADIO_SET_TX_OC = 0x020A, + LR1121_MODEM_RADIO_SET_RF_FREQUENCY_OC = 0x020B, + LR1121_MODEM_RADIO_AUTOTXRX_OC = 0x020C, + LR1121_MODEM_RADIO_SET_CAD_PARAMS_OC = 0x020D, + LR1121_MODEM_RADIO_SET_PKT_TYPE_OC = 0x020E, + LR1121_MODEM_RADIO_SET_MODULATION_PARAM_OC = 0x020F, + LR1121_MODEM_RADIO_SET_PKT_PARAM_OC = 0x0210, + LR1121_MODEM_RADIO_SET_TX_PARAMS_OC = 0x0211, + LR1121_MODEM_RADIO_SET_PKT_ADRS_OC = 0x0212, + LR1121_MODEM_RADIO_SET_RX_TX_FALLBACK_MODE_OC = 0x0213, + LR1121_MODEM_RADIO_SET_RX_DUTY_CYCLE_OC = 0x0214, + LR1121_MODEM_RADIO_SET_PA_CFG_OC = 0x0215, + LR1121_MODEM_RADIO_STOP_TIMEOUT_ON_PREAMBLE_OC = 0x0217, + LR1121_MODEM_RADIO_SET_CAD_OC = 0x0218, + LR1121_MODEM_RADIO_SET_TX_CW_OC = 0x0219, + LR1121_MODEM_RADIO_SET_TX_INFINITE_PREAMBLE_OC = 0x021A, + LR1121_MODEM_RADIO_SET_LORA_SYNC_TIMEOUT_OC = 0x021B, + LR1121_MODEM_RADIO_SET_GFSK_CRC_PARAMS_OC = 0x0224, + LR1121_MODEM_RADIO_SET_GFSK_WHITENING_PARAMS_OC = 0x0225, + LR1121_MODEM_RADIO_SET_RX_BOOSTED_OC = 0x0227, + LR1121_MODEM_RADIO_SET_RSSI_CALIBRATION_OC = 0x0229, + LR1121_MODEM_RADIO_SET_LORA_SYNC_WORD_OC = 0x022B, + LR1121_MODEM_RADIO_SET_LR_FHSS_SYNC_WORD_OC = 0x022D, + LR1121_MODEM_RADIO_CFG_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_OC = 0x022E, + LR1121_MODEM_RADIO_GET_LORA_RX_INFO_OC = 0x0230, + LR1121_MODEM_RADIO_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_SEND_OC = 0x0231, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Get the CRC length in byte from the corresponding GFSK radio parameter + * + * @param [in] crc_type GFSK CRC parameter + * + * @returns CRC length in byte + */ +static inline uint32_t lr1121_modem_radio_get_gfsk_crc_len_in_bytes( lr1121_modem_radio_gfsk_crc_type_t crc_type ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr1121_modem_response_code_t lr1121_modem_radio_reset_stats( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_RESET_STATS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_RESET_STATS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_RESET_STATS_OC >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_RADIO_RESET_STATS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_get_gfsk_stats( const void* context, + lr1121_modem_radio_stats_gfsk_t* stats ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_GET_STATS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_GET_STATS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_GET_STATS_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( lr1121_modem_radio_stats_gfsk_t )] = { 0x00 }; + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_RADIO_GET_STATS_CMD_LENGTH, rbuffer, sizeof( lr1121_modem_radio_stats_gfsk_t ) ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + stats->nb_pkt_received = ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1]; + stats->nb_pkt_crc_error = ( ( uint16_t ) rbuffer[2] << 8 ) + ( uint16_t ) rbuffer[3]; + stats->nb_pkt_len_error = ( ( uint16_t ) rbuffer[4] << 8 ) + ( uint16_t ) rbuffer[5]; + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_radio_get_lora_stats( const void* context, + lr1121_modem_radio_stats_lora_t* stats ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_GET_STATS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_GET_STATS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_GET_STATS_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( lr1121_modem_radio_stats_lora_t )] = { 0x00 }; + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_RADIO_GET_STATS_CMD_LENGTH, rbuffer, sizeof( lr1121_modem_radio_stats_lora_t ) ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + stats->nb_pkt_received = ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1]; + stats->nb_pkt_crc_error = ( ( uint16_t ) rbuffer[2] << 8 ) + ( uint16_t ) rbuffer[3]; + stats->nb_pkt_header_error = ( ( uint16_t ) rbuffer[4] << 8 ) + ( uint16_t ) rbuffer[5]; + stats->nb_pkt_falsesync = ( ( uint16_t ) rbuffer[6] << 8 ) + ( uint16_t ) rbuffer[7]; + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_radio_get_pkt_type( const void* context, + lr1121_modem_radio_pkt_type_t* pkt_type ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_GET_PKT_TYPE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_GET_PKT_TYPE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_GET_PKT_TYPE_OC >> 0 ), + }; + uint8_t pkt_type_raw = 0; + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_RADIO_GET_PKT_TYPE_CMD_LENGTH, &pkt_type_raw, 1 ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *pkt_type = ( lr1121_modem_radio_pkt_type_t ) pkt_type_raw; + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_radio_get_rx_buffer_status( + const void* context, lr1121_modem_radio_rx_buffer_status_t* rx_buffer_status ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_GET_RXBUFFER_STATUS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_GET_RXBUFFER_STATUS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_GET_RXBUFFER_STATUS_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( *rx_buffer_status )] = { 0x00 }; + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_RADIO_GET_RXBUFFER_STATUS_CMD_LENGTH, rbuffer, sizeof( *rx_buffer_status ) ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + rx_buffer_status->pld_len_in_bytes = rbuffer[0]; + rx_buffer_status->buffer_start_pointer = rbuffer[1]; + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_radio_get_gfsk_pkt_status( const void* context, + lr1121_modem_radio_pkt_status_gfsk_t* pkt_status ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_GET_PKT_STATUS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_GET_PKT_STATUS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_GET_PKT_STATUS_OC >> 0 ), + }; + uint8_t rbuffer[4] = { 0x00 }; + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_RADIO_GET_PKT_STATUS_CMD_LENGTH, rbuffer, 4 ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + pkt_status->rssi_sync_in_dbm = -( int8_t )( rbuffer[0] >> 1 ); + pkt_status->rssi_avg_in_dbm = -( int8_t )( rbuffer[1] >> 1 ); + pkt_status->rx_len_in_bytes = rbuffer[2]; + pkt_status->is_addr_err = ( ( rbuffer[3] & 0x20 ) != 0 ) ? true : false; + pkt_status->is_crc_err = ( ( rbuffer[3] & 0x10 ) != 0 ) ? true : false; + pkt_status->is_len_err = ( ( rbuffer[3] & 0x08 ) != 0 ) ? true : false; + pkt_status->is_abort_err = ( ( rbuffer[3] & 0x04 ) != 0 ) ? true : false; + pkt_status->is_received = ( ( rbuffer[3] & 0x02 ) != 0 ) ? true : false; + pkt_status->is_sent = ( ( rbuffer[3] & 0x01 ) != 0 ) ? true : false; + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_radio_get_lora_pkt_status( const void* context, + lr1121_modem_radio_pkt_status_lora_t* pkt_status ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_GET_PKT_STATUS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_GET_PKT_STATUS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_GET_PKT_STATUS_OC >> 0 ), + }; + uint8_t rbuffer[3] = { 0x00 }; + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_RADIO_GET_PKT_STATUS_CMD_LENGTH, rbuffer, 3 ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + pkt_status->rssi_pkt_in_dbm = -( int8_t )( rbuffer[0] >> 1 ); + pkt_status->snr_pkt_in_db = ( ( ( int8_t ) rbuffer[1] ) + 2 ) >> 2; + pkt_status->signal_rssi_pkt_in_dbm = -( int8_t )( rbuffer[2] >> 1 ); + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_radio_get_rssi_inst( const void* context, int8_t* rssi_in_dbm ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_GET_RSSI_INST_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_GET_RSSI_INST_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_GET_RSSI_INST_OC >> 0 ), + }; + uint8_t rssi = 0; + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_RADIO_GET_RSSI_INST_CMD_LENGTH, &rssi, sizeof( rssi ) ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *rssi_in_dbm = -( int8_t )( rssi >> 1 ); + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_gfsk_sync_word( + const void* context, const uint8_t gfsk_sync_word[LR1121_MODEM_RADIO_GFSK_SYNC_WORD_LENGTH] ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_GFSK_SYNC_WORD_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_GFSK_SYNC_WORD_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_GFSK_SYNC_WORD_OC >> 0 ), + gfsk_sync_word[0], + gfsk_sync_word[1], + gfsk_sync_word[2], + gfsk_sync_word[3], + gfsk_sync_word[4], + gfsk_sync_word[5], + gfsk_sync_word[6], + gfsk_sync_word[7], + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_GFSK_SYNC_WORD_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_lora_sync_word( const void* context, const uint8_t sync_word ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_LORA_SYNC_WORD_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_LORA_SYNC_WORD_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_LORA_SYNC_WORD_OC >> 0 ), + sync_word, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_LORA_SYNC_WORD_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_lora_public_network( + const void* context, const lr1121_modem_radio_lora_network_type_t network_type ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_LORA_PUBLIC_NETWORK_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_LORA_PUBLIC_NETWORK_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_LORA_PUBLIC_NETWORK_OC >> 0 ), + ( uint8_t ) network_type, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_LORA_SYNC_WORD_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_lr_fhss_sync_word( + const void* context, const uint8_t sync_word[LR1121_MODEM_RADIO_LR_FHSS_SYNC_WORD_LENGTH] ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_LR_FHSS_SYNC_WORD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_LR_FHSS_SYNC_WORD_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_LR_FHSS_SYNC_WORD_OC >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_LR_FHSS_SYNC_WORD_LENGTH, sync_word, + LR1121_MODEM_RADIO_LR_FHSS_SYNC_WORD_LENGTH ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_rx( const void* context, const uint32_t timeout_in_ms ) +{ + const uint32_t timeout_in_rtc_step = lr1121_modem_radio_convert_time_in_ms_to_rtc_step( timeout_in_ms ); + + return lr1121_modem_radio_set_rx_with_timeout_in_rtc_step( context, timeout_in_rtc_step ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_rx_and_lna_mode( const void* context, const uint32_t timeout_in_ms, + lr1121_modem_radio_lna_mode_t lna_mode ) +{ + const uint32_t timeout_in_rtc_step = lr1121_modem_radio_convert_time_in_ms_to_rtc_step( timeout_in_ms ); + + return lr1121_modem_radio_set_rx_with_timeout_in_rtc_step_and_lna_mode( context, timeout_in_rtc_step, lna_mode ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_rx_with_timeout_in_rtc_step( const void* context, + const uint32_t timeout_in_ms ) +{ + return lr1121_modem_radio_set_rx_with_timeout_in_rtc_step_and_lna_mode( + context, timeout_in_ms, LR1121_MODEM_RADIO_LNA_MODE_DIFFERENTIAL_LF0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_rx_with_timeout_in_rtc_step_and_lna_mode( + const void* context, const uint32_t timeout, lr1121_modem_radio_lna_mode_t lna_mode ) +{ + lr1121_modem_response_code_t status = LR1121_MODEM_RESPONSE_CODE_NOT_INITIALIZED; + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_RX_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_RX_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_RX_OC >> 0 ), + ( uint8_t )( timeout >> 16 ), + ( uint8_t )( timeout >> 8 ), + ( uint8_t )( timeout >> 0 ), + }; + + do + { + status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_RADIO_SET_RX_CMD_LENGTH, 0, 0 ); + if( status != LR1121_MODEM_RESPONSE_CODE_OK ) + { + break; + } + + if( ( lna_mode == LR1121_MODEM_RADIO_LNA_MODE_SINGLE_RFI_P_LF0 ) || + ( lna_mode == LR1121_MODEM_RADIO_LNA_MODE_SINGLE_RFI_N_LF0 ) ) + { + status = lr1121_modem_radio_set_lna_mode( context, lna_mode ); + if( status != LR1121_MODEM_RESPONSE_CODE_OK ) + { + break; + } + } + + } while( 0 ); + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_tx( const void* context, const uint32_t timeout_in_ms ) +{ + const uint32_t timeout_in_rtc_step = lr1121_modem_radio_convert_time_in_ms_to_rtc_step( timeout_in_ms ); + + return lr1121_modem_radio_set_tx_with_timeout_in_rtc_step( context, timeout_in_rtc_step ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_tx_with_timeout_in_rtc_step( const void* context, + const uint32_t timeout_in_rtc_step ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_TX_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_TX_OC >> 8 ), ( uint8_t )( LR1121_MODEM_RADIO_SET_TX_OC >> 0 ), + ( uint8_t )( timeout_in_rtc_step >> 16 ), ( uint8_t )( timeout_in_rtc_step >> 8 ), + ( uint8_t )( timeout_in_rtc_step >> 0 ), + }; + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_RADIO_SET_TX_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_rf_freq( const void* context, const uint32_t freq_in_hz ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_RF_FREQUENCY_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_RF_FREQUENCY_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_RF_FREQUENCY_OC >> 0 ), + ( uint8_t )( freq_in_hz >> 24 ), + ( uint8_t )( freq_in_hz >> 16 ), + ( uint8_t )( freq_in_hz >> 8 ), + ( uint8_t )( freq_in_hz >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_RF_FREQUENCY_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_auto_tx_rx( + const void* context, const uint32_t delay, const lr1121_modem_radio_intermediary_mode_t intermediary_mode, + const uint32_t timeout ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_AUTO_TX_RX_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_AUTOTXRX_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_AUTOTXRX_OC >> 0 ), + ( uint8_t )( delay >> 16 ), + ( uint8_t )( delay >> 8 ), + ( uint8_t )( delay ), + ( uint8_t ) intermediary_mode, + ( uint8_t )( timeout >> 16 ), + ( uint8_t )( timeout >> 8 ), + ( uint8_t )( timeout ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_AUTO_TX_RX_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_cad_params( const void* context, + const lr1121_modem_radio_cad_params_t* cad_params ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_CAD_PARAMS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_CAD_PARAMS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_CAD_PARAMS_OC >> 0 ), + cad_params->cad_symb_nb, + cad_params->cad_detect_peak, + cad_params->cad_detect_min, + ( uint8_t ) cad_params->cad_exit_mode, + ( uint8_t )( cad_params->cad_timeout >> 16 ), + ( uint8_t )( cad_params->cad_timeout >> 8 ), + ( uint8_t )( cad_params->cad_timeout ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_CAD_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_pkt_type( const void* context, + const lr1121_modem_radio_pkt_type_t pkt_type ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_PKT_TYPE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_PKT_TYPE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_PKT_TYPE_OC >> 0 ), + ( uint8_t ) pkt_type, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_RADIO_SET_PKT_TYPE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_gfsk_mod_params( + const void* context, const lr1121_modem_radio_mod_params_gfsk_t* mod_params ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_MODULATION_PARAMS_GFSK_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_MODULATION_PARAM_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_MODULATION_PARAM_OC >> 0 ), + ( uint8_t )( mod_params->br_in_bps >> 24 ), + ( uint8_t )( mod_params->br_in_bps >> 16 ), + ( uint8_t )( mod_params->br_in_bps >> 8 ), + ( uint8_t )( mod_params->br_in_bps >> 0 ), + ( uint8_t ) mod_params->pulse_shape, + ( uint8_t ) mod_params->bw_dsb_param, + ( uint8_t )( mod_params->fdev_in_hz >> 24 ), + ( uint8_t )( mod_params->fdev_in_hz >> 16 ), + ( uint8_t )( mod_params->fdev_in_hz >> 8 ), + ( uint8_t )( mod_params->fdev_in_hz >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_MODULATION_PARAMS_GFSK_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_bpsk_mod_params( + const void* context, const lr1121_modem_radio_mod_params_bpsk_t* mod_params ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_MODULATION_PARAMS_BPSK_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_MODULATION_PARAM_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_MODULATION_PARAM_OC >> 0 ), + ( uint8_t )( mod_params->br_in_bps >> 24 ), + ( uint8_t )( mod_params->br_in_bps >> 16 ), + ( uint8_t )( mod_params->br_in_bps >> 8 ), + ( uint8_t )( mod_params->br_in_bps >> 0 ), + ( uint8_t ) mod_params->pulse_shape, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_MODULATION_PARAMS_BPSK_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_lora_mod_params( + const void* context, const lr1121_modem_radio_mod_params_lora_t* mod_params ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_MODULATION_PARAMS_LORA_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_MODULATION_PARAM_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_MODULATION_PARAM_OC >> 0 ), + ( uint8_t ) mod_params->sf, + ( uint8_t ) mod_params->bw, + ( uint8_t ) mod_params->cr, + ( uint8_t ) mod_params->ldro, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_MODULATION_PARAMS_LORA_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_lr_fhss_mod_params( + const void* radio, const lr1121_modem_radio_mod_params_lr_fhss_t* mod_params ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_MODULATION_PARAMS_LR_FHSS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_MODULATION_PARAM_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_MODULATION_PARAM_OC >> 0 ), + ( uint8_t )( mod_params->br_in_bps >> 24 ), + ( uint8_t )( mod_params->br_in_bps >> 16 ), + ( uint8_t )( mod_params->br_in_bps >> 8 ), + ( uint8_t )( mod_params->br_in_bps >> 0 ), + ( uint8_t ) mod_params->pulse_shape, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + radio, cbuffer, LR1121_MODEM_RADIO_SET_MODULATION_PARAMS_LR_FHSS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_gfsk_pkt_params( + const void* context, const lr1121_modem_radio_pkt_params_gfsk_t* pkt_params ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_PKT_PARAM_GFSK_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_PKT_PARAM_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_PKT_PARAM_OC >> 0 ), + ( uint8_t )( pkt_params->preamble_len_in_bits >> 8 ), + ( uint8_t )( pkt_params->preamble_len_in_bits >> 0 ), + ( uint8_t )( pkt_params->preamble_detector ), + pkt_params->sync_word_len_in_bits, + ( uint8_t )( pkt_params->address_filtering ), + ( uint8_t )( pkt_params->header_type ), + pkt_params->pld_len_in_bytes, + ( uint8_t )( pkt_params->crc_type ), + ( uint8_t )( pkt_params->dc_free ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_PKT_PARAM_GFSK_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_bpsk_pkt_params( + const void* context, const lr1121_modem_radio_pkt_params_bpsk_t* pkt_params ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_PKT_PARAM_BPSK_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_PKT_PARAM_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_PKT_PARAM_OC >> 0 ), + pkt_params->pld_len_in_bytes, + ( uint8_t )( pkt_params->ramp_up_delay >> 8 ), + ( uint8_t )( pkt_params->ramp_up_delay >> 0 ), + ( uint8_t )( pkt_params->ramp_down_delay >> 8 ), + ( uint8_t )( pkt_params->ramp_down_delay >> 0 ), + ( uint8_t )( pkt_params->pld_len_in_bits >> 8 ), + ( uint8_t )( pkt_params->pld_len_in_bits >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_PKT_PARAM_BPSK_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_lora_pkt_params( + const void* context, const lr1121_modem_radio_pkt_params_lora_t* pkt_params ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_PKT_PARAM_LORA_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_PKT_PARAM_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_PKT_PARAM_OC >> 0 ), + ( uint8_t )( pkt_params->preamble_len_in_symb >> 8 ), + ( uint8_t )( pkt_params->preamble_len_in_symb >> 0 ), + ( uint8_t )( pkt_params->header_type ), + pkt_params->pld_len_in_bytes, + ( uint8_t )( pkt_params->crc ), + ( uint8_t )( pkt_params->iq ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_PKT_PARAM_LORA_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_tx_params( const void* context, const int8_t pwr_in_dbm, + const lr1121_modem_radio_ramp_time_t ramp_time ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_TX_PARAMS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_TX_PARAMS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_TX_PARAMS_OC >> 0 ), + ( uint8_t ) pwr_in_dbm, + ( uint8_t ) ramp_time, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_RADIO_SET_TX_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_pkt_address( const void* context, const uint8_t node_address, + const uint8_t broadcast_address ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_PKT_ADDRESS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_PKT_ADRS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_PKT_ADRS_OC >> 0 ), + node_address, + broadcast_address, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_PKT_ADDRESS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_rx_tx_fallback_mode( + const void* context, const lr1121_modem_radio_fallback_modes_t fallback_mode ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_RX_TX_FALLBACK_MODE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_RX_TX_FALLBACK_MODE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_RX_TX_FALLBACK_MODE_OC >> 0 ), + fallback_mode, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_RX_TX_FALLBACK_MODE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_rx_duty_cycle( const void* context, const uint32_t rx_period_in_ms, + const uint32_t sleep_period_in_ms, + const lr1121_modem_radio_rx_duty_cycle_mode_t mode ) +{ + const uint32_t rx_period_in_rtc_step = lr1121_modem_radio_convert_time_in_ms_to_rtc_step( rx_period_in_ms ); + const uint32_t sleep_period_in_rtc_step = lr1121_modem_radio_convert_time_in_ms_to_rtc_step( sleep_period_in_ms ); + + return lr1121_modem_radio_set_rx_duty_cycle_with_timings_in_rtc_step( context, rx_period_in_rtc_step, + sleep_period_in_rtc_step, mode ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_rx_duty_cycle_with_timings_in_rtc_step( + const void* context, const uint32_t rx_period_in_rtc_step, const uint32_t sleep_period_in_rtc_step, + const lr1121_modem_radio_rx_duty_cycle_mode_t mode ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_RX_DUTY_CYCLE_MODE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_RX_DUTY_CYCLE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_RX_DUTY_CYCLE_OC >> 0 ), + ( uint8_t )( rx_period_in_rtc_step >> 16 ), + ( uint8_t )( rx_period_in_rtc_step >> 8 ), + ( uint8_t )( rx_period_in_rtc_step >> 0 ), + ( uint8_t )( sleep_period_in_rtc_step >> 16 ), + ( uint8_t )( sleep_period_in_rtc_step >> 8 ), + ( uint8_t )( sleep_period_in_rtc_step >> 0 ), + ( uint8_t ) mode, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_RX_DUTY_CYCLE_MODE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_pa_cfg( const void* context, + const lr1121_modem_radio_pa_cfg_t* pa_cfg ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_PA_CFG_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_PA_CFG_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_PA_CFG_OC >> 0 ), + ( uint8_t ) pa_cfg->pa_sel, + ( uint8_t ) pa_cfg->pa_reg_supply, + pa_cfg->pa_duty_cycle, + pa_cfg->pa_hp_sel, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_RADIO_SET_PA_CFG_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_stop_timeout_on_preamble( const void* context, + const bool stop_timeout_on_preamble ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_STOP_TIMEOUT_ON_PREAMBLE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_STOP_TIMEOUT_ON_PREAMBLE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_STOP_TIMEOUT_ON_PREAMBLE_OC >> 0 ), + ( uint8_t ) stop_timeout_on_preamble, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_STOP_TIMEOUT_ON_PREAMBLE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_cad( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_CAD_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_CAD_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_CAD_OC >> 0 ), + }; + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_RADIO_SET_CAD_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_tx_cw( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_TX_CW_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_TX_CW_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_TX_CW_OC >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_RADIO_SET_TX_CW_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_tx_infinite_preamble( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_TX_INFINITE_PREAMBLE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_TX_INFINITE_PREAMBLE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_TX_INFINITE_PREAMBLE_OC >> 0 ), + }; + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_TX_INFINITE_PREAMBLE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_lora_sync_timeout( const void* context, const uint16_t nb_symbol ) +{ + if( nb_symbol <= 255 ) + { + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_LORA_SYNC_TIMEOUT_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_LORA_SYNC_TIMEOUT_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_LORA_SYNC_TIMEOUT_OC >> 0 ), + nb_symbol, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_LORA_SYNC_TIMEOUT_CMD_LENGTH, 0, 0 ); + } + else + { + uint8_t exp; + uint8_t mant; + + lr1121_modem_radio_convert_nb_symb_to_mant_exp( nb_symbol, &mant, &exp ); + + return lr1121_modem_radio_set_lora_sync_timeout_with_mantissa_exponent( context, mant, exp ); + } +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_lora_sync_timeout_with_mantissa_exponent( const void* context, + const uint8_t mantissa, + const uint8_t exponent ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_LORA_SYNC_TIMEOUT_EXT_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_LORA_SYNC_TIMEOUT_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_LORA_SYNC_TIMEOUT_OC >> 0 ), + mantissa << 3 | exponent, + 0x01, + }; + + if( ( mantissa > 31 ) || ( exponent > 7 ) ) + { + return LR1121_MODEM_RESPONSE_CODE_INVALID; + } + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_LORA_SYNC_TIMEOUT_EXT_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_gfsk_crc_params( const void* context, const uint32_t seed, + const uint32_t polynomial ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_GFSK_CRC_PARAMS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_GFSK_CRC_PARAMS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_GFSK_CRC_PARAMS_OC >> 0 ), + ( uint8_t )( seed >> 24 ), + ( uint8_t )( seed >> 16 ), + ( uint8_t )( seed >> 8 ), + ( uint8_t )( seed >> 0 ), + ( uint8_t )( polynomial >> 24 ), + ( uint8_t )( polynomial >> 16 ), + ( uint8_t )( polynomial >> 8 ), + ( uint8_t )( polynomial >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_GFSK_CRC_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_gfsk_whitening_seed( const void* context, const uint16_t seed ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_GFSK_WHITENING_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_GFSK_WHITENING_PARAMS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_GFSK_WHITENING_PARAMS_OC >> 0 ), + ( uint8_t )( seed >> 8 ), + ( uint8_t )( seed >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_GFSK_WHITENING_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_cfg_rx_boosted( const void* context, const bool enable_boost_mode ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_RX_BOOSTED_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_RX_BOOSTED_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_RX_BOOSTED_OC >> 0 ), + ( enable_boost_mode == true ) ? 0x01 : 0x00, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_RADIO_SET_RX_BOOSTED_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_rssi_calibration( + const void* context, const lr1121_modem_radio_rssi_calibration_table_t* rssi_cal_table ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_SET_RSSI_CALIBRATION_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_SET_RSSI_CALIBRATION_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_SET_RSSI_CALIBRATION_OC >> 0 ), + ( uint8_t )( ( ( rssi_cal_table->gain_tune.g5 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g4 & 0x0F ) ), + ( uint8_t )( ( ( rssi_cal_table->gain_tune.g7 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g6 & 0x0F ) ), + ( uint8_t )( ( ( rssi_cal_table->gain_tune.g9 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g8 & 0x0F ) ), + ( uint8_t )( ( ( rssi_cal_table->gain_tune.g11 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g10 & 0x0F ) ), + ( uint8_t )( ( ( rssi_cal_table->gain_tune.g13 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g12 & 0x0F ) ), + ( uint8_t )( ( ( rssi_cal_table->gain_tune.g13hp2 & 0x0F ) << 4 ) + + ( rssi_cal_table->gain_tune.g13hp1 & 0x0F ) ), + ( uint8_t )( ( ( rssi_cal_table->gain_tune.g13hp4 & 0x0F ) << 4 ) + + ( rssi_cal_table->gain_tune.g13hp3 & 0x0F ) ), + ( uint8_t )( ( ( rssi_cal_table->gain_tune.g13hp6 & 0x0F ) << 4 ) + + ( rssi_cal_table->gain_tune.g13hp5 & 0x0F ) ), + ( uint8_t )( rssi_cal_table->gain_tune.g13hp7 & 0x0F ), + ( uint8_t )( rssi_cal_table->gain_offset >> 8 ), + ( uint8_t )( rssi_cal_table->gain_offset >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RADIO_SET_RSSI_CALIBRATION_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_get_gfsk_rx_bandwidth( uint32_t bw_in_hz, + lr1121_modem_radio_gfsk_bw_t* bw_parameter ) +{ + if( bw_in_hz <= 4800 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_4800; + } + else if( bw_in_hz <= 5800 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_5800; + } + else if( bw_in_hz <= 7300 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_7300; + } + else if( bw_in_hz <= 9700 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_9700; + } + else if( bw_in_hz <= 11700 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_11700; + } + else if( bw_in_hz <= 14600 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_14600; + } + else if( bw_in_hz <= 19500 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_19500; + } + else if( bw_in_hz <= 23400 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_23400; + } + else if( bw_in_hz <= 29300 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_29300; + } + else if( bw_in_hz <= 39000 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_39000; + } + else if( bw_in_hz <= 46900 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_46900; + } + else if( bw_in_hz <= 58600 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_58600; + } + else if( bw_in_hz <= 78200 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_78200; + } + else if( bw_in_hz <= 93800 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_93800; + } + else if( bw_in_hz <= 117300 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_117300; + } + else if( bw_in_hz <= 156200 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_156200; + } + else if( bw_in_hz <= 187200 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_187200; + } + else if( bw_in_hz <= 234300 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_234300; + } + else if( bw_in_hz <= 312000 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_312000; + } + else if( bw_in_hz <= 373600 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_373600; + } + else if( bw_in_hz <= 467000 ) + { + *bw_parameter = LR1121_MODEM_RADIO_GFSK_BW_467000; + } + else + { + return LR1121_MODEM_RESPONSE_CODE_INVALID; + } + + return LR1121_MODEM_RESPONSE_CODE_OK; +} + +uint32_t lr1121_modem_radio_get_lora_time_on_air_numerator( const lr1121_modem_radio_pkt_params_lora_t* pkt_p, + const lr1121_modem_radio_mod_params_lora_t* mod_p ) +{ + const int32_t pld_len_in_bytes = pkt_p->pld_len_in_bytes; + const int32_t sf = mod_p->sf; + const bool pld_is_fix = pkt_p->header_type == LR1121_MODEM_RADIO_LORA_PKT_IMPLICIT; + + int32_t fine_synch = ( sf <= 6 ) ? 1 : 0; + bool long_interleaving = ( mod_p->cr > 4 ); + + int32_t total_bytes_nb = pld_len_in_bytes + ( ( pkt_p->crc == LR1121_MODEM_RADIO_LORA_CRC_ON ) ? 2 : 0 ); + int32_t tx_bits_symbol = sf - 2 * ( mod_p->ldro != 0 ? 1 : 0 ); + + int32_t ceil_numerator; + int32_t ceil_denominator; + + uint32_t intermed; + + int32_t symbols_nb_data; + int32_t tx_infobits_header; + int32_t tx_infobits_payload; + + if( long_interleaving ) + { + const int32_t fec_rate_numerator = 4; + const int32_t fec_rate_denominator = ( mod_p->cr + ( mod_p->cr == 7 ? 1 : 0 ) ); + + if( pld_is_fix ) + { + int32_t tx_bits_symbol_start = sf - 2 + 2 * fine_synch; + if( 8 * total_bytes_nb * fec_rate_denominator <= 7 * fec_rate_numerator * tx_bits_symbol_start ) + { + ceil_numerator = 8 * total_bytes_nb * fec_rate_denominator; + ceil_denominator = fec_rate_numerator * tx_bits_symbol_start; + } + else + { + int32_t tx_codedbits_header = tx_bits_symbol_start * 8; + ceil_numerator = 8 * fec_rate_numerator * tx_bits_symbol + 8 * total_bytes_nb * fec_rate_denominator - + fec_rate_numerator * tx_codedbits_header; + ceil_denominator = fec_rate_numerator * tx_bits_symbol; + } + } + else + { + tx_infobits_header = ( sf * 4 + fine_synch * 8 - 28 ) & ~0x07; + if( tx_infobits_header < 8 * total_bytes_nb ) + { + if( tx_infobits_header > 8 * pld_len_in_bytes ) + { + tx_infobits_header = 8 * pld_len_in_bytes; + } + } + tx_infobits_payload = 8 * total_bytes_nb - tx_infobits_header; + if( tx_infobits_payload < 0 ) + { + tx_infobits_payload = 0; + } + + ceil_numerator = tx_infobits_payload * fec_rate_denominator + 8 * fec_rate_numerator * tx_bits_symbol; + ceil_denominator = fec_rate_numerator * tx_bits_symbol; + } + } + else + { + tx_infobits_header = sf * 4 + fine_synch * 8 - 8; + + if( !pld_is_fix ) + { + tx_infobits_header -= 20; + } + + tx_infobits_payload = 8 * total_bytes_nb - tx_infobits_header; + + if( tx_infobits_payload < 0 ) tx_infobits_payload = 0; + + ceil_numerator = tx_infobits_payload; + ceil_denominator = 4 * tx_bits_symbol; + } + + symbols_nb_data = ( ( ceil_numerator + ceil_denominator - 1 ) / ceil_denominator ); + if( !long_interleaving ) + { + symbols_nb_data = symbols_nb_data * ( mod_p->cr + 4 ) + 8; + } + intermed = pkt_p->preamble_len_in_symb + 4 + 2 * fine_synch + symbols_nb_data; + + return ( uint32_t )( ( 4 * intermed + 1 ) * ( 1 << ( sf - 2 ) ) ) - 1; +} + +uint32_t lr1121_modem_radio_get_lora_bw_in_hz( lr1121_modem_radio_lora_bw_t bw ) +{ + uint32_t bw_in_hz = 0; + + switch( bw ) + { + case LR1121_MODEM_RADIO_LORA_BW_10: + bw_in_hz = 10417UL; + break; + case LR1121_MODEM_RADIO_LORA_BW_15: + bw_in_hz = 15625UL; + break; + case LR1121_MODEM_RADIO_LORA_BW_20: + bw_in_hz = 20833UL; + break; + case LR1121_MODEM_RADIO_LORA_BW_31: + bw_in_hz = 31250UL; + break; + case LR1121_MODEM_RADIO_LORA_BW_41: + bw_in_hz = 41667UL; + break; + case LR1121_MODEM_RADIO_LORA_BW_62: + bw_in_hz = 62500UL; + break; + case LR1121_MODEM_RADIO_LORA_BW_125: + bw_in_hz = 125000UL; + break; + case LR1121_MODEM_RADIO_LORA_BW_250: + bw_in_hz = 250000UL; + break; + case LR1121_MODEM_RADIO_LORA_BW_500: + bw_in_hz = 500000UL; + break; + case LR1121_MODEM_RADIO_LORA_BW_200: + bw_in_hz = 203000UL; + break; + case LR1121_MODEM_RADIO_LORA_BW_400: + bw_in_hz = 406000UL; + break; + case LR1121_MODEM_RADIO_LORA_BW_800: + bw_in_hz = 812000UL; + break; + } + + return bw_in_hz; +} + +uint32_t lr1121_modem_radio_get_lora_time_on_air_in_ms( const lr1121_modem_radio_pkt_params_lora_t* pkt_p, + const lr1121_modem_radio_mod_params_lora_t* mod_p ) +{ + uint32_t numerator = 1000U * lr1121_modem_radio_get_lora_time_on_air_numerator( pkt_p, mod_p ); + uint32_t denominator = lr1121_modem_radio_get_lora_bw_in_hz( mod_p->bw ); + // Perform integral ceil() + return ( numerator + denominator - 1 ) / denominator; +} + +uint32_t lr1121_modem_radio_get_gfsk_time_on_air_numerator( const lr1121_modem_radio_pkt_params_gfsk_t* pkt_p ) +{ + uint8_t header_len_in_bits; + + switch( pkt_p->header_type ) + { + case LR1121_MODEM_RADIO_GFSK_PKT_FIX_LEN: + { + header_len_in_bits = 0; + break; + } + case LR1121_MODEM_RADIO_GFSK_PKT_VAR_LEN: + { + header_len_in_bits = 8; + break; + } + case LR1121_MODEM_RADIO_GFSK_PKT_VAR_LEN_SX128X_COMP: + { + header_len_in_bits = 9; + break; + } + default: + { + return 0; + } + } + + return pkt_p->preamble_len_in_bits + header_len_in_bits + pkt_p->sync_word_len_in_bits + + ( ( pkt_p->pld_len_in_bytes + + ( pkt_p->address_filtering == LR1121_MODEM_RADIO_GFSK_ADDRESS_FILTERING_DISABLE ? 0 : 1 ) + + lr1121_modem_radio_get_gfsk_crc_len_in_bytes( pkt_p->crc_type ) ) + << 3 ); +} + +uint32_t lr1121_modem_radio_get_gfsk_time_on_air_in_ms( const lr1121_modem_radio_pkt_params_gfsk_t* pkt_p, + const lr1121_modem_radio_mod_params_gfsk_t* mod_p ) +{ + uint32_t numerator = 1000U * lr1121_modem_radio_get_gfsk_time_on_air_numerator( pkt_p ); + uint32_t denominator = mod_p->br_in_bps; + + // Perform integral ceil() + return ( numerator + denominator - 1 ) / denominator; +} + +uint32_t lr1121_modem_radio_convert_time_in_ms_to_rtc_step( uint32_t time_in_ms ) +{ + return ( uint32_t )( time_in_ms * LR1121_MODEM_RTC_FREQ_IN_HZ / 1000 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_cfg_bluetooth_low_energy_beaconning_compatibility( + const void* context, const uint8_t channel_id, const uint8_t* buffer, const uint8_t length ) +{ + const uint8_t command[LR1121_MODEM_RADIO_CFG_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_CFG_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_CFG_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_OC >> 0 ), + channel_id, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, command, LR1121_MODEM_RADIO_CFG_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_CMD_LENGTH, buffer, + length ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_get_lora_rx_info( const void* context, bool* is_crc_present, + lr1121_modem_radio_lora_cr_t* cr ) +{ + const uint8_t cbuffer[LR1121_MODEM_RADIO_GET_LORA_RX_INFO_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_GET_LORA_RX_INFO_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_GET_LORA_RX_INFO_OC >> 0 ), + }; + uint8_t rbuffer = 0; + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_RADIO_GET_LORA_RX_INFO_CMD_LENGTH, &rbuffer, 1 ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *is_crc_present = ( ( ( rbuffer & ( 0x01 << 4 ) ) != 0 ) ) ? true : false; + *cr = ( lr1121_modem_radio_lora_cr_t )( rbuffer & 0x07 ); + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_radio_cfg_and_send_bluetooth_low_energy_beaconning_compatibility( + const void* context, const uint8_t channel_id, const uint8_t* buffer, const uint8_t length ) +{ + const uint8_t command[LR1121_MODEM_RADIO_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_SEND_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_RADIO_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_SEND_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_RADIO_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_SEND_OC >> 0 ), + channel_id, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, command, LR1121_MODEM_RADIO_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_SEND_CMD_LENGTH, buffer, + length ); +} + +uint16_t lr1121_modem_radio_convert_nb_symb_to_mant_exp( const uint16_t nb_symbol, uint8_t* mant, uint8_t* exp ) +{ + uint8_t exp_loc = 0; + uint16_t mant_loc = ( nb_symbol + 1 ) >> 1; + + while( mant_loc > 31 ) + { + mant_loc = ( mant_loc + 3 ) >> 2; + exp_loc++; + } + + *mant = ( uint8_t ) mant_loc; + *exp = exp_loc; + + return mant_loc << ( 2 * exp_loc + 1 ); +} + +lr1121_modem_response_code_t lr1121_modem_radio_set_lna_mode( const void* context, + lr1121_modem_radio_lna_mode_t lna_config ) +{ + return lr1121_modem_regmem_write_regmem32_mask( context, 0x00F3008C, 0xF0, lna_config << 4 ); +} +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static inline uint32_t lr1121_modem_radio_get_gfsk_crc_len_in_bytes( lr1121_modem_radio_gfsk_crc_type_t crc_type ) +{ + switch( crc_type ) + { + case LR1121_MODEM_RADIO_GFSK_CRC_OFF: + return 0; + case LR1121_MODEM_RADIO_GFSK_CRC_1_BYTE: + return 1; + case LR1121_MODEM_RADIO_GFSK_CRC_2_BYTES: + return 2; + case LR1121_MODEM_RADIO_GFSK_CRC_1_BYTE_INV: + return 1; + case LR1121_MODEM_RADIO_GFSK_CRC_2_BYTES_INV: + return 2; + } + + return 0; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_radio_timings.c b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_radio_timings.c new file mode 100755 index 0000000..6890645 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_radio_timings.c @@ -0,0 +1,234 @@ +/** + * @file lr1121_modem_radio_timings.c + * + * @brief LR1121 timing helper functions implementation + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr1121_modem_radio_timings.h" +#include "lr1121_modem_radio.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/** + * @brief Time in microsecond taken by the chip to process the Rx done interrupt + */ +#define RX_DONE_IRQ_PROCESSING_TIME_IN_US 74 + +/** + * @brief Time in microsecond taken by the chip to process the Tx done interrupt + */ +#define TX_DONE_IRQ_PROCESSING_TIME_IN_US 111 + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/** + * @brief Get the power amplifier ramp time for a given power amplifier ramp time parameter + * + * @param [in] ramp_time Power amplifier ramp time parameter + * + * @returns Ramp time in microsecond + */ +static uint32_t lr1121_modem_radio_timings_get_pa_ramp_time_in_us( const lr1121_modem_radio_ramp_time_t ramp_time ); + +/** + * @brief Get the LoRa reception input delay + * + * @param [in] bw LoRa bandwidth + * + * @returns LoRa reception input delay in microsecond + */ +static uint32_t lr1121_modem_radio_timings_get_lora_rx_input_delay_in_us( lr1121_modem_radio_lora_bw_t bw ); + +/** + * @brief Get the LoRa symbol time + * + * @param [in] bw LoRa bandwidth + * @param [in] sf LoRa spreading factor + * + * @returns LoRa symbol time in microsecond + */ +static uint32_t lr1121_modem_radio_timings_get_lora_symb_time_in_us( const lr1121_modem_radio_lora_sf_t sf, + const lr1121_modem_radio_lora_bw_t bw ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +uint32_t lr1121_modem_radio_timings_get_delay_between_last_bit_sent_and_rx_done_in_us( + const lr1121_modem_radio_mod_params_lora_t* mod_params ) +{ + return lr1121_modem_radio_timings_get_lora_rx_input_delay_in_us( mod_params->bw ) + + 2 * lr1121_modem_radio_timings_get_lora_symb_time_in_us( mod_params->sf, mod_params->bw ) + + RX_DONE_IRQ_PROCESSING_TIME_IN_US; +} + +uint32_t lr1121_modem_radio_timings_get_delay_between_last_bit_sent_and_tx_done_in_us( + const lr1121_modem_radio_ramp_time_t ramp_time ) +{ + return lr1121_modem_radio_timings_get_pa_ramp_time_in_us( ramp_time ) + TX_DONE_IRQ_PROCESSING_TIME_IN_US; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static uint32_t lr1121_modem_radio_timings_get_pa_ramp_time_in_us( const lr1121_modem_radio_ramp_time_t ramp_time ) +{ + switch( ramp_time ) + { + case LR1121_MODEM_RADIO_RAMP_16_US: + { + return 16; + } + case LR1121_MODEM_RADIO_RAMP_32_US: + { + return 32; + } + case LR1121_MODEM_RADIO_RAMP_48_US: + { + return 48; + } + case LR1121_MODEM_RADIO_RAMP_64_US: + { + return 64; + } + case LR1121_MODEM_RADIO_RAMP_80_US: + { + return 80; + } + case LR1121_MODEM_RADIO_RAMP_96_US: + { + return 96; + } + case LR1121_MODEM_RADIO_RAMP_112_US: + { + return 112; + } + case LR1121_MODEM_RADIO_RAMP_128_US: + { + return 128; + } + case LR1121_MODEM_RADIO_RAMP_144_US: + { + return 144; + } + case LR1121_MODEM_RADIO_RAMP_160_US: + { + return 160; + } + case LR1121_MODEM_RADIO_RAMP_176_US: + { + return 176; + } + case LR1121_MODEM_RADIO_RAMP_192_US: + { + return 192; + } + case LR1121_MODEM_RADIO_RAMP_208_US: + { + return 208; + } + case LR1121_MODEM_RADIO_RAMP_240_US: + { + return 240; + } + case LR1121_MODEM_RADIO_RAMP_272_US: + { + return 272; + } + case LR1121_MODEM_RADIO_RAMP_304_US: + { + return 304; + } + default: + return 0; + } +} + +static uint32_t lr1121_modem_radio_timings_get_lora_rx_input_delay_in_us( lr1121_modem_radio_lora_bw_t bw ) +{ + switch( bw ) + { + case LR1121_MODEM_RADIO_LORA_BW_500: + { + return 16; + } + case LR1121_MODEM_RADIO_LORA_BW_250: + { + return 31; + } + case LR1121_MODEM_RADIO_LORA_BW_125: + { + return 57; + } + default: + { + return 0; + } + } +} + +static uint32_t lr1121_modem_radio_timings_get_lora_symb_time_in_us( const lr1121_modem_radio_lora_sf_t sf, + const lr1121_modem_radio_lora_bw_t bw ) +{ + return ( 1 << ( uint8_t ) sf ) * 1000000 / lr1121_modem_radio_get_lora_bw_in_hz( bw ); +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_regmem.c b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_regmem.c new file mode 100755 index 0000000..1f43591 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_regmem.c @@ -0,0 +1,332 @@ +/*! + * @file lr1121_modem_regmem.c + * + * @brief Register/memory driver implementation for LR1121 + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr1121_modem_regmem.h" +#include "lr1121_modem_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR1121_MODEM_REGMEM_CLEAR_RXBUFFER_CMD_LENGTH 2 +#define LR1121_MODEM_REGMEM_WRITE_REGMEM32_CMD_LENGTH ( 2 + 4 ) +#define LR1121_MODEM_REGMEM_READ_REGMEM32_CMD_LENGTH ( 2 + 4 + 1 ) +#define LR1121_MODEM_REGMEM_WRITE_MEM8_CMD_LENGTH ( 2 + 4 ) +#define LR1121_MODEM_REGMEM_READ_MEM8_CMD_LENGTH ( 2 + 4 + 1 ) +#define LR1121_MODEM_REGMEM_WRITE_BUFFER8_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_REGMEM_READ_BUFFER8_CMD_LENGTH ( 2 + 2 ) +#define LR1121_MODEM_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH ( 2 + 4 + 4 + 4 ) + +#define LR1121_MODEM_REGMEM_BUFFER_SIZE_MAX ( 256 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for register and memory related operations + */ +enum +{ + LR1121_MODEM_REGMEM_WRITE_REGMEM32_OC = 0x0105, + LR1121_MODEM_REGMEM_READ_REGMEM32_OC = 0x0106, + LR1121_MODEM_REGMEM_WRITE_MEM8_OC = 0x0107, + LR1121_MODEM_REGMEM_READ_MEM8_OC = 0x0108, + LR1121_MODEM_REGMEM_WRITE_BUFFER8_OC = 0x0109, + LR1121_MODEM_REGMEM_READ_BUFFER8_OC = 0x010A, + LR1121_MODEM_REGMEM_CLEAR_RXBUFFER_OC = 0x010B, + LR1121_MODEM_REGMEM_WRITE_REGMEM32_MASK_OC = 0x010C, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Helper function that fill both cbuffer with opcode and memory address + * + * It is typically used in read/write regmem32 functions. + * + * @warning It is up to the caller to ensure cbuffer is big enough to contain opcode and address! + */ +static void lr1121_modem_regmem_fill_cbuffer_opcode_address( uint8_t* cbuffer, uint16_t opcode, uint32_t address ); + +/*! + * @brief Helper function that fill both cbuffer with opcode memory address, and data length to read + * + * It is typically used in read functions. + * + * @warning It is up to the caller to ensure cbuffer is big enough to contain opcode and address! + */ +static void lr1121_modem_regmem_fill_cbuffer_opcode_address_length( uint8_t* cbuffer, uint16_t opcode, uint32_t address, + uint8_t length ); + +/*! + * @brief Helper function that fill both cbuffer with data + * + * It is typically used in write write regmem32 functions. + * + * @warning It is up to the caller to ensure cdata is big enough to contain all data! + */ +static void lr1121_modem_regmem_fill_cdata( uint8_t* cdata, const uint32_t* data, uint8_t data_length ); + +/*! + * @brief Helper function that fill both cbuffer and cdata buffers with opcode, memory address and data + * + * It is typically used to factorize and write regmem32 operations. Behind the scene it calls the other helpers + * lr1121_modem_regmem_fill_cbuffer_opcode_address and lr1121_modem_regmem_fill_cdata. + * + * @warning It is up to the caller to ensure cbuffer and cdata are big enough to contain their respective information! + */ +static void lr1121_modem_regmem_fill_cbuffer_cdata_opcode_address_data( uint8_t* cbuffer, uint8_t* cdata, + uint16_t opcode, uint32_t address, + const uint32_t* data, uint8_t data_length ); + +/*! + * @brief Helper function that convert an array of uint8_t into an array of uint32_t + * + * Typically used in the read function returning uint32_t array. + * + * @warning It is up to the caller to ensure the raw_buffer is of length at least "out_buffer_length * + * sizeof(uint32_t)"! + */ +static void lr1121_modem_regmem_fill_out_buffer_from_raw_buffer( uint32_t* out_buffer, const uint8_t* raw_buffer, + uint8_t out_buffer_length ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr1121_modem_response_code_t lr1121_modem_regmem_write_regmem32( const void* context, const uint32_t address, + const uint32_t* buffer, const uint8_t length ) +{ + uint8_t cbuffer[LR1121_MODEM_REGMEM_WRITE_REGMEM32_CMD_LENGTH]; + uint8_t cdata[LR1121_MODEM_REGMEM_BUFFER_SIZE_MAX]; + + if( length > LR1121_MODEM_REGMEM_MAX_WRITE_READ_WORDS ) + { + return LR1121_MODEM_RESPONSE_CODE_BAD_SIZE; + } + + lr1121_modem_regmem_fill_cbuffer_cdata_opcode_address_data( cbuffer, cdata, LR1121_MODEM_REGMEM_WRITE_REGMEM32_OC, + address, buffer, length ); + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_REGMEM_WRITE_REGMEM32_CMD_LENGTH, cdata, length * sizeof( uint32_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_regmem_read_regmem32( const void* context, const uint32_t address, + uint32_t* buffer, const uint8_t length ) +{ + uint8_t cbuffer[LR1121_MODEM_REGMEM_READ_REGMEM32_CMD_LENGTH] = { 0 }; + + if( length > LR1121_MODEM_REGMEM_MAX_WRITE_READ_WORDS ) + { + return LR1121_MODEM_RESPONSE_CODE_BAD_SIZE; + } + + lr1121_modem_regmem_fill_cbuffer_opcode_address_length( cbuffer, LR1121_MODEM_REGMEM_READ_REGMEM32_OC, address, + length ); + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_REGMEM_READ_REGMEM32_CMD_LENGTH, ( uint8_t* ) buffer, + length * sizeof( uint32_t ) ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + lr1121_modem_regmem_fill_out_buffer_from_raw_buffer( buffer, ( const uint8_t* ) buffer, length ); + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_regmem_write_mem8( const void* context, const uint32_t address, + const uint8_t* buffer, const uint8_t length ) +{ + uint8_t cbuffer[LR1121_MODEM_REGMEM_WRITE_MEM8_CMD_LENGTH]; + + lr1121_modem_regmem_fill_cbuffer_opcode_address( cbuffer, LR1121_MODEM_REGMEM_WRITE_MEM8_OC, address ); + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_REGMEM_WRITE_MEM8_CMD_LENGTH, buffer, length ); +} + +lr1121_modem_response_code_t lr1121_modem_regmem_read_mem8( const void* context, const uint32_t address, + uint8_t* buffer, const uint8_t length ) +{ + uint8_t cbuffer[LR1121_MODEM_REGMEM_READ_MEM8_CMD_LENGTH]; + + lr1121_modem_regmem_fill_cbuffer_opcode_address_length( cbuffer, LR1121_MODEM_REGMEM_READ_MEM8_OC, address, + length ); + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_REGMEM_READ_MEM8_CMD_LENGTH, buffer, length ); +} + +lr1121_modem_response_code_t lr1121_modem_regmem_write_buffer8( const void* context, const uint8_t* buffer, + const uint8_t length ) +{ + const uint8_t cbuffer[LR1121_MODEM_REGMEM_WRITE_BUFFER8_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_REGMEM_WRITE_BUFFER8_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_REGMEM_WRITE_BUFFER8_OC >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_REGMEM_WRITE_BUFFER8_CMD_LENGTH, buffer, length ); +} + +lr1121_modem_response_code_t lr1121_modem_regmem_read_buffer8( const void* context, uint8_t* buffer, + const uint8_t offset, const uint8_t length ) +{ + const uint8_t cbuffer[LR1121_MODEM_REGMEM_READ_BUFFER8_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_REGMEM_READ_BUFFER8_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_REGMEM_READ_BUFFER8_OC >> 0 ), + offset, + length, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_REGMEM_READ_BUFFER8_CMD_LENGTH, buffer, length ); +} + +lr1121_modem_response_code_t lr1121_modem_regmem_clear_rxbuffer( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_REGMEM_CLEAR_RXBUFFER_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_REGMEM_CLEAR_RXBUFFER_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_REGMEM_CLEAR_RXBUFFER_OC >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_REGMEM_CLEAR_RXBUFFER_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_regmem_write_regmem32_mask( const void* context, const uint32_t address, + const uint32_t mask, const uint32_t data ) +{ + uint8_t cbuffer[LR1121_MODEM_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH]; + + lr1121_modem_regmem_fill_cbuffer_opcode_address( cbuffer, LR1121_MODEM_REGMEM_WRITE_REGMEM32_MASK_OC, address ); + + cbuffer[6] = ( uint8_t )( mask >> 24 ); + cbuffer[7] = ( uint8_t )( mask >> 16 ); + cbuffer[8] = ( uint8_t )( mask >> 8 ); + cbuffer[9] = ( uint8_t )( mask >> 0 ); + + cbuffer[10] = ( uint8_t )( data >> 24 ); + cbuffer[11] = ( uint8_t )( data >> 16 ); + cbuffer[12] = ( uint8_t )( data >> 8 ); + cbuffer[13] = ( uint8_t )( data >> 0 ); + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH, 0, 0 ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +void lr1121_modem_regmem_fill_cbuffer_opcode_address( uint8_t* cbuffer, uint16_t opcode, uint32_t address ) +{ + cbuffer[0] = ( uint8_t )( opcode >> 8 ); + cbuffer[1] = ( uint8_t )( opcode >> 0 ); + + cbuffer[2] = ( uint8_t )( address >> 24 ); + cbuffer[3] = ( uint8_t )( address >> 16 ); + cbuffer[4] = ( uint8_t )( address >> 8 ); + cbuffer[5] = ( uint8_t )( address >> 0 ); +} + +void lr1121_modem_regmem_fill_cbuffer_opcode_address_length( uint8_t* cbuffer, uint16_t opcode, uint32_t address, + uint8_t length ) +{ + lr1121_modem_regmem_fill_cbuffer_opcode_address( cbuffer, opcode, address ); + cbuffer[6] = length; +} + +void lr1121_modem_regmem_fill_cdata( uint8_t* cdata, const uint32_t* data, uint8_t data_length ) +{ + for( uint16_t index = 0; index < data_length; index++ ) + { + uint8_t* cdata_local = &cdata[index * sizeof( uint32_t )]; + + cdata_local[0] = ( uint8_t )( data[index] >> 24 ); + cdata_local[1] = ( uint8_t )( data[index] >> 16 ); + cdata_local[2] = ( uint8_t )( data[index] >> 8 ); + cdata_local[3] = ( uint8_t )( data[index] >> 0 ); + } +} + +void lr1121_modem_regmem_fill_cbuffer_cdata_opcode_address_data( uint8_t* cbuffer, uint8_t* cdata, uint16_t opcode, + uint32_t address, const uint32_t* data, + uint8_t data_length ) +{ + lr1121_modem_regmem_fill_cbuffer_opcode_address( cbuffer, opcode, address ); + lr1121_modem_regmem_fill_cdata( cdata, data, data_length ); +} + +void lr1121_modem_regmem_fill_out_buffer_from_raw_buffer( uint32_t* out_buffer, const uint8_t* raw_buffer, + uint8_t out_buffer_length ) +{ + for( uint8_t out_index = 0; out_index < out_buffer_length; out_index++ ) + { + const uint8_t* raw_buffer_local = &raw_buffer[out_index * 4]; + + out_buffer[out_index] = ( ( uint32_t ) raw_buffer_local[0] << 24 ) + + ( ( uint32_t ) raw_buffer_local[1] << 16 ) + ( ( uint32_t ) raw_buffer_local[2] << 8 ) + + ( ( uint32_t ) raw_buffer_local[3] << 0 ); + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_relay.c b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_relay.c new file mode 100755 index 0000000..ca6dfea --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_relay.c @@ -0,0 +1,159 @@ +/*! + * @file lr1121_modem_relay.c + * + * @brief Relay driver implementation for LR1121 modem + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr1121_modem_relay.h" +#include +#include "lr1121_modem_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +#define LR1121_MODEM_RELAY_GET_TX_CONFIG_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_RELAY_SET_TX_CONFIG_RBUFFER_LENGTH ( 3 + 14 ) + +#define LR1121_MODEM_RELAY_GET_TX_CONFIG_RBUFFER_LENGTH ( 14 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operation code command + */ +enum +{ + LR1121_MODEM_RELAY_GET_TX_CONFIG_CMD = 0x00, + LR1121_MODEM_RELAY_SET_TX_CONFIG_CMD = 0x01, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +static uint32_t lr1121_uint8_to_uint32( const uint8_t value[4] ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr1121_modem_response_code_t lr1121_modem_relay_get_tx_config( const void* context, + lr1121_modem_relay_tx_configuration_t* configuration ) +{ + uint8_t rbuffer[LR1121_MODEM_RELAY_GET_TX_CONFIG_RBUFFER_LENGTH] = { 0 }; + + const uint8_t cbuffer[LR1121_MODEM_RELAY_GET_TX_CONFIG_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_RELAY >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_RELAY, + LR1121_MODEM_RELAY_GET_TX_CONFIG_CMD, + }; + + const lr1121_modem_response_code_t rc = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_RELAY_GET_TX_CONFIG_CMD_LENGTH, rbuffer, + LR1121_MODEM_RELAY_GET_TX_CONFIG_RBUFFER_LENGTH ); + + if( rc == LR1121_MODEM_RESPONSE_CODE_OK ) + { + configuration->wor_second_channel_frequency_hz = lr1121_uint8_to_uint32( rbuffer + 0 ); + configuration->wor_ack_second_channel_frequency_hz = lr1121_uint8_to_uint32( rbuffer + 4 ); + configuration->wor_second_channel_datarate = rbuffer[8]; + configuration->wor_second_channel_enable = rbuffer[9]; + configuration->backoff_behavior = rbuffer[10]; + configuration->activation = ( lr1121_modem_relay_activation_t ) rbuffer[11]; + configuration->smart_level = ( lr1121_modem_relay_smart_level_t ) rbuffer[12]; + configuration->missed_ack_to_unsynchronized_threshold = rbuffer[13]; + } + + return rc; +} + +lr1121_modem_response_code_t lr1121_modem_relay_set_tx_config( + const void* context, const lr1121_modem_relay_tx_configuration_t* configuration ) +{ + const uint8_t cbuffer[LR1121_MODEM_RELAY_SET_TX_CONFIG_RBUFFER_LENGTH] = { + ( uint8_t )( LR1121_MODEM_GROUP_ID_RELAY >> 8 ), + ( uint8_t ) LR1121_MODEM_GROUP_ID_RELAY, + LR1121_MODEM_RELAY_SET_TX_CONFIG_CMD, + ( uint8_t )( configuration->wor_second_channel_frequency_hz >> 24 ), + ( uint8_t )( configuration->wor_second_channel_frequency_hz >> 16 ), + ( uint8_t )( configuration->wor_second_channel_frequency_hz >> 8 ), + ( uint8_t ) configuration->wor_second_channel_frequency_hz, + ( uint8_t )( configuration->wor_ack_second_channel_frequency_hz >> 24 ), + ( uint8_t )( configuration->wor_ack_second_channel_frequency_hz >> 16 ), + ( uint8_t )( configuration->wor_ack_second_channel_frequency_hz >> 8 ), + ( uint8_t ) configuration->wor_ack_second_channel_frequency_hz, + configuration->wor_second_channel_datarate, + configuration->wor_second_channel_enable, + configuration->backoff_behavior, + ( uint8_t ) configuration->activation, + ( uint8_t ) configuration->smart_level, + configuration->missed_ack_to_unsynchronized_threshold, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_RELAY_SET_TX_CONFIG_RBUFFER_LENGTH, 0, 0 ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +uint32_t lr1121_uint8_to_uint32( const uint8_t value[4] ) +{ + return ( ( ( uint32_t ) value[0] ) << 24 ) + ( ( ( uint32_t ) value[1] ) << 16 ) + + ( ( ( uint32_t ) value[2] ) << 8 ) + ( ( ( uint32_t ) value[3] ) ); +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_system.c b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_system.c new file mode 100755 index 0000000..7221c23 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_modem/lr1121_modem_system.c @@ -0,0 +1,699 @@ +/*! + * @file lr1121_modem_system.c + * + * @brief System driver implementation for LR1121 + * + * @copyright Copyright Semtech Corporation 2024. All rights reserved. + * + * @license{The Clear BSD License + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * @par + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.} + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include + +#include "lr1121_modem_system.h" +#include "lr1121_modem_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/*! + * @brief Length in byte of the LR1121 version blob + */ +#define LR1121_MODEM_SYSTEM_VERSION_LENGTH ( 4 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR1121_MODEM_SYSTEM_GET_VERSION_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_SYSTEM_GET_ERRORS_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_SYSTEM_CLEAR_ERRORS_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_SYSTEM_CALIBRATE_CMD_LENGTH ( 2 + 1 ) +#define LR1121_MODEM_SYSTEM_SET_REGMODE_CMD_LENGTH ( 2 + 1 ) +#define LR1121_MODEM_SYSTEM_CALIBRATE_IMAGE_CMD_LENGTH ( 2 + 2 ) +#define LR1121_MODEM_SYSTEM_SET_DIO_AS_RF_SWITCH_CMD_LENGTH ( 2 + 8 ) +#define LR1121_MODEM_SYSTEM_SET_DIO_IRQ_PARAMS_CMD_LENGTH ( 2 + 8 ) +#define LR1121_MODEM_SYSTEM_CLEAR_IRQ_CMD_LENGTH ( 2 + 4 ) +#define LR1121_MODEM_SYSTEM_CFG_LFCLK_CMD_LENGTH ( 2 + 1 ) +#define LR1121_MODEM_SYSTEM_SET_TCXO_MODE_CMD_LENGTH ( 2 + 4 ) +#define LR1121_MODEM_SYSTEM_REBOOT_CMD_LENGTH ( 2 + 1 ) +#define LR1121_MODEM_SYSTEM_GET_VBAT_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_SYSTEM_GET_TEMP_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_SYSTEM_SET_SLEEP_CMD_LENGTH ( 2 + 5 ) +#define LR1121_MODEM_SYSTEM_SET_STANDBY_CMD_LENGTH ( 2 + 1 ) +#define LR1121_MODEM_SYSTEM_SET_FS_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_SYSTEM_ERASE_INFOPAGE_CMD_LENGTH ( 2 + 1 ) +#define LR1121_MODEM_SYSTEM_WRITE_INFOPAGE_CMD_LENGTH ( 2 + 3 ) +#define LR1121_MODEM_SYSTEM_READ_INFOPAGE_CMD_LENGTH ( 2 + 4 ) +#define LR1121_MODEM_SYSTEM_READ_UID_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_SYSTEM_READ_JOIN_EUI_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_SYSTEM_READ_PIN_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_SYSTEM_READ_PIN_CUSTOM_EUI_CMD_LENGTH ( LR1121_MODEM_SYSTEM_READ_PIN_CMD_LENGTH + 17 ) +#define LR1121_MODEM_SYSTEM_GET_RANDOM_CMD_LENGTH ( 2 ) +#define LR1121_MODEM_SYSTEM_ENABLE_SPI_CRC_CMD_LENGTH ( 3 ) +#define LR1121_MODEM_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_CMD_LENGTH ( 3 ) + +#define LR1121_MODEM_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH ( 6 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for system-related operations + */ +enum +{ + LR1121_MODEM_SYSTEM_GET_STATUS_OC = 0x0100, + LR1121_MODEM_SYSTEM_GET_VERSION_OC = 0x0101, + LR1121_MODEM_SYSTEM_GET_ERRORS_OC = 0x010D, + LR1121_MODEM_SYSTEM_CLEAR_ERRORS_OC = 0x010E, + LR1121_MODEM_SYSTEM_CALIBRATE_OC = 0x010F, + LR1121_MODEM_SYSTEM_SET_REGMODE_OC = 0x0110, + LR1121_MODEM_SYSTEM_CALIBRATE_IMAGE_OC = 0x0111, + LR1121_MODEM_SYSTEM_SET_DIO_AS_RF_SWITCH_OC = 0x0112, + LR1121_MODEM_SYSTEM_SET_DIOIRQPARAMS_OC = 0x0113, + LR1121_MODEM_SYSTEM_CLEAR_IRQ_OC = 0x0114, + LR1121_MODEM_SYSTEM_CFG_LFCLK_OC = 0x0116, + LR1121_MODEM_SYSTEM_SET_TCXO_MODE_OC = 0x0117, + LR1121_MODEM_SYSTEM_REBOOT_OC = 0x0118, + LR1121_MODEM_SYSTEM_GET_VBAT_OC = 0x0119, + LR1121_MODEM_SYSTEM_GET_TEMP_OC = 0x011A, + LR1121_MODEM_SYSTEM_SET_SLEEP_OC = 0x011B, + LR1121_MODEM_SYSTEM_SET_STANDBY_OC = 0x011C, + LR1121_MODEM_SYSTEM_SET_FS_OC = 0x011D, + LR1121_MODEM_SYSTEM_GET_RANDOM_OC = 0x0120, + LR1121_MODEM_SYSTEM_ERASE_INFOPAGE_OC = 0x0121, + LR1121_MODEM_SYSTEM_WRITE_INFOPAGE_OC = 0x0122, + LR1121_MODEM_SYSTEM_READ_INFOPAGE_OC = 0x0123, + LR1121_MODEM_SYSTEM_READ_UID_OC = 0x0125, + LR1121_MODEM_SYSTEM_READ_JOIN_EUI_OC = 0x0126, + LR1121_MODEM_SYSTEM_READ_PIN_OC = 0x0127, + LR1121_MODEM_SYSTEM_ENABLE_SPI_CRC_OC = 0x0128, + LR1121_MODEM_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_OC = 0x012A, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Fill stat1 structure with data from stat1_byte + * + * @param [in] stat1_byte stat1 byte + * @param [out] stat1 stat1 structure + */ +static void lr1121_modem_system_convert_stat1_byte_to_enum( uint8_t stat1_byte, lr1121_modem_system_stat1_t* stat1 ); + +/*! + * @brief Fill stat2 structure with data from stat2_byte + * + * @param [in] stat2_byte stat2 byte + * @param [out] stat2 stat2 structure + */ +static void lr1121_modem_system_convert_stat2_byte_to_enum( uint8_t stat2_byte, lr1121_modem_system_stat2_t* stat2 ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr1121_modem_response_code_t lr1121_modem_system_reset( const void* context ) +{ + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_reset( context ); +} + +lr1121_modem_response_code_t lr1121_modem_system_get_status( const void* context, lr1121_modem_system_stat1_t* stat1, + lr1121_modem_system_stat2_t* stat2, + lr1121_modem_system_irq_mask_t* irq_status ) +{ + uint8_t data[LR1121_MODEM_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH] = { 0 }; + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_direct_read( + context, data, LR1121_MODEM_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + lr1121_modem_system_convert_stat1_byte_to_enum( data[0], stat1 ); + lr1121_modem_system_convert_stat2_byte_to_enum( data[1], stat2 ); + if( irq_status != NULL ) + { + *irq_status = ( ( lr1121_modem_system_irq_mask_t ) data[2] << 24 ) + + ( ( lr1121_modem_system_irq_mask_t ) data[3] << 16 ) + + ( ( lr1121_modem_system_irq_mask_t ) data[4] << 8 ) + + ( ( lr1121_modem_system_irq_mask_t ) data[5] << 0 ); + } + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_system_clear_reset_status_info( const void* context ) +{ + uint8_t cbuffer[2] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_GET_STATUS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_GET_STATUS_OC >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, sizeof( cbuffer ), 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_get_version( const void* context, + lr1121_modem_system_version_t* version ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_GET_VERSION_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_GET_VERSION_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_GET_VERSION_OC >> 0 ), + }; + uint8_t rbuffer[LR1121_MODEM_SYSTEM_VERSION_LENGTH] = { 0x00 }; + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_SYSTEM_GET_VERSION_CMD_LENGTH, rbuffer, LR1121_MODEM_SYSTEM_VERSION_LENGTH ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + version->hw = rbuffer[0]; + version->type = ( lr1121_modem_system_version_type_t ) rbuffer[1]; + version->fw = ( ( uint16_t ) rbuffer[2] << 8 ) + ( uint16_t ) rbuffer[3]; + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_system_get_errors( const void* context, lr1121_modem_system_errors_t* errors ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_GET_ERRORS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_GET_ERRORS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_GET_ERRORS_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( errors )] = { 0x00 }; + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_SYSTEM_GET_ERRORS_CMD_LENGTH, rbuffer, sizeof( *errors ) ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *errors = ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1]; + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_system_clear_errors( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_CLEAR_ERRORS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_CLEAR_ERRORS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_CLEAR_ERRORS_OC >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SYSTEM_CLEAR_ERRORS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_calibrate( const void* context, const uint8_t calib_param ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_CALIBRATE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_CALIBRATE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_CALIBRATE_OC >> 0 ), + calib_param, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SYSTEM_CALIBRATE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_set_reg_mode( const void* context, + const lr1121_modem_system_reg_mode_t reg_mode ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_SET_REGMODE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_REGMODE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_REGMODE_OC >> 0 ), + ( uint8_t ) reg_mode, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SYSTEM_SET_REGMODE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_calibrate_image( const void* context, const uint8_t freq1, + const uint8_t freq2 ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_CALIBRATE_IMAGE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_CALIBRATE_IMAGE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_CALIBRATE_IMAGE_OC >> 0 ), + freq1, + freq2, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SYSTEM_CALIBRATE_IMAGE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_calibrate_image_in_mhz( const void* context, + const uint16_t freq1_in_mhz, + const uint16_t freq2_in_mhz ) +{ + // Perform a floor() to get a value for freq1 corresponding to a frequency lower than or equal to freq1_in_mhz + const uint8_t freq1 = freq1_in_mhz / LR1121_MODEM_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ; + + // Perform a ceil() to get a value for freq2 corresponding to a frequency higher than or equal to freq2_in_mhz + const uint8_t freq2 = ( freq2_in_mhz + LR1121_MODEM_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ - 1 ) / + LR1121_MODEM_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ; + + return lr1121_modem_system_calibrate_image( context, freq1, freq2 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_set_dio_as_rf_switch( + const void* context, const lr1121_modem_system_rfswitch_cfg_t* rf_switch_cfg ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_SET_DIO_AS_RF_SWITCH_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_DIO_AS_RF_SWITCH_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_DIO_AS_RF_SWITCH_OC >> 0 ), + rf_switch_cfg->enable, + rf_switch_cfg->standby, + rf_switch_cfg->rx, + rf_switch_cfg->tx, + rf_switch_cfg->tx_hp, + rf_switch_cfg->tx_hf, + 0x00, + 0x00, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SYSTEM_SET_DIO_AS_RF_SWITCH_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_set_dio_irq_params( + const void* context, const lr1121_modem_system_irq_mask_t irqs_to_enable_dio1, + const lr1121_modem_system_irq_mask_t irqs_to_enable_dio2 ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_SET_DIO_IRQ_PARAMS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_DIOIRQPARAMS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_DIOIRQPARAMS_OC >> 0 ), + ( uint8_t )( irqs_to_enable_dio1 >> 24 ), + ( uint8_t )( irqs_to_enable_dio1 >> 16 ), + ( uint8_t )( irqs_to_enable_dio1 >> 8 ), + ( uint8_t )( irqs_to_enable_dio1 >> 0 ), + ( uint8_t )( irqs_to_enable_dio2 >> 24 ), + ( uint8_t )( irqs_to_enable_dio2 >> 16 ), + ( uint8_t )( irqs_to_enable_dio2 >> 8 ), + ( uint8_t )( irqs_to_enable_dio2 >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SYSTEM_SET_DIO_IRQ_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_clear_irq_status( const void* context, + const lr1121_modem_system_irq_mask_t irqs_to_clear ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_CLEAR_IRQ_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_CLEAR_IRQ_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_CLEAR_IRQ_OC >> 0 ), + ( uint8_t )( irqs_to_clear >> 24 ), + ( uint8_t )( irqs_to_clear >> 16 ), + ( uint8_t )( irqs_to_clear >> 8 ), + ( uint8_t )( irqs_to_clear >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SYSTEM_CLEAR_IRQ_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_get_and_clear_irq_status( const void* context, + lr1121_modem_system_irq_mask_t* irq ) +{ + lr1121_modem_system_irq_mask_t lr1121_modem_irq_mask = LR1121_MODEM_SYSTEM_IRQ_NONE; + + lr1121_modem_response_code_t status = lr1121_modem_system_get_irq_status( context, &lr1121_modem_irq_mask ); + + if( ( status == LR1121_MODEM_RESPONSE_CODE_OK ) && ( lr1121_modem_irq_mask != 0 ) ) + { + status = lr1121_modem_system_clear_irq_status( context, lr1121_modem_irq_mask ); + } + if( ( status == LR1121_MODEM_RESPONSE_CODE_OK ) && ( irq != NULL ) ) + { + *irq = lr1121_modem_irq_mask; + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_system_cfg_lfclk( const void* context, + const lr1121_modem_system_lfclk_cfg_t lfclock_cfg, + const bool wait_for_32k_ready ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_CFG_LFCLK_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_CFG_LFCLK_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_CFG_LFCLK_OC >> 0 ), + ( uint8_t )( lfclock_cfg | ( wait_for_32k_ready << 2 ) ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SYSTEM_CFG_LFCLK_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_set_tcxo_mode( const void* context, + const lr1121_modem_system_tcxo_supply_voltage_t tune, + const uint32_t timeout ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_SET_TCXO_MODE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_TCXO_MODE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_TCXO_MODE_OC >> 0 ), + ( uint8_t ) tune, + ( uint8_t )( timeout >> 16 ), + ( uint8_t )( timeout >> 8 ), + ( uint8_t )( timeout >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SYSTEM_SET_TCXO_MODE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_reboot( const void* context, const bool stay_in_bootloader ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_REBOOT_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_REBOOT_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_REBOOT_OC >> 0 ), + ( stay_in_bootloader == true ) ? 0x03 : 0x00, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SYSTEM_REBOOT_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_get_vbat( const void* context, uint8_t* vbat ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_GET_VBAT_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_GET_VBAT_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_GET_VBAT_OC >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_SYSTEM_GET_VBAT_CMD_LENGTH, vbat, sizeof( *vbat ) ); +} + +lr1121_modem_response_code_t lr1121_modem_system_get_temp( const void* context, uint16_t* temp ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_GET_TEMP_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_GET_TEMP_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_GET_TEMP_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( uint16_t )] = { 0x00 }; + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_SYSTEM_GET_TEMP_CMD_LENGTH, rbuffer, sizeof( uint16_t ) ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + *temp = ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1]; + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_system_set_sleep( const void* context, + const lr1121_modem_system_sleep_cfg_t sleep_cfg, + const uint32_t sleep_time ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_SET_SLEEP_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_SLEEP_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_SLEEP_OC >> 0 ), + 0x02u + sleep_cfg.is_warm_start, + ( uint8_t )( sleep_time >> 24 ), + ( uint8_t )( sleep_time >> 16 ), + ( uint8_t )( sleep_time >> 8 ), + ( uint8_t )( sleep_time >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SYSTEM_SET_SLEEP_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_set_standby( const void* context, + const lr1121_modem_system_standby_cfg_t standby_cfg ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_SET_STANDBY_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_STANDBY_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_STANDBY_OC >> 0 ), + ( uint8_t ) standby_cfg, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SYSTEM_SET_STANDBY_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_wakeup( const void* context ) +{ + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_wakeup( context ); +} + +lr1121_modem_response_code_t lr1121_modem_system_set_fs( const void* context ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_SET_FS_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_FS_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_SET_FS_OC >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( context, cbuffer, + LR1121_MODEM_SYSTEM_SET_FS_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_erase_infopage( const void* context, + const lr1121_modem_system_infopage_id_t infopage_id ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_ERASE_INFOPAGE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_ERASE_INFOPAGE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_ERASE_INFOPAGE_OC >> 0 ), + ( uint8_t ) infopage_id, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SYSTEM_ERASE_INFOPAGE_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_write_infopage( const void* context, + const lr1121_modem_system_infopage_id_t infopage_id, + const uint16_t address, const uint32_t* data, + const uint8_t length ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_WRITE_INFOPAGE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_WRITE_INFOPAGE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_WRITE_INFOPAGE_OC >> 0 ), + ( uint8_t ) infopage_id, + ( uint8_t )( address >> 8 ), + ( uint8_t )( address >> 0 ), + }; + uint8_t cdata[256]; + + for( uint16_t index = 0; index < length; index++ ) + { + uint8_t* cdata_local = &cdata[index * sizeof( uint32_t )]; + + cdata_local[0] = ( uint8_t )( data[index] >> 24 ); + cdata_local[1] = ( uint8_t )( data[index] >> 16 ); + cdata_local[2] = ( uint8_t )( data[index] >> 8 ); + cdata_local[3] = ( uint8_t )( data[index] >> 0 ); + } + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SYSTEM_WRITE_INFOPAGE_CMD_LENGTH, cdata, length * sizeof( uint32_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_system_read_infopage( const void* context, + const lr1121_modem_system_infopage_id_t infopage_id, + const uint16_t address, uint32_t* data, + const uint8_t length ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_READ_INFOPAGE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_READ_INFOPAGE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_READ_INFOPAGE_OC >> 0 ), + ( uint8_t ) infopage_id, + ( uint8_t )( address >> 8 ), + ( uint8_t )( address >> 0 ), + length, + }; + + const lr1121_modem_response_code_t status = ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_SYSTEM_READ_INFOPAGE_CMD_LENGTH, ( uint8_t* ) data, length * sizeof( *data ) ); + + if( status == LR1121_MODEM_RESPONSE_CODE_OK ) + { + for( uint8_t index = 0; index < length; index++ ) + { + uint8_t* buffer_local = ( uint8_t* ) &data[index]; + + data[index] = ( ( uint32_t ) buffer_local[0] << 24 ) + ( ( uint32_t ) buffer_local[1] << 16 ) + + ( ( uint32_t ) buffer_local[2] << 8 ) + ( ( uint32_t ) buffer_local[3] << 0 ); + } + } + + return status; +} + +lr1121_modem_response_code_t lr1121_modem_system_read_uid( const void* context, + lr1121_modem_system_uid_t unique_identifier ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_READ_UID_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_READ_UID_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_READ_UID_OC >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_SYSTEM_READ_UID_CMD_LENGTH, unique_identifier, LR1121_MODEM_SYSTEM_UID_LENGTH ); +} + +lr1121_modem_response_code_t lr1121_modem_system_read_join_eui( const void* context, + lr1121_modem_system_join_eui_t join_eui ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_READ_JOIN_EUI_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_READ_JOIN_EUI_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_READ_JOIN_EUI_OC >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_SYSTEM_READ_JOIN_EUI_CMD_LENGTH, join_eui, LR1121_MODEM_SYSTEM_JOIN_EUI_LENGTH ); +} + +lr1121_modem_response_code_t lr1121_modem_system_read_pin( const void* context, lr1121_modem_system_pin_t pin ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_READ_PIN_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_READ_PIN_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_READ_PIN_OC >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_SYSTEM_READ_PIN_CMD_LENGTH, pin, LR1121_MODEM_SYSTEM_PIN_LENGTH ); +} + +lr1121_modem_response_code_t lr1121_modem_system_read_pin_custom_eui( const void* context, + lr1121_modem_system_uid_t device_eui, + lr1121_modem_system_join_eui_t join_eui, + uint8_t rfu, lr1121_modem_system_pin_t pin ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_READ_PIN_CUSTOM_EUI_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_READ_PIN_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_READ_PIN_OC >> 0 ), + device_eui[0], + device_eui[1], + device_eui[2], + device_eui[3], + device_eui[4], + device_eui[5], + device_eui[6], + device_eui[7], + join_eui[0], + join_eui[1], + join_eui[2], + join_eui[3], + join_eui[4], + join_eui[5], + join_eui[6], + join_eui[7], + rfu, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_SYSTEM_READ_PIN_CUSTOM_EUI_CMD_LENGTH, pin, LR1121_MODEM_SYSTEM_PIN_LENGTH ); +} + +lr1121_modem_response_code_t lr1121_modem_system_get_random_number( const void* context, uint32_t* random_number ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_GET_RANDOM_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_GET_RANDOM_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_GET_RANDOM_OC >> 0 ), + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_read( + context, cbuffer, LR1121_MODEM_SYSTEM_GET_RANDOM_CMD_LENGTH, ( uint8_t* ) random_number, sizeof( uint32_t ) ); +} + +lr1121_modem_response_code_t lr1121_modem_system_enable_spi_crc( const void* context, bool enable_crc ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_ENABLE_SPI_CRC_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_ENABLE_SPI_CRC_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_ENABLE_SPI_CRC_OC >> 0 ), + ( enable_crc == true ) ? 0x01 : 0x00, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SYSTEM_ENABLE_SPI_CRC_CMD_LENGTH, 0, 0 ); +} + +lr1121_modem_response_code_t lr1121_modem_system_drive_dio_in_sleep_mode( const void* context, bool enable_drive ) +{ + const uint8_t cbuffer[LR1121_MODEM_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_CMD_LENGTH] = { + ( uint8_t )( LR1121_MODEM_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_OC >> 8 ), + ( uint8_t )( LR1121_MODEM_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_OC >> 0 ), + ( enable_drive == true ) ? 0x01 : 0x00, + }; + + return ( lr1121_modem_response_code_t ) lr1121_modem_hal_write( + context, cbuffer, LR1121_MODEM_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_CMD_LENGTH, 0, 0 ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static void lr1121_modem_system_convert_stat1_byte_to_enum( uint8_t stat1_byte, lr1121_modem_system_stat1_t* stat1 ) +{ + if( stat1 != NULL ) + { + stat1->is_interrupt_active = ( ( stat1_byte & 0x01 ) != 0 ) ? true : false; + stat1->command_status = ( lr1121_modem_system_command_status_t )( stat1_byte >> 1 ); + } +} + +static void lr1121_modem_system_convert_stat2_byte_to_enum( uint8_t stat2_byte, lr1121_modem_system_stat2_t* stat2 ) +{ + if( stat2 != NULL ) + { + stat2->is_running_from_flash = ( ( stat2_byte & 0x01 ) != 0 ) ? true : false; + stat2->chip_mode = ( lr1121_modem_system_chip_modes_t )( ( stat2_byte & 0x0F ) >> 1 ); + stat2->reset_status = ( lr1121_modem_system_reset_status_t )( ( stat2_byte & 0xF0 ) >> 4 ); + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr1121_modem_hal.c b/components/esp_lora_1121/src/lr1121_modem_hal.c new file mode 100755 index 0000000..3521c0e --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_modem_hal.c @@ -0,0 +1,344 @@ +/*! + * @file lr1121_modem_hal.c + * + * @brief Hardware Abstraction Layer (HAL) implementation for lr1121 + * + * The Clear BSD License + * Copyright Semtech Corporation 2024. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include +#include "esp_lora_1121.h" + +/*! + * @brief lr1121_modem_hal.h API implementation + */ + +/*! + * @brief Function to wait that the lr1121 modem-e busy line fall to low + * + * @param [in] context Chip implementation context + * @param [in] timeout_ms timeout in millisec before leave the function + * + * @returns lr1121_hal_status_t + */ +static lr1121_modem_hal_status_t lr1121_modem_hal_wait_on_busy(const void *context, uint32_t timeout_ms); + +/*! + * @brief Function to wait the that lr1121 modem-e busy line raise to high + * + * @param [in] context Chip implementation context + * @param [in] timeout_ms timeout in millisec before leave the function + * + * @returns lr1121_hal_status_t + */ +static lr1121_modem_hal_status_t lr1121_modem_hal_wait_on_unbusy(const void *context, uint32_t timeout_ms); + +lr1121_modem_hal_status_t lr1121_modem_hal_write(const void *context, const uint8_t *command, + const uint16_t command_length, const uint8_t *data, + const uint16_t data_length) +{ + + if (lr1121_modem_hal_wakeup( context ) == LR1121_MODEM_HAL_STATUS_OK) + { + uint8_t crc = 0; + uint8_t crc_received = 0; + lr1121_modem_hal_status_t status; + + /* NSS low */ + gpio_set_level(((lr1121_t *)context)->cs, 0); + /* Send CMD */ + lora_spi_write_bytes(context, (uint8_t *)command, command_length); + /* Send Data */ + if (data_length > 0) + { + lora_spi_write_bytes(context, (uint8_t *)data, data_length); + } + + /* Compute and send CRC */ + crc = lr1121_modem_compute_crc( 0xFF, command, command_length ); + if (data_length > 0) + { + crc = lr1121_modem_compute_crc( crc, data, data_length ); + } + /* Send CRC */ + lora_spi_write_bytes(context, (uint8_t *)&crc, 1); + + /* NSS high */ + gpio_set_level(((lr1121_t *)context)->cs, 1); + + /* Wait on busy pin up to 1000 ms */ + if( lr1121_modem_hal_wait_on_busy( context, 1000 ) != LR1121_MODEM_HAL_STATUS_OK ) + { + return LR1121_MODEM_HAL_STATUS_BUSY_TIMEOUT; + } + + /* Send dummy byte to retrieve RC & CRC */ + + /* NSS low */ + gpio_set_level(((lr1121_t *)context)->cs, 0); + + /* read RC */ + lora_spi_read_bytes(context, ( uint8_t* ) &status, 1); + lora_spi_read_bytes(context, ( uint8_t* ) &crc_received, 1); + + /* Compute response crc */ + crc = lr1121_modem_compute_crc( 0xFF, ( uint8_t* ) &status, 1 ); + + /* NSS high */ + gpio_set_level(((lr1121_t *)context)->cs, 1); + + if( crc != crc_received ) + { + /* change the response code */ + status = LR1121_MODEM_HAL_STATUS_BAD_FRAME; + } + + /* Wait on busy pin up to 1000 ms */ + if( lr1121_modem_hal_wait_on_unbusy( context, 1000 ) != LR1121_MODEM_HAL_STATUS_OK ) + { + return LR1121_MODEM_HAL_STATUS_BUSY_TIMEOUT; + } + + return status; + } + return LR1121_MODEM_HAL_STATUS_BUSY_TIMEOUT; +} + +lr1121_modem_hal_status_t lr1121_modem_hal_write_without_rc( const void* context, const uint8_t* command, + const uint16_t command_length, const uint8_t* data, + const uint16_t data_length ) +{ + if( lr1121_modem_hal_wakeup( context ) == LR1121_MODEM_HAL_STATUS_OK ) + { + uint8_t crc = 0; + lr1121_modem_hal_status_t status = LR1121_MODEM_HAL_STATUS_OK; + + /* NSS low */ + gpio_set_level(((lr1121_t *)context)->cs, 0); + /* Send CMD */ + lora_spi_write_bytes(context, (uint8_t *)command, command_length); + /* Send Data */ + if (data_length > 0) + { + lora_spi_write_bytes(context, (uint8_t *)data, data_length); + } + /* Compute and send CRC */ + crc = lr1121_modem_compute_crc( 0xFF, command, command_length ); + crc = lr1121_modem_compute_crc( crc, data, data_length ); + /* Send CRC */ + lora_spi_write_bytes(context, (uint8_t *)&crc, 1); + /* NSS high */ + gpio_set_level(((lr1121_t *)context)->cs, 1); + + return status; + } + return LR1121_MODEM_HAL_STATUS_BUSY_TIMEOUT; +} + +lr1121_modem_hal_status_t lr1121_modem_hal_read(const void *context, const uint8_t *command, + const uint16_t command_length, uint8_t *data, + const uint16_t data_length) +{ + if (lr1121_modem_hal_wakeup( context ) == LR1121_MODEM_HAL_STATUS_OK) + { + uint8_t crc = 0; + uint8_t crc_received = 0; + lr1121_modem_hal_status_t status = LR1121_MODEM_HAL_STATUS_ERROR; + + /* NSS low */ + gpio_set_level(((lr1121_t *)context)->cs, 0); + /* Send CMD */ + lora_spi_write_bytes(context, (uint8_t *)command, command_length); + + /* Compute and send CRC */ + crc = lr1121_modem_compute_crc( 0xFF, command, command_length ); + /* Send CRC */ + lora_spi_write_bytes(context, &crc, 1); + + /* NSS high */ + gpio_set_level(((lr1121_t *)context)->cs, 1); + + /* Wait on busy pin up to 1000 ms */ + if (lr1121_modem_hal_wait_on_busy(context, 1000) != LR1121_MODEM_HAL_STATUS_OK) + { + return LR1121_MODEM_HAL_STATUS_ERROR; + } + + /* NSS low */ + gpio_set_level(((lr1121_t *)context)->cs, 0); + + /* read RC */ + lora_spi_read_bytes(context, ( uint8_t* ) &status, 1); + // printf("status:%d\r\n",status); + if( status == LR1121_MODEM_HAL_STATUS_OK ) + { + // printf("data_length:%d\r\n",data_length); + lora_spi_read_bytes(context, data, data_length); + } + lora_spi_read_bytes( context, ( uint8_t* ) &crc_received, 1 ); + + /* NSS high */ + gpio_set_level(((lr1121_t *)context)->cs, 1); + + /* Compute response crc */ + crc = lr1121_modem_compute_crc( 0xFF, ( uint8_t* ) &status, 1 ); + if( status == LR1121_MODEM_HAL_STATUS_OK ) + { + crc = lr1121_modem_compute_crc( crc, data, data_length ); + } + + if( crc != crc_received ) + { + if( crc != crc_received ) + /* change the response code */ + status = LR1121_MODEM_HAL_STATUS_BAD_FRAME; + } + + /* Wait on busy pin up to 1000 ms */ + if( lr1121_modem_hal_wait_on_unbusy( context, 1000 ) != LR1121_MODEM_HAL_STATUS_OK ) + { + return LR1121_MODEM_HAL_STATUS_BUSY_TIMEOUT; + } + + return status; + } + return LR1121_MODEM_HAL_STATUS_BUSY_TIMEOUT; +} + +lr1121_modem_hal_status_t lr1121_modem_hal_direct_read(const void *context, uint8_t *data, + const uint16_t data_length) +{ + if (lr1121_modem_hal_wait_on_unbusy(context, 10000) == LR1121_MODEM_HAL_STATUS_OK) + { + /* NSS low */ + gpio_set_level(((lr1121_t *)context)->cs, 0); + + lora_spi_read_bytes(context, data, data_length); + + /* NSS high */ + gpio_set_level(((lr1121_t *)context)->cs, 1); + + return LR1121_MODEM_HAL_STATUS_OK; + } + return LR1121_MODEM_HAL_STATUS_ERROR; +} + +lr1121_modem_hal_status_t lr1121_modem_hal_reset(const void *context) +{ + + if (((lr1121_t *)context)->reset >= 0) + { + gpio_set_level(((lr1121_t *)context)->reset, 0); + vTaskDelay(1 / portTICK_PERIOD_MS); + gpio_set_level(((lr1121_t *)context)->reset, 1); + } + + return LR1121_MODEM_HAL_STATUS_OK; +} + +lr1121_modem_hal_status_t lr1121_modem_hal_wakeup(const void *context) +{ + if( lr1121_modem_hal_wait_on_busy( context, 10000 ) == LR1121_MODEM_HAL_STATUS_OK ) + { + /* Wakeup radio */ + gpio_set_level(((lr1121_t *)context)->cs, 0); + vTaskDelay(1 / portTICK_PERIOD_MS); + gpio_set_level(((lr1121_t *)context)->cs, 1); + vTaskDelay(1 / portTICK_PERIOD_MS); + } + else + { + return LR1121_MODEM_HAL_STATUS_BUSY_TIMEOUT; + } + + /* Wait on busy pin for 1000 ms */ + return lr1121_modem_hal_wait_on_unbusy( context, 1000 ); +} + + +static lr1121_modem_hal_status_t lr1121_modem_hal_wait_on_busy(const void *context, uint32_t timeout_ms) +{ +#if 0 + while( gpio_get_level( ( ( lr1121_t* ) context )->busy ) == 0 ) + { + ; + } +#else + if (((lr1121_t *)context)->busy < 0) + { + return LR1121_MODEM_HAL_STATUS_OK; + } + uint32_t start = esp_timer_get_time() / 1000; + uint32_t current = 0; + + while (gpio_get_level(((lr1121_t *)context)->busy) == 0) + { + current = esp_timer_get_time() / 1000; + if ((int32_t)(current - start) > (int32_t)timeout_ms) + { + return LR1121_MODEM_HAL_STATUS_ERROR; + } + } + +#endif + return LR1121_MODEM_HAL_STATUS_OK; +} + +static lr1121_modem_hal_status_t lr1121_modem_hal_wait_on_unbusy(const void *context, uint32_t timeout_ms) +{ +#if 0 + while( gpio_get_level( ( ( lr1121_t* ) context )->busy ) == 1 ) + { + ; + } +#else + if (((lr1121_t *)context)->busy < 0) + { + return LR1121_MODEM_HAL_STATUS_OK; + } + uint32_t start = esp_timer_get_time() / 1000; + uint32_t current = 0; + while (gpio_get_level(((lr1121_t *)context)->busy) == 1) + { + + current = esp_timer_get_time() / 1000; + if ((int32_t)(current - start) > (int32_t)timeout_ms) + { + return LR1121_MODEM_HAL_STATUS_ERROR; + } + } +#endif + return LR1121_MODEM_HAL_STATUS_OK; +} diff --git a/components/esp_lora_1121/src/lr1121_printers/lr1121_modem_printf_info.c b/components/esp_lora_1121/src/lr1121_printers/lr1121_modem_printf_info.c new file mode 100755 index 0000000..1853177 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_printers/lr1121_modem_printf_info.c @@ -0,0 +1,315 @@ +/*! + * @file lr1121_modem_printf_info.c + * + * @brief Common Application Helper function implementations + * + * @copyright + * @parblock + * The Clear BSD License + * Copyright Semtech Corporation 2024. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * @endparblock + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ +#include "lr1121_modem_printf_info.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC VARIABLES -------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +void print_hex_buffer(const uint8_t *buffer, uint8_t size) +{ + uint8_t newline = 0; + + for (uint8_t i = 0; i < size; i++) + { + if (newline != 0) + { + printf("\n\n"); + newline = 0; + } + + printf("%02X ", buffer[i]); + + if (((i + 1) % 16) == 0) + { + newline = 1; + } + } + printf("\n\n"); +} + +void print_version(lr1121_modem_version_t modem_version) +{ + printf("###### ===== lr1121 MODEM-E VERSION ==== ######\n\n\n"); + printf("USE CASE : %02X\n", modem_version.use_case); + printf("MODEM : %02X.%02X.%02X\n", modem_version.modem_major, modem_version.modem_minor, + modem_version.modem_patch); + printf("LBM : %02X.%02X.%02X\n\n", modem_version.lbm_major, modem_version.lbm_minor, + modem_version.lbm_patch); +} + +void print_lorawan_credentials(const uint8_t *dev_eui, const uint8_t *join_eui, const uint8_t *pin, + const bool use_internal_credentials) +{ + if (use_internal_credentials) + { + printf("---=== INTERNAL CREDENTIALS ===---\n\n"); + } + else + { + printf("---=== CUSTOM CREDENTIALS ===---\n\n"); + } + printf("DevEui : %02X", dev_eui[0]); + for (int i = 1; i < 8; i++) + { + printf("-%02X", dev_eui[i]); + } + printf("\n"); + printf("JoinEui : %02X", join_eui[0]); + for (int i = 1; i < 8; i++) + { + printf("-%02X", join_eui[i]); + } + printf("\n"); + printf("Pin : %02X", pin[0]); + for (int i = 1; i < 4; i++) + { + printf("-%02X", pin[i]); + } + printf("\n\n"); +} + +void modem_status_to_string(lr1121_modem_lorawan_status_t modem_status) +{ + printf("Modem status : "); + + if ((modem_status & LR1121_LORAWAN_CRASH) == LR1121_LORAWAN_CRASH) + { + printf("CRASH "); + } + if ((modem_status & LR1121_LORAWAN_JOINED) == LR1121_LORAWAN_JOINED) + { + printf("JOINED "); + } + if ((modem_status & LR1121_LORAWAN_SUSPEND) == LR1121_LORAWAN_SUSPEND) + { + printf("SUSPEND "); + } + if ((modem_status & LR1121_LORAWAN_JOINING) == LR1121_LORAWAN_JOINING) + { + printf("JOINING "); + } + + printf("\n\n\n"); +} + +void get_and_print_lorawan_region_from_modem(const void *context, lr1121_modem_regions_t *modem_region) +{ + // 1. Get the region from modem + lr1121_modem_regions_t local_region = LR1121_LORAWAN_REGION_EU868; + const lr1121_modem_response_code_t response_code = lr1121_modem_get_region(context, &local_region); + if (response_code == LR1121_MODEM_RESPONSE_CODE_OK) + { + // 2a. If the get from the modem is successful print it. + // And the output pointer is not null: return the region + if (modem_region != NULL) + { + *modem_region = local_region; + } + print_lorawan_region(*modem_region); + } + else + { + // 2b. If the get from modem failed: print an error message + printf("Error on lr1121_modem_get_region, get response code: %d\n", response_code); + } +} + +void print_lorawan_region(lr1121_modem_regions_t region) +{ + switch (region) + { + case LR1121_LORAWAN_REGION_EU868: + { + printf("REGION : EU868\n\n\n"); + break; + } + case LR1121_LORAWAN_REGION_US915: + { + printf("REGION : US915\n\n\n"); + break; + } + case LR1121_LORAWAN_REGION_AU915: + { + printf("REGION : AU915\n\n\n"); + break; + } + case LR1121_LORAWAN_REGION_AS923_GRP1: + { + printf("REGION : AS923_GRP1\n\n\n"); + break; + } + case LR1121_LORAWAN_REGION_CN470: + { + printf("REGION : CN470\n\n\n"); + break; + } + case LR1121_LORAWAN_REGION_AS923_GRP2: + { + printf("REGION : AS923_GRP2\n\n\n"); + break; + } + case LR1121_LORAWAN_REGION_AS923_GRP3: + { + printf("REGION : AS923_GRP3\n\n\n"); + break; + } + case LR1121_LORAWAN_REGION_AS923_GRP4: + { + printf("REGION : AS923_GRP4\n\n\n"); + break; + } + case LR1121_LORAWAN_REGION_IN865: + { + printf("REGION : IN865\n\n\n"); + break; + } + case LR1121_LORAWAN_REGION_KR920: + { + // printf( "LBT : ACTIVATE LBT\n\n" ); + printf("REGION : KR920\n\n\n"); + break; + } + case LR1121_LORAWAN_REGION_RU864: + { + printf("REGION : RU864\n\n\n"); + break; + } + default: + printf("No supported region selected\n\n\n"); + break; + } +} + +void print_certification(lr1121_modem_certification_mode_t certif_running) +{ + if (certif_running == LR1121_MODEM_CERTIFICATION_MODE_ENABLE) + { + printf("###### ===================================== ######\n"); + printf("###### ===== CERTIFICATION MODE ENABLED ==== ######\n"); + printf("###### ===================================== ######\n\n\n"); + } + else + { + printf("###### ====================================== ######\n"); + printf("###### ===== CERTIFICATION MODE DISABLED ==== ######\n"); + printf("###### ====================================== ######\n\n\n"); + } +} + +void get_and_print_crashlog(const void *context) +{ + lr1121_modem_response_code_t response_code = LR1121_MODEM_RESPONSE_CODE_OK; + lr1121_modem_lorawan_status_t modem_status; + + response_code = lr1121_modem_get_status(context, (lr1121_modem_lorawan_status_bitmask_t*)&modem_status); + // Check if the crashlog bit is set in modem_status + if (response_code == LR1121_MODEM_RESPONSE_CODE_OK) + { + if ((modem_status & LR1121_LORAWAN_CRASH) == LR1121_LORAWAN_CRASH) + { + lr1121_modem_crashlog_status_t status_crashlog = LR1121_NO_NEW_CRASHLOG; + uint8_t crashlog[242] = {0}; + // Get the crashlog + response_code = lr1121_modem_get_crashlog(context, &status_crashlog, crashlog); + if ((response_code == LR1121_MODEM_RESPONSE_CODE_OK) && (status_crashlog == LR1121_NEW_CRASHLOG)) + { + printf("###### ===================================== ######\n"); + printf("###### =========== MODEM CRASHED =========== ######\n"); + printf("###### ===================================== ######\n\n"); + // HAL_DBG_TRACE_ARRAY( "Crashlog: ", crashlog, 242 ); + printf("%s - (%u bytes):\n", "Crashlog: ", 242); + for (uint32_t i = 0; i < 242; i++) + { + if (((i % 16) == 0) && (i > 0)) + { + printf("\n"); + } + printf(" %02X", crashlog[i]); + } + printf("\n"); + printf("\n\n"); + } + } + } + else + { + printf("Error on lr1121_modem_get_status, get response code: %d\n", response_code); + } +} +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr1121_printers/lr11xx_bootloader_types_str.c b/components/esp_lora_1121/src/lr1121_printers/lr11xx_bootloader_types_str.c new file mode 100755 index 0000000..38d6286 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_printers/lr11xx_bootloader_types_str.c @@ -0,0 +1,159 @@ +/*! + * @file lr11xx_bootloader_types_str.c + * + * @brief Printer helper functions for LR11xx bootloader types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lr11xx_bootloader_types_str.h" + +const char* lr11xx_bootloader_chip_modes_to_str( const lr11xx_bootloader_chip_modes_t value ) +{ + switch( value ) + { + case LR11XX_BOOTLOADER_CHIP_MODE_SLEEP: + { + return ( const char* ) "LR11XX_BOOTLOADER_CHIP_MODE_SLEEP"; + } + + case LR11XX_BOOTLOADER_CHIP_MODE_STBY_RC: + { + return ( const char* ) "LR11XX_BOOTLOADER_CHIP_MODE_STBY_RC"; + } + + case LR11XX_BOOTLOADER_CHIP_MODE_STBY_XOSC: + { + return ( const char* ) "LR11XX_BOOTLOADER_CHIP_MODE_STBY_XOSC"; + } + + case LR11XX_BOOTLOADER_CHIP_MODE_FS: + { + return ( const char* ) "LR11XX_BOOTLOADER_CHIP_MODE_FS"; + } + + case LR11XX_BOOTLOADER_CHIP_MODE_RX: + { + return ( const char* ) "LR11XX_BOOTLOADER_CHIP_MODE_RX"; + } + + case LR11XX_BOOTLOADER_CHIP_MODE_TX: + { + return ( const char* ) "LR11XX_BOOTLOADER_CHIP_MODE_TX"; + } + + case LR11XX_BOOTLOADER_CHIP_MODE_LOC: + { + return ( const char* ) "LR11XX_BOOTLOADER_CHIP_MODE_LOC"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_bootloader_reset_status_to_str( const lr11xx_bootloader_reset_status_t value ) +{ + switch( value ) + { + case LR11XX_BOOTLOADER_RESET_STATUS_CLEARED: + { + return ( const char* ) "LR11XX_BOOTLOADER_RESET_STATUS_CLEARED"; + } + + case LR11XX_BOOTLOADER_RESET_STATUS_ANALOG: + { + return ( const char* ) "LR11XX_BOOTLOADER_RESET_STATUS_ANALOG"; + } + + case LR11XX_BOOTLOADER_RESET_STATUS_EXTERNAL: + { + return ( const char* ) "LR11XX_BOOTLOADER_RESET_STATUS_EXTERNAL"; + } + + case LR11XX_BOOTLOADER_RESET_STATUS_SYSTEM: + { + return ( const char* ) "LR11XX_BOOTLOADER_RESET_STATUS_SYSTEM"; + } + + case LR11XX_BOOTLOADER_RESET_STATUS_WATCHDOG: + { + return ( const char* ) "LR11XX_BOOTLOADER_RESET_STATUS_WATCHDOG"; + } + + case LR11XX_BOOTLOADER_RESET_STATUS_IOCD_RESTART: + { + return ( const char* ) "LR11XX_BOOTLOADER_RESET_STATUS_IOCD_RESTART"; + } + + case LR11XX_BOOTLOADER_RESET_STATUS_RTC_RESTART: + { + return ( const char* ) "LR11XX_BOOTLOADER_RESET_STATUS_RTC_RESTART"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_bootloader_command_status_to_str( const lr11xx_bootloader_command_status_t value ) +{ + switch( value ) + { + case LR11XX_BOOTLOADER_CMD_STATUS_FAIL: + { + return ( const char* ) "LR11XX_BOOTLOADER_CMD_STATUS_FAIL"; + } + + case LR11XX_BOOTLOADER_CMD_STATUS_PERR: + { + return ( const char* ) "LR11XX_BOOTLOADER_CMD_STATUS_PERR"; + } + + case LR11XX_BOOTLOADER_CMD_STATUS_OK: + { + return ( const char* ) "LR11XX_BOOTLOADER_CMD_STATUS_OK"; + } + + case LR11XX_BOOTLOADER_CMD_STATUS_DATA: + { + return ( const char* ) "LR11XX_BOOTLOADER_CMD_STATUS_DATA"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} diff --git a/components/esp_lora_1121/src/lr1121_printers/lr11xx_crypto_engine_types_str.c b/components/esp_lora_1121/src/lr1121_printers/lr11xx_crypto_engine_types_str.c new file mode 100755 index 0000000..9bfe9c6 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_printers/lr11xx_crypto_engine_types_str.c @@ -0,0 +1,260 @@ +/*! + * @file lr11xx_crypto_engine_types_str.c + * + * @brief Printer helper functions for LR11xx crypto engine types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lr11xx_crypto_engine_types_str.h" + +const char* lr11xx_crypto_element_to_str( const lr11xx_crypto_element_t value ) +{ + switch( value ) + { + case LR11XX_CRYPTO_ELEMENT_CRYPTO_ENGINE: + { + return ( const char* ) "LR11XX_CRYPTO_ELEMENT_CRYPTO_ENGINE"; + } + + case LR11XX_CRYPTO_ELEMENT_SECURE_ELEMENT: + { + return ( const char* ) "LR11XX_CRYPTO_ELEMENT_SECURE_ELEMENT"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_crypto_status_to_str( const lr11xx_crypto_status_t value ) +{ + switch( value ) + { + case LR11XX_CRYPTO_STATUS_SUCCESS: + { + return ( const char* ) "LR11XX_CRYPTO_STATUS_SUCCESS"; + } + + case LR11XX_CRYPTO_STATUS_ERROR_FAIL_CMAC: + { + return ( const char* ) "LR11XX_CRYPTO_STATUS_ERROR_FAIL_CMAC"; + } + + case LR11XX_CRYPTO_STATUS_ERROR_INVALID_KEY_ID: + { + return ( const char* ) "LR11XX_CRYPTO_STATUS_ERROR_INVALID_KEY_ID"; + } + + case LR11XX_CRYPTO_STATUS_ERROR_BUFFER_SIZE: + { + return ( const char* ) "LR11XX_CRYPTO_STATUS_ERROR_BUFFER_SIZE"; + } + + case LR11XX_CRYPTO_STATUS_ERROR: + { + return ( const char* ) "LR11XX_CRYPTO_STATUS_ERROR"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_crypto_lorawan_version_to_str( const lr11xx_crypto_lorawan_version_t value ) +{ + switch( value ) + { + case LR11XX_CRYPTO_LORAWAN_VERSION_1_0_X: + { + return ( const char* ) "LR11XX_CRYPTO_LORAWAN_VERSION_1_0_X"; + } + + case LR11XX_CRYPTO_LORAWAN_VERSION_1_1_X: + { + return ( const char* ) "LR11XX_CRYPTO_LORAWAN_VERSION_1_1_X"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_crypto_keys_idx_to_str( const lr11xx_crypto_keys_idx_t value ) +{ + switch( value ) + { + case LR11XX_CRYPTO_KEYS_IDX_MOTHER_KEY: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_MOTHER_KEY"; + } + + case LR11XX_CRYPTO_KEYS_IDX_NWK_KEY: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_NWK_KEY"; + } + + case LR11XX_CRYPTO_KEYS_IDX_APP_KEY: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_APP_KEY"; + } + + case LR11XX_CRYPTO_KEYS_IDX_J_S_ENC_KEY: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_J_S_ENC_KEY"; + } + + case LR11XX_CRYPTO_KEYS_IDX_J_S_INT_KEY: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_J_S_INT_KEY"; + } + + case LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_0: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_0"; + } + + case LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_1: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_1"; + } + + case LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_2: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_2"; + } + + case LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_3: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_3"; + } + + case LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_4: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_4"; + } + + case LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_5: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_GP_KE_KEY_5"; + } + + case LR11XX_CRYPTO_KEYS_IDX_APP_S_KEY: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_APP_S_KEY"; + } + + case LR11XX_CRYPTO_KEYS_IDX_F_NWK_S_INT_KEY: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_F_NWK_S_INT_KEY"; + } + + case LR11XX_CRYPTO_KEYS_IDX_S_NWK_S_INT_KEY: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_S_NWK_S_INT_KEY"; + } + + case LR11XX_CRYPTO_KEYS_IDX_NWK_S_ENC_KEY: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_NWK_S_ENC_KEY"; + } + + case LR11XX_CRYPTO_KEYS_IDX_RFU_0: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_RFU_0"; + } + + case LR11XX_CRYPTO_KEYS_IDX_RFU_1: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_RFU_1"; + } + + case LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_0: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_0"; + } + + case LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_1: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_1"; + } + + case LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_2: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_2"; + } + + case LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_3: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_MC_APP_S_KEY_3"; + } + + case LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_0: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_0"; + } + + case LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_1: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_1"; + } + + case LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_2: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_2"; + } + + case LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_3: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_3"; + } + + case LR11XX_CRYPTO_KEYS_IDX_GP0: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_GP0"; + } + + case LR11XX_CRYPTO_KEYS_IDX_GP1: + { + return ( const char* ) "LR11XX_CRYPTO_KEYS_IDX_GP1"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} diff --git a/components/esp_lora_1121/src/lr1121_printers/lr11xx_lr_fhss_types_str.c b/components/esp_lora_1121/src/lr1121_printers/lr11xx_lr_fhss_types_str.c new file mode 100755 index 0000000..435bb47 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_printers/lr11xx_lr_fhss_types_str.c @@ -0,0 +1,165 @@ +/*! + * @file lr11xx_lr_fhss_types_str.c + * + * @brief Printer helper functions for LR11xx LRFHSS types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lr11xx_lr_fhss_types_str.h" + +const char* lr_fhss_v1_modulation_type_to_str( const lr_fhss_v1_modulation_type_t value ) +{ + switch( value ) + { + case LR_FHSS_V1_MODULATION_TYPE_GMSK_488: + { + return ( const char* ) "LR_FHSS_V1_MODULATION_TYPE_GMSK_488"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr_fhss_v1_cr_to_str( const lr_fhss_v1_cr_t value ) +{ + switch( value ) + { + case LR_FHSS_V1_CR_5_6: + { + return ( const char* ) "LR_FHSS_V1_CR_5_6"; + } + + case LR_FHSS_V1_CR_2_3: + { + return ( const char* ) "LR_FHSS_V1_CR_2_3"; + } + + case LR_FHSS_V1_CR_1_2: + { + return ( const char* ) "LR_FHSS_V1_CR_1_2"; + } + + case LR_FHSS_V1_CR_1_3: + { + return ( const char* ) "LR_FHSS_V1_CR_1_3"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr_fhss_v1_grid_to_str( const lr_fhss_v1_grid_t value ) +{ + switch( value ) + { + case LR_FHSS_V1_GRID_25391_HZ: + { + return ( const char* ) "LR_FHSS_V1_GRID_25391_HZ"; + } + + case LR_FHSS_V1_GRID_3906_HZ: + { + return ( const char* ) "LR_FHSS_V1_GRID_3906_HZ"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr_fhss_v1_bw_to_str( const lr_fhss_v1_bw_t value ) +{ + switch( value ) + { + case LR_FHSS_V1_BW_39063_HZ: + { + return ( const char* ) "LR_FHSS_V1_BW_39063_HZ"; + } + + case LR_FHSS_V1_BW_85938_HZ: + { + return ( const char* ) "LR_FHSS_V1_BW_85938_HZ"; + } + + case LR_FHSS_V1_BW_136719_HZ: + { + return ( const char* ) "LR_FHSS_V1_BW_136719_HZ"; + } + + case LR_FHSS_V1_BW_183594_HZ: + { + return ( const char* ) "LR_FHSS_V1_BW_183594_HZ"; + } + + case LR_FHSS_V1_BW_335938_HZ: + { + return ( const char* ) "LR_FHSS_V1_BW_335938_HZ"; + } + + case LR_FHSS_V1_BW_386719_HZ: + { + return ( const char* ) "LR_FHSS_V1_BW_386719_HZ"; + } + + case LR_FHSS_V1_BW_722656_HZ: + { + return ( const char* ) "LR_FHSS_V1_BW_722656_HZ"; + } + + case LR_FHSS_V1_BW_773438_HZ: + { + return ( const char* ) "LR_FHSS_V1_BW_773438_HZ"; + } + + case LR_FHSS_V1_BW_1523438_HZ: + { + return ( const char* ) "LR_FHSS_V1_BW_1523438_HZ"; + } + + case LR_FHSS_V1_BW_1574219_HZ: + { + return ( const char* ) "LR_FHSS_V1_BW_1574219_HZ"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} diff --git a/components/esp_lora_1121/src/lr1121_printers/lr11xx_printf_info.c b/components/esp_lora_1121/src/lr1121_printers/lr11xx_printf_info.c new file mode 100755 index 0000000..01ccc5a --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_printers/lr11xx_printf_info.c @@ -0,0 +1,40 @@ +#include "lr11xx_printf_info.h" +#include "lr1121_common/lr1121_common.h" +#include "stdio.h" + +// Print the version information of LR1121 +void lora_print_version( const void* context ) +{ + lr11xx_system_version_t ver; // Variable to store version information + if(lr11xx_system_get_version(context, &ver) == 0) // Check if version information is successfully retrieved + { + if(ver.type == LR11XX_SYSTEM_VERSION_TYPE_LR1121) // Check if the device is LR1121 + { + printf( "LR1121 information:\n" ); // Print header for LR1121 information + printf( " - Firmware = 0x%04X\n",ver.fw ); // Print firmware version in hexadecimal format + printf( " - Hardware = 0x%02X\n", ver.hw ); // Print hardware version in hexadecimal format + } + else + printf("NO LR1121\r\n"); // Print message if the device is not LR1121 + } +} + +// Print the temperature information of LR1121 +void lora__print_temp( const void* context ) +{ + uint16_t temp; // Variable to store raw temperature value + float t; // Variable to store calculated temperature in Celsius + lr11xx_system_get_temp(context,&temp); // Retrieve raw temperature value from the device + t = ((((float)(temp&0x07ff)/2047) * 1.35)-0.7295)*(1000/(-1.7)) + 25; // Calculate temperature in Celsius based on the raw value + printf( " LR1121 Temp = %0.2f ℃\n", t); // Print temperature with two decimal places +} + +// Print the battery voltage information of LR1121 +void lora_print_vbat( const void* context ) +{ + uint8_t vbat; // Variable to store raw battery voltage value + float v; // Variable to store calculated battery voltage in volts + lr11xx_system_get_vbat(context,&vbat); // Retrieve raw battery voltage value from the device + v = (((float)(5*vbat)/255)-1)*1.35; // Calculate battery voltage in volts based on the raw value + printf( " LR1121 Vbat = %0.2fV\n", v); // Print battery voltage with two decimal places +} diff --git a/components/esp_lora_1121/src/lr1121_printers/lr11xx_radio_types_str.c b/components/esp_lora_1121/src/lr1121_printers/lr11xx_radio_types_str.c new file mode 100755 index 0000000..159f558 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_printers/lr11xx_radio_types_str.c @@ -0,0 +1,926 @@ +/*! + * @file lr11xx_radio_types_str.c + * + * @brief Printer helper functions for LR11xx radio types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lr11xx_radio_types_str.h" + +const char* lr11xx_radio_pa_selection_to_str( const lr11xx_radio_pa_selection_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_PA_SEL_LP: + { + return ( const char* ) "LR11XX_RADIO_PA_SEL_LP"; + } + + case LR11XX_RADIO_PA_SEL_HP: + { + return ( const char* ) "LR11XX_RADIO_PA_SEL_HP"; + } + + case LR11XX_RADIO_PA_SEL_HF: + { + return ( const char* ) "LR11XX_RADIO_PA_SEL_HF"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_gfsk_address_filtering_to_str( const lr11xx_radio_gfsk_address_filtering_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_GFSK_ADDRESS_FILTERING_DISABLE: + { + return ( const char* ) "LR11XX_RADIO_GFSK_ADDRESS_FILTERING_DISABLE"; + } + + case LR11XX_RADIO_GFSK_ADDRESS_FILTERING_NODE_ADDRESS: + { + return ( const char* ) "LR11XX_RADIO_GFSK_ADDRESS_FILTERING_NODE_ADDRESS"; + } + + case LR11XX_RADIO_GFSK_ADDRESS_FILTERING_NODE_AND_BROADCAST_ADDRESSES: + { + return ( const char* ) "LR11XX_RADIO_GFSK_ADDRESS_FILTERING_NODE_AND_BROADCAST_ADDRESSES"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_fallback_modes_to_str( const lr11xx_radio_fallback_modes_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_FALLBACK_STDBY_RC: + { + return ( const char* ) "LR11XX_RADIO_FALLBACK_STDBY_RC"; + } + + case LR11XX_RADIO_FALLBACK_STDBY_XOSC: + { + return ( const char* ) "LR11XX_RADIO_FALLBACK_STDBY_XOSC"; + } + + case LR11XX_RADIO_FALLBACK_FS: + { + return ( const char* ) "LR11XX_RADIO_FALLBACK_FS"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_ramp_time_to_str( const lr11xx_radio_ramp_time_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_RAMP_16_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_16_US"; + } + + case LR11XX_RADIO_RAMP_32_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_32_US"; + } + + case LR11XX_RADIO_RAMP_48_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_48_US"; + } + + case LR11XX_RADIO_RAMP_64_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_64_US"; + } + + case LR11XX_RADIO_RAMP_80_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_80_US"; + } + + case LR11XX_RADIO_RAMP_96_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_96_US"; + } + + case LR11XX_RADIO_RAMP_112_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_112_US"; + } + + case LR11XX_RADIO_RAMP_128_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_128_US"; + } + + case LR11XX_RADIO_RAMP_144_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_144_US"; + } + + case LR11XX_RADIO_RAMP_160_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_160_US"; + } + + case LR11XX_RADIO_RAMP_176_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_176_US"; + } + + case LR11XX_RADIO_RAMP_192_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_192_US"; + } + + case LR11XX_RADIO_RAMP_208_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_208_US"; + } + + case LR11XX_RADIO_RAMP_240_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_240_US"; + } + + case LR11XX_RADIO_RAMP_272_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_272_US"; + } + + case LR11XX_RADIO_RAMP_304_US: + { + return ( const char* ) "LR11XX_RADIO_RAMP_304_US"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_lora_network_type_to_str( const lr11xx_radio_lora_network_type_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_LORA_NETWORK_PRIVATE: + { + return ( const char* ) "LR11XX_RADIO_LORA_NETWORK_PRIVATE"; + } + + case LR11XX_RADIO_LORA_NETWORK_PUBLIC: + { + return ( const char* ) "LR11XX_RADIO_LORA_NETWORK_PUBLIC"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_lora_sf_to_str( const lr11xx_radio_lora_sf_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_LORA_SF5: + { + return ( const char* ) "LR11XX_RADIO_LORA_SF5"; + } + + case LR11XX_RADIO_LORA_SF6: + { + return ( const char* ) "LR11XX_RADIO_LORA_SF6"; + } + + case LR11XX_RADIO_LORA_SF7: + { + return ( const char* ) "LR11XX_RADIO_LORA_SF7"; + } + + case LR11XX_RADIO_LORA_SF8: + { + return ( const char* ) "LR11XX_RADIO_LORA_SF8"; + } + + case LR11XX_RADIO_LORA_SF9: + { + return ( const char* ) "LR11XX_RADIO_LORA_SF9"; + } + + case LR11XX_RADIO_LORA_SF10: + { + return ( const char* ) "LR11XX_RADIO_LORA_SF10"; + } + + case LR11XX_RADIO_LORA_SF11: + { + return ( const char* ) "LR11XX_RADIO_LORA_SF11"; + } + + case LR11XX_RADIO_LORA_SF12: + { + return ( const char* ) "LR11XX_RADIO_LORA_SF12"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_lora_bw_to_str( const lr11xx_radio_lora_bw_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_LORA_BW_10: + { + return ( const char* ) "LR11XX_RADIO_LORA_BW_10"; + } + + case LR11XX_RADIO_LORA_BW_15: + { + return ( const char* ) "LR11XX_RADIO_LORA_BW_15"; + } + + case LR11XX_RADIO_LORA_BW_20: + { + return ( const char* ) "LR11XX_RADIO_LORA_BW_20"; + } + + case LR11XX_RADIO_LORA_BW_31: + { + return ( const char* ) "LR11XX_RADIO_LORA_BW_31"; + } + + case LR11XX_RADIO_LORA_BW_41: + { + return ( const char* ) "LR11XX_RADIO_LORA_BW_41"; + } + + case LR11XX_RADIO_LORA_BW_62: + { + return ( const char* ) "LR11XX_RADIO_LORA_BW_62"; + } + + case LR11XX_RADIO_LORA_BW_125: + { + return ( const char* ) "LR11XX_RADIO_LORA_BW_125"; + } + + case LR11XX_RADIO_LORA_BW_250: + { + return ( const char* ) "LR11XX_RADIO_LORA_BW_250"; + } + + case LR11XX_RADIO_LORA_BW_500: + { + return ( const char* ) "LR11XX_RADIO_LORA_BW_500"; + } + + case LR11XX_RADIO_LORA_BW_200: + { + return ( const char* ) "LR11XX_RADIO_LORA_BW_200"; + } + + case LR11XX_RADIO_LORA_BW_400: + { + return ( const char* ) "LR11XX_RADIO_LORA_BW_400"; + } + + case LR11XX_RADIO_LORA_BW_800: + { + return ( const char* ) "LR11XX_RADIO_LORA_BW_800"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_lora_cr_to_str( const lr11xx_radio_lora_cr_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_LORA_NO_CR: + { + return ( const char* ) "LR11XX_RADIO_LORA_NO_CR"; + } + + case LR11XX_RADIO_LORA_CR_4_5: + { + return ( const char* ) "LR11XX_RADIO_LORA_CR_4_5"; + } + + case LR11XX_RADIO_LORA_CR_4_6: + { + return ( const char* ) "LR11XX_RADIO_LORA_CR_4_6"; + } + + case LR11XX_RADIO_LORA_CR_4_7: + { + return ( const char* ) "LR11XX_RADIO_LORA_CR_4_7"; + } + + case LR11XX_RADIO_LORA_CR_4_8: + { + return ( const char* ) "LR11XX_RADIO_LORA_CR_4_8"; + } + + case LR11XX_RADIO_LORA_CR_LI_4_5: + { + return ( const char* ) "LR11XX_RADIO_LORA_CR_LI_4_5"; + } + + case LR11XX_RADIO_LORA_CR_LI_4_6: + { + return ( const char* ) "LR11XX_RADIO_LORA_CR_LI_4_6"; + } + + case LR11XX_RADIO_LORA_CR_LI_4_8: + { + return ( const char* ) "LR11XX_RADIO_LORA_CR_LI_4_8"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_intermediary_mode_to_str( const lr11xx_radio_intermediary_mode_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_MODE_SLEEP: + { + return ( const char* ) "LR11XX_RADIO_MODE_SLEEP"; + } + + case LR11XX_RADIO_MODE_STANDBY_RC: + { + return ( const char* ) "LR11XX_RADIO_MODE_STANDBY_RC"; + } + + case LR11XX_RADIO_MODE_STANDBY_XOSC: + { + return ( const char* ) "LR11XX_RADIO_MODE_STANDBY_XOSC"; + } + + case LR11XX_RADIO_MODE_FS: + { + return ( const char* ) "LR11XX_RADIO_MODE_FS"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_gfsk_crc_type_to_str( const lr11xx_radio_gfsk_crc_type_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_GFSK_CRC_OFF: + { + return ( const char* ) "LR11XX_RADIO_GFSK_CRC_OFF"; + } + + case LR11XX_RADIO_GFSK_CRC_1_BYTE: + { + return ( const char* ) "LR11XX_RADIO_GFSK_CRC_1_BYTE"; + } + + case LR11XX_RADIO_GFSK_CRC_2_BYTES: + { + return ( const char* ) "LR11XX_RADIO_GFSK_CRC_2_BYTES"; + } + + case LR11XX_RADIO_GFSK_CRC_1_BYTE_INV: + { + return ( const char* ) "LR11XX_RADIO_GFSK_CRC_1_BYTE_INV"; + } + + case LR11XX_RADIO_GFSK_CRC_2_BYTES_INV: + { + return ( const char* ) "LR11XX_RADIO_GFSK_CRC_2_BYTES_INV"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_gfsk_dc_free_to_str( const lr11xx_radio_gfsk_dc_free_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_GFSK_DC_FREE_OFF: + { + return ( const char* ) "LR11XX_RADIO_GFSK_DC_FREE_OFF"; + } + + case LR11XX_RADIO_GFSK_DC_FREE_WHITENING: + { + return ( const char* ) "LR11XX_RADIO_GFSK_DC_FREE_WHITENING"; + } + + case LR11XX_RADIO_GFSK_DC_FREE_WHITENING_SX128X_COMP: + { + return ( const char* ) "LR11XX_RADIO_GFSK_DC_FREE_WHITENING_SX128X_COMP"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_gfsk_pkt_len_modes_to_str( const lr11xx_radio_gfsk_pkt_len_modes_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_GFSK_PKT_FIX_LEN: + { + return ( const char* ) "LR11XX_RADIO_GFSK_PKT_FIX_LEN"; + } + + case LR11XX_RADIO_GFSK_PKT_VAR_LEN: + { + return ( const char* ) "LR11XX_RADIO_GFSK_PKT_VAR_LEN"; + } + + case LR11XX_RADIO_GFSK_PKT_VAR_LEN_SX128X_COMP: + { + return ( const char* ) "LR11XX_RADIO_GFSK_PKT_VAR_LEN_SX128X_COMP"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_gfsk_preamble_detector_to_str( const lr11xx_radio_gfsk_preamble_detector_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_OFF: + { + return ( const char* ) "LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_OFF"; + } + + case LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_8BITS: + { + return ( const char* ) "LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_8BITS"; + } + + case LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_16BITS: + { + return ( const char* ) "LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_16BITS"; + } + + case LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_24BITS: + { + return ( const char* ) "LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_24BITS"; + } + + case LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_32BITS: + { + return ( const char* ) "LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_32BITS"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_lora_crc_to_str( const lr11xx_radio_lora_crc_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_LORA_CRC_OFF: + { + return ( const char* ) "LR11XX_RADIO_LORA_CRC_OFF"; + } + + case LR11XX_RADIO_LORA_CRC_ON: + { + return ( const char* ) "LR11XX_RADIO_LORA_CRC_ON"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_lora_pkt_len_modes_to_str( const lr11xx_radio_lora_pkt_len_modes_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_LORA_PKT_EXPLICIT: + { + return ( const char* ) "LR11XX_RADIO_LORA_PKT_EXPLICIT"; + } + + case LR11XX_RADIO_LORA_PKT_IMPLICIT: + { + return ( const char* ) "LR11XX_RADIO_LORA_PKT_IMPLICIT"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_lora_iq_to_str( const lr11xx_radio_lora_iq_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_LORA_IQ_STANDARD: + { + return ( const char* ) "LR11XX_RADIO_LORA_IQ_STANDARD"; + } + + case LR11XX_RADIO_LORA_IQ_INVERTED: + { + return ( const char* ) "LR11XX_RADIO_LORA_IQ_INVERTED"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_pkt_type_to_str( const lr11xx_radio_pkt_type_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_PKT_NONE: + { + return ( const char* ) "LR11XX_RADIO_PKT_NONE"; + } + + case LR11XX_RADIO_PKT_TYPE_GFSK: + { + return ( const char* ) "LR11XX_RADIO_PKT_TYPE_GFSK"; + } + + case LR11XX_RADIO_PKT_TYPE_LORA: + { + return ( const char* ) "LR11XX_RADIO_PKT_TYPE_LORA"; + } + + case LR11XX_RADIO_PKT_TYPE_BPSK: + { + return ( const char* ) "LR11XX_RADIO_PKT_TYPE_BPSK"; + } + + case LR11XX_RADIO_PKT_TYPE_LR_FHSS: + { + return ( const char* ) "LR11XX_RADIO_PKT_TYPE_LR_FHSS"; + } + + case LR11XX_RADIO_PKT_TYPE_RTTOF: + { + return ( const char* ) "LR11XX_RADIO_PKT_TYPE_RTTOF"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_pa_reg_supply_to_str( const lr11xx_radio_pa_reg_supply_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_PA_REG_SUPPLY_VREG: + { + return ( const char* ) "LR11XX_RADIO_PA_REG_SUPPLY_VREG"; + } + + case LR11XX_RADIO_PA_REG_SUPPLY_VBAT: + { + return ( const char* ) "LR11XX_RADIO_PA_REG_SUPPLY_VBAT"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_rx_duty_cycle_mode_to_str( const lr11xx_radio_rx_duty_cycle_mode_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_RX_DUTY_CYCLE_MODE_RX: + { + return ( const char* ) "LR11XX_RADIO_RX_DUTY_CYCLE_MODE_RX"; + } + + case LR11XX_RADIO_RX_DUTY_CYCLE_MODE_CAD: + { + return ( const char* ) "LR11XX_RADIO_RX_DUTY_CYCLE_MODE_CAD"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_gfsk_bw_to_str( const lr11xx_radio_gfsk_bw_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_GFSK_BW_4800: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_4800"; + } + + case LR11XX_RADIO_GFSK_BW_5800: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_5800"; + } + + case LR11XX_RADIO_GFSK_BW_7300: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_7300"; + } + + case LR11XX_RADIO_GFSK_BW_9700: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_9700"; + } + + case LR11XX_RADIO_GFSK_BW_11700: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_11700"; + } + + case LR11XX_RADIO_GFSK_BW_14600: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_14600"; + } + + case LR11XX_RADIO_GFSK_BW_19500: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_19500"; + } + + case LR11XX_RADIO_GFSK_BW_23400: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_23400"; + } + + case LR11XX_RADIO_GFSK_BW_29300: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_29300"; + } + + case LR11XX_RADIO_GFSK_BW_39000: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_39000"; + } + + case LR11XX_RADIO_GFSK_BW_46900: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_46900"; + } + + case LR11XX_RADIO_GFSK_BW_58600: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_58600"; + } + + case LR11XX_RADIO_GFSK_BW_78200: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_78200"; + } + + case LR11XX_RADIO_GFSK_BW_93800: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_93800"; + } + + case LR11XX_RADIO_GFSK_BW_117300: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_117300"; + } + + case LR11XX_RADIO_GFSK_BW_156200: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_156200"; + } + + case LR11XX_RADIO_GFSK_BW_187200: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_187200"; + } + + case LR11XX_RADIO_GFSK_BW_234300: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_234300"; + } + + case LR11XX_RADIO_GFSK_BW_312000: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_312000"; + } + + case LR11XX_RADIO_GFSK_BW_373600: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_373600"; + } + + case LR11XX_RADIO_GFSK_BW_467000: + { + return ( const char* ) "LR11XX_RADIO_GFSK_BW_467000"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_cad_exit_mode_to_str( const lr11xx_radio_cad_exit_mode_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_CAD_EXIT_MODE_STANDBYRC: + { + return ( const char* ) "LR11XX_RADIO_CAD_EXIT_MODE_STANDBYRC"; + } + + case LR11XX_RADIO_CAD_EXIT_MODE_RX: + { + return ( const char* ) "LR11XX_RADIO_CAD_EXIT_MODE_RX"; + } + + case LR11XX_RADIO_CAD_EXIT_MODE_TX: + { + return ( const char* ) "LR11XX_RADIO_CAD_EXIT_MODE_TX"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_gfsk_pulse_shape_to_str( const lr11xx_radio_gfsk_pulse_shape_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_GFSK_PULSE_SHAPE_OFF: + { + return ( const char* ) "LR11XX_RADIO_GFSK_PULSE_SHAPE_OFF"; + } + + case LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_03: + { + return ( const char* ) "LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_03"; + } + + case LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_05: + { + return ( const char* ) "LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_05"; + } + + case LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_07: + { + return ( const char* ) "LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_07"; + } + + case LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_1: + { + return ( const char* ) "LR11XX_RADIO_GFSK_PULSE_SHAPE_BT_1"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_bpsk_pulse_shape_to_str( const lr11xx_radio_bpsk_pulse_shape_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_DBPSK_PULSE_SHAPE: + { + return ( const char* ) "LR11XX_RADIO_DBPSK_PULSE_SHAPE"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_lr_fhss_bitrate_to_str( const lr11xx_radio_lr_fhss_bitrate_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_LR_FHSS_BITRATE_488_BPS: + { + return ( const char* ) "LR11XX_RADIO_LR_FHSS_BITRATE_488_BPS"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_radio_lr_fhss_pulse_shape_to_str( const lr11xx_radio_lr_fhss_pulse_shape_t value ) +{ + switch( value ) + { + case LR11XX_RADIO_LR_FHSS_PULSE_SHAPE_BT_1: + { + return ( const char* ) "LR11XX_RADIO_LR_FHSS_PULSE_SHAPE_BT_1"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} diff --git a/components/esp_lora_1121/src/lr1121_printers/lr11xx_rttof_types_str.c b/components/esp_lora_1121/src/lr1121_printers/lr11xx_rttof_types_str.c new file mode 100755 index 0000000..2237746 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_printers/lr11xx_rttof_types_str.c @@ -0,0 +1,57 @@ +/*! + * @file lr11xx_rttof_types_str.c + * + * @brief Printer helper functions for LR11xx RTToF types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lr11xx_rttof_types_str.h" + +const char* lr11xx_rttof_result_type_to_str( const lr11xx_rttof_result_type_t value ) +{ + switch( value ) + { + case LR11XX_RTTOF_RESULT_TYPE_RAW: + { + return ( const char* ) "LR11XX_RTTOF_RESULT_TYPE_RAW"; + } + + case LR11XX_RTTOF_RESULT_TYPE_RSSI: + { + return ( const char* ) "LR11XX_RTTOF_RESULT_TYPE_RSSI"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} diff --git a/components/esp_lora_1121/src/lr1121_printers/lr11xx_system_types_str.c b/components/esp_lora_1121/src/lr1121_printers/lr11xx_system_types_str.c new file mode 100755 index 0000000..e0b5b42 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_printers/lr11xx_system_types_str.c @@ -0,0 +1,325 @@ +/*! + * @file lr11xx_system_types_str.c + * + * @brief Printer helper functions for LR11xx system types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lr11xx_system_types_str.h" + +const char* lr11xx_system_chip_modes_to_str( const lr11xx_system_chip_modes_t value ) +{ + switch( value ) + { + case LR11XX_SYSTEM_CHIP_MODE_SLEEP: + { + return ( const char* ) "LR11XX_SYSTEM_CHIP_MODE_SLEEP"; + } + + case LR11XX_SYSTEM_CHIP_MODE_STBY_RC: + { + return ( const char* ) "LR11XX_SYSTEM_CHIP_MODE_STBY_RC"; + } + + case LR11XX_SYSTEM_CHIP_MODE_STBY_XOSC: + { + return ( const char* ) "LR11XX_SYSTEM_CHIP_MODE_STBY_XOSC"; + } + + case LR11XX_SYSTEM_CHIP_MODE_FS: + { + return ( const char* ) "LR11XX_SYSTEM_CHIP_MODE_FS"; + } + + case LR11XX_SYSTEM_CHIP_MODE_RX: + { + return ( const char* ) "LR11XX_SYSTEM_CHIP_MODE_RX"; + } + + case LR11XX_SYSTEM_CHIP_MODE_TX: + { + return ( const char* ) "LR11XX_SYSTEM_CHIP_MODE_TX"; + } + + case LR11XX_SYSTEM_CHIP_MODE_LOC: + { + return ( const char* ) "LR11XX_SYSTEM_CHIP_MODE_LOC"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_system_reset_status_to_str( const lr11xx_system_reset_status_t value ) +{ + switch( value ) + { + case LR11XX_SYSTEM_RESET_STATUS_CLEARED: + { + return ( const char* ) "LR11XX_SYSTEM_RESET_STATUS_CLEARED"; + } + + case LR11XX_SYSTEM_RESET_STATUS_ANALOG: + { + return ( const char* ) "LR11XX_SYSTEM_RESET_STATUS_ANALOG"; + } + + case LR11XX_SYSTEM_RESET_STATUS_EXTERNAL: + { + return ( const char* ) "LR11XX_SYSTEM_RESET_STATUS_EXTERNAL"; + } + + case LR11XX_SYSTEM_RESET_STATUS_SYSTEM: + { + return ( const char* ) "LR11XX_SYSTEM_RESET_STATUS_SYSTEM"; + } + + case LR11XX_SYSTEM_RESET_STATUS_WATCHDOG: + { + return ( const char* ) "LR11XX_SYSTEM_RESET_STATUS_WATCHDOG"; + } + + case LR11XX_SYSTEM_RESET_STATUS_IOCD_RESTART: + { + return ( const char* ) "LR11XX_SYSTEM_RESET_STATUS_IOCD_RESTART"; + } + + case LR11XX_SYSTEM_RESET_STATUS_RTC_RESTART: + { + return ( const char* ) "LR11XX_SYSTEM_RESET_STATUS_RTC_RESTART"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_system_command_status_to_str( const lr11xx_system_command_status_t value ) +{ + switch( value ) + { + case LR11XX_SYSTEM_CMD_STATUS_FAIL: + { + return ( const char* ) "LR11XX_SYSTEM_CMD_STATUS_FAIL"; + } + + case LR11XX_SYSTEM_CMD_STATUS_PERR: + { + return ( const char* ) "LR11XX_SYSTEM_CMD_STATUS_PERR"; + } + + case LR11XX_SYSTEM_CMD_STATUS_OK: + { + return ( const char* ) "LR11XX_SYSTEM_CMD_STATUS_OK"; + } + + case LR11XX_SYSTEM_CMD_STATUS_DATA: + { + return ( const char* ) "LR11XX_SYSTEM_CMD_STATUS_DATA"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_system_lfclk_cfg_to_str( const lr11xx_system_lfclk_cfg_t value ) +{ + switch( value ) + { + case LR11XX_SYSTEM_LFCLK_RC: + { + return ( const char* ) "LR11XX_SYSTEM_LFCLK_RC"; + } + + case LR11XX_SYSTEM_LFCLK_XTAL: + { + return ( const char* ) "LR11XX_SYSTEM_LFCLK_XTAL"; + } + + case LR11XX_SYSTEM_LFCLK_EXT: + { + return ( const char* ) "LR11XX_SYSTEM_LFCLK_EXT"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_system_reg_mode_to_str( const lr11xx_system_reg_mode_t value ) +{ + switch( value ) + { + case LR11XX_SYSTEM_REG_MODE_LDO: + { + return ( const char* ) "LR11XX_SYSTEM_REG_MODE_LDO"; + } + + case LR11XX_SYSTEM_REG_MODE_DCDC: + { + return ( const char* ) "LR11XX_SYSTEM_REG_MODE_DCDC"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_system_infopage_id_to_str( const lr11xx_system_infopage_id_t value ) +{ + switch( value ) + { + case LR11XX_SYSTEM_INFOPAGE_0: + { + return ( const char* ) "LR11XX_SYSTEM_INFOPAGE_0"; + } + + case LR11XX_SYSTEM_INFOPAGE_1: + { + return ( const char* ) "LR11XX_SYSTEM_INFOPAGE_1"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_system_standby_cfg_to_str( const lr11xx_system_standby_cfg_t value ) +{ + switch( value ) + { + case LR11XX_SYSTEM_STANDBY_CFG_RC: + { + return ( const char* ) "LR11XX_SYSTEM_STANDBY_CFG_RC"; + } + + case LR11XX_SYSTEM_STANDBY_CFG_XOSC: + { + return ( const char* ) "LR11XX_SYSTEM_STANDBY_CFG_XOSC"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_system_tcxo_supply_voltage_to_str( const lr11xx_system_tcxo_supply_voltage_t value ) +{ + switch( value ) + { + case LR11XX_SYSTEM_TCXO_CTRL_1_6V: + { + return ( const char* ) "LR11XX_SYSTEM_TCXO_CTRL_1_6V"; + } + + case LR11XX_SYSTEM_TCXO_CTRL_1_7V: + { + return ( const char* ) "LR11XX_SYSTEM_TCXO_CTRL_1_7V"; + } + + case LR11XX_SYSTEM_TCXO_CTRL_1_8V: + { + return ( const char* ) "LR11XX_SYSTEM_TCXO_CTRL_1_8V"; + } + + case LR11XX_SYSTEM_TCXO_CTRL_2_2V: + { + return ( const char* ) "LR11XX_SYSTEM_TCXO_CTRL_2_2V"; + } + + case LR11XX_SYSTEM_TCXO_CTRL_2_4V: + { + return ( const char* ) "LR11XX_SYSTEM_TCXO_CTRL_2_4V"; + } + + case LR11XX_SYSTEM_TCXO_CTRL_2_7V: + { + return ( const char* ) "LR11XX_SYSTEM_TCXO_CTRL_2_7V"; + } + + case LR11XX_SYSTEM_TCXO_CTRL_3_0V: + { + return ( const char* ) "LR11XX_SYSTEM_TCXO_CTRL_3_0V"; + } + + case LR11XX_SYSTEM_TCXO_CTRL_3_3V: + { + return ( const char* ) "LR11XX_SYSTEM_TCXO_CTRL_3_3V"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} + +const char* lr11xx_system_version_type_to_str( const lr11xx_system_version_type_t value ) +{ + switch( value ) + { + case LR11XX_SYSTEM_VERSION_TYPE_LR1110: + { + return ( const char* ) "LR11XX_SYSTEM_VERSION_TYPE_LR1110"; + } + + case LR11XX_SYSTEM_VERSION_TYPE_LR1120: + { + return ( const char* ) "LR11XX_SYSTEM_VERSION_TYPE_LR1120"; + } + + case LR11XX_SYSTEM_VERSION_TYPE_LR1121: + { + return ( const char* ) "LR11XX_SYSTEM_VERSION_TYPE_LR1121"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} diff --git a/components/esp_lora_1121/src/lr1121_printers/lr11xx_types_str.c b/components/esp_lora_1121/src/lr1121_printers/lr11xx_types_str.c new file mode 100755 index 0000000..20bfd47 --- /dev/null +++ b/components/esp_lora_1121/src/lr1121_printers/lr11xx_types_str.c @@ -0,0 +1,57 @@ +/*! + * @file lr11xx_types_str.c + * + * @brief Printer helper functions for LR11xx types + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2023. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lr11xx_types_str.h" + +const char* lr11xx_status_to_str( const lr11xx_status_t value ) +{ + switch( value ) + { + case LR11XX_STATUS_OK: + { + return ( const char* ) "LR11XX_STATUS_OK"; + } + + case LR11XX_STATUS_ERROR: + { + return ( const char* ) "LR11XX_STATUS_ERROR"; + } + + default: + { + return ( const char* ) "Unknown"; + } + } +} diff --git a/components/esp_lora_1121/src/lr11xx_driver/lr11xx_bootloader.c b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_bootloader.c new file mode 100755 index 0000000..7c67adf --- /dev/null +++ b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_bootloader.c @@ -0,0 +1,294 @@ +/*! + * @file lr11xx_bootloader.c + * + * @brief Bootloader driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_bootloader.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_FLASH_DATA_MAX_LENGTH_UINT32 ( 64 ) +#define LR11XX_FLASH_DATA_MAX_LENGTH_UINT8 ( LR11XX_FLASH_DATA_MAX_LENGTH_UINT32 * 4 ) + +#define LR11XX_BL_CMD_NO_PARAM_LENGTH ( 2 ) +#define LR11XX_BL_GET_STATUS_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_BL_VERSION_CMD_LENGTH LR11XX_BL_CMD_NO_PARAM_LENGTH +#define LR11XX_BL_ERASE_FLASH_CMD_LENGTH LR11XX_BL_CMD_NO_PARAM_LENGTH +#define LR11XX_BL_WRITE_FLASH_ENCRYPTED_CMD_LENGTH ( LR11XX_BL_CMD_NO_PARAM_LENGTH + 4 ) +#define LR11XX_BL_REBOOT_CMD_LENGTH ( LR11XX_BL_CMD_NO_PARAM_LENGTH + 1 ) +#define LR11XX_BL_GET_PIN_CMD_LENGTH ( LR11XX_BL_CMD_NO_PARAM_LENGTH ) +#define LR11XX_BL_READ_CHIP_EUI_CMD_LENGTH ( LR11XX_BL_CMD_NO_PARAM_LENGTH ) +#define LR11XX_BL_READ_JOIN_EUI_CMD_LENGTH ( LR11XX_BL_CMD_NO_PARAM_LENGTH ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for bootloader-related operations + */ +enum +{ + LR11XX_BL_GET_STATUS_OC = 0x0100, + LR11XX_BL_GET_VERSION_OC = 0x0101, + LR11XX_BL_ERASE_FLASH_OC = 0x8000, + LR11XX_BL_WRITE_FLASH_ENCRYPTED_OC = 0x8003, + LR11XX_BL_REBOOT_OC = 0x8005, + LR11XX_BL_GET_PIN_OC = 0x800B, + LR11XX_BL_READ_CHIP_EUI_OC = 0x800C, + LR11XX_BL_READ_JOIN_EUI_OC = 0x800D, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Returns the minimum of the operand given as parameter and the maximum allowed block size + * + * @param [in] operand Size to compare + * + * @returns Minimum between operand and @ref LR11XX_FLASH_DATA_MAX_LENGTH_UINT32 + */ +static uint8_t lr11xx_bootloader_get_min_from_operand_and_max_block_size( uint32_t operand ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_bootloader_get_status( const void* context, lr11xx_bootloader_stat1_t* stat1, + lr11xx_bootloader_stat2_t* stat2, + lr11xx_bootloader_irq_mask_t* irq_status ) +{ + uint8_t data[LR11XX_BL_GET_STATUS_CMD_LENGTH] = { 0 }; + + const lr11xx_status_t status = + ( lr11xx_status_t ) lr11xx_hal_direct_read( context, data, LR11XX_BL_GET_STATUS_CMD_LENGTH ); + + if( status == LR11XX_STATUS_OK ) + { + stat1->is_interrupt_active = ( ( data[0] & 0x01 ) != 0 ) ? true : false; + stat1->command_status = ( lr11xx_bootloader_command_status_t ) ( data[0] >> 1 ); + + stat2->is_running_from_flash = ( ( data[1] & 0x01 ) != 0 ) ? true : false; + stat2->chip_mode = ( lr11xx_bootloader_chip_modes_t ) ( ( data[1] & 0x0F ) >> 1 ); + stat2->reset_status = ( lr11xx_bootloader_reset_status_t ) ( ( data[1] & 0xF0 ) >> 4 ); + + *irq_status = + ( ( lr11xx_bootloader_irq_mask_t ) data[2] << 24 ) + ( ( lr11xx_bootloader_irq_mask_t ) data[3] << 16 ) + + ( ( lr11xx_bootloader_irq_mask_t ) data[4] << 8 ) + ( ( lr11xx_bootloader_irq_mask_t ) data[5] << 0 ); + } + + return status; +} + +lr11xx_status_t lr11xx_bootloader_clear_reset_status_info( const void* context ) +{ + const uint8_t cbuffer[LR11XX_BL_CMD_NO_PARAM_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_GET_STATUS_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_GET_STATUS_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_BL_CMD_NO_PARAM_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_bootloader_get_version( const void* context, lr11xx_bootloader_version_t* version ) +{ + const uint8_t cbuffer[LR11XX_BL_VERSION_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_GET_VERSION_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_GET_VERSION_OC >> 0 ), + }; + uint8_t rbuffer[LR11XX_BL_VERSION_LENGTH] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_BL_VERSION_CMD_LENGTH, + rbuffer, LR11XX_BL_VERSION_LENGTH ); + + if( status == LR11XX_STATUS_OK ) + { + version->hw = rbuffer[0]; + version->type = rbuffer[1]; + version->fw = ( ( uint16_t ) rbuffer[2] << 8 ) + ( uint16_t ) rbuffer[3]; + } + + return status; +} + +lr11xx_status_t lr11xx_bootloader_erase_flash( const void* context ) +{ + const uint8_t cbuffer[LR11XX_BL_ERASE_FLASH_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_ERASE_FLASH_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_ERASE_FLASH_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_BL_ERASE_FLASH_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_bootloader_write_flash_encrypted( const void* context, const uint32_t offset_in_byte, + const uint32_t* data, uint8_t length_in_word ) +{ + const uint8_t cbuffer[LR11XX_BL_WRITE_FLASH_ENCRYPTED_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_WRITE_FLASH_ENCRYPTED_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_WRITE_FLASH_ENCRYPTED_OC >> 0 ), + ( uint8_t ) ( offset_in_byte >> 24 ), + ( uint8_t ) ( offset_in_byte >> 16 ), + ( uint8_t ) ( offset_in_byte >> 8 ), + ( uint8_t ) ( offset_in_byte >> 0 ), + }; + + uint8_t cdata[LR11XX_FLASH_DATA_MAX_LENGTH_UINT8] = { 0 }; + for( uint8_t index = 0; index < length_in_word; index++ ) + { + uint8_t* cdata_local = &cdata[index * sizeof( uint32_t )]; + + cdata_local[0] = ( uint8_t ) ( data[index] >> 24 ); + cdata_local[1] = ( uint8_t ) ( data[index] >> 16 ); + cdata_local[2] = ( uint8_t ) ( data[index] >> 8 ); + cdata_local[3] = ( uint8_t ) ( data[index] >> 0 ); + } + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_BL_WRITE_FLASH_ENCRYPTED_CMD_LENGTH, cdata, + length_in_word * sizeof( uint32_t ) ); +} + +lr11xx_status_t lr11xx_bootloader_write_flash_encrypted_full( const void* context, const uint32_t offset_in_byte, + const uint32_t* buffer, const uint32_t length_in_word ) +{ + uint32_t remaining_length = length_in_word; + uint32_t local_offset = offset_in_byte; + uint32_t loop = 0; + + while( remaining_length != 0 ) + { + const lr11xx_status_t status = lr11xx_bootloader_write_flash_encrypted( + context, local_offset, buffer + loop * LR11XX_FLASH_DATA_MAX_LENGTH_UINT32, + lr11xx_bootloader_get_min_from_operand_and_max_block_size( remaining_length ) ); + + if( status != LR11XX_STATUS_OK ) + { + return status; + } + + local_offset += LR11XX_FLASH_DATA_MAX_LENGTH_UINT8; + remaining_length = ( remaining_length < LR11XX_FLASH_DATA_MAX_LENGTH_UINT32 ) + ? 0 + : ( remaining_length - LR11XX_FLASH_DATA_MAX_LENGTH_UINT32 ); + + loop++; + } + + return LR11XX_STATUS_OK; +} + +lr11xx_status_t lr11xx_bootloader_reboot( const void* context, const bool stay_in_bootloader ) +{ + const uint8_t cbuffer[LR11XX_BL_REBOOT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_REBOOT_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_REBOOT_OC >> 0 ), + ( stay_in_bootloader == true ) ? 0x03 : 0x00, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_BL_REBOOT_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_bootloader_read_pin( const void* context, lr11xx_bootloader_pin_t pin ) +{ + const uint8_t cbuffer[LR11XX_BL_GET_PIN_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_GET_PIN_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_GET_PIN_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_BL_GET_PIN_CMD_LENGTH, pin, + LR11XX_BL_PIN_LENGTH ); +} + +lr11xx_status_t lr11xx_bootloader_read_chip_eui( const void* context, lr11xx_bootloader_chip_eui_t chip_eui ) +{ + const uint8_t cbuffer[LR11XX_BL_READ_CHIP_EUI_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_READ_CHIP_EUI_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_READ_CHIP_EUI_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_BL_READ_CHIP_EUI_CMD_LENGTH, chip_eui, + LR11XX_BL_CHIP_EUI_LENGTH ); +} + +lr11xx_status_t lr11xx_bootloader_read_join_eui( const void* context, lr11xx_bootloader_join_eui_t join_eui ) +{ + const uint8_t cbuffer[LR11XX_BL_READ_JOIN_EUI_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_BL_READ_JOIN_EUI_OC >> 8 ), + ( uint8_t ) ( LR11XX_BL_READ_JOIN_EUI_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_BL_READ_JOIN_EUI_CMD_LENGTH, join_eui, + LR11XX_BL_JOIN_EUI_LENGTH ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +uint8_t lr11xx_bootloader_get_min_from_operand_and_max_block_size( uint32_t operand ) +{ + if( operand > LR11XX_FLASH_DATA_MAX_LENGTH_UINT32 ) + { + return LR11XX_FLASH_DATA_MAX_LENGTH_UINT32; + } + else + { + return ( uint8_t ) operand; + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr11xx_driver/lr11xx_crypto_engine.c b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_crypto_engine.c new file mode 100755 index 0000000..3ff69a5 --- /dev/null +++ b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_crypto_engine.c @@ -0,0 +1,589 @@ +/*! + * @file lr11xx_crypto_engine.c + * + * @brief Cryptographic engine driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_crypto_engine.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_CRYPTO_FW_IMAGE_DATA_MAX_LENGTH_UINT32 ( 64 ) +#define LR11XX_CRYPTO_FW_IMAGE_DATA_MAX_LENGTH_UINT8 ( LR11XX_CRYPTO_FW_IMAGE_DATA_MAX_LENGTH_UINT32 * 4 ) + +#define LR11XX_CRYPTO_SELECT_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_CRYPTO_SET_KEY_CMD_LENGTH ( 2 + 17 ) +#define LR11XX_CRYPTO_DERIVE_KEY_CMD_LENGTH ( 2 + 18 ) +#define LR11XX_CRYPTO_PROCESS_JOIN_ACCEPT_CMD_LENGTH ( 2 + 3 + 12 + 32 ) +#define LR11XX_CRYPTO_COMPUTE_AES_CMAC_CMD_LENGTH ( 2 + 1 + 272 ) +#define LR11XX_CRYPTO_VERIFY_AES_CMAC_CMD_LENGTH ( 2 + 1 + 4 + 256 ) +#define LR11XX_CRYPTO_AES_ENCRYPT_CMD_LENGTH ( 2 + 1 + 256 ) +#define LR11XX_CRYPTO_AES_DECRYPT_CMD_LENGTH ( 2 + 1 + 256 ) +#define LR11XX_CRYPTO_STORE_TO_FLASH_CMD_LENGTH ( 2 ) +#define LR11XX_CRYPTO_RESTORE_FROM_FLASH_CMD_LENGTH ( 2 ) +#define LR11XX_CRYPTO_SET_PARAMETER_CMD_LENGTH ( 2 + 1 + 4 ) +#define LR11XX_CRYPTO_GET_PARAMETER_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_CRYPTO_CHECK_ENCRYPTED_FW_IMAGE_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_CRYPTO_GET_CHECK_ENCRYPTED_FW_IMAGE_RESULT_CMD_LENGTH ( 2 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for crypto-related operations + */ +enum +{ + LR11XX_CRYPTO_SELECT_OC = 0x0500, + LR11XX_CRYPTO_SET_KEY_OC = 0x0502, + LR11XX_CRYPTO_DERIVE_KEY_OC = 0x0503, + LR11XX_CRYPTO_PROCESS_JOIN_ACCEPT_OC = 0x0504, + LR11XX_CRYPTO_COMPUTE_AES_CMAC_OC = 0x0505, + LR11XX_CRYPTO_VERIFY_AES_CMAC_OC = 0x0506, + LR11XX_CRYPTO_ENCRYPT_AES_01_OC = 0x0507, + LR11XX_CRYPTO_ENCRYPT_AES_OC = 0x0508, + LR11XX_CRYPTO_DECRYPT_AES_OC = 0x0509, + LR11XX_CRYPTO_STORE_TO_FLASH_OC = 0x050A, + LR11XX_CRYPTO_RESTORE_FROM_FLASH_OC = 0x050B, + LR11XX_CRYPTO_SET_PARAMETER_OC = 0x050D, + LR11XX_CRYPTO_GET_PARAMETER_OC = 0x050E, + LR11XX_CRYPTO_CHECK_ENCRYPTED_FW_IMAGE_OC = 0x050F, + LR11XX_CRYPTO_GET_CHECK_ENCRYPTED_FW_IMAGE_RESULT_OC = 0x0510, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Helper function that fill the cbuffer provided in first argument with the command opcode, the key id + * and the data to encrypt/decrypt/compute aes cmac + * + * @param [out] cbuffer Buffer used to build the frame + * @param [in] opcode Opcode to be added to the frame + * @param [in] key_id Key ID to be added to the frame + * @param [in] data Data to be added to the frame + * @param [in] length Number of bytes from data to be added to the frame + * + * @warning The caller MUST ensure cbuffer is array is big enough to contain opcode, key_id, and data! + */ +static void lr11xx_crypto_fill_cbuffer_opcode_key_data( uint8_t* cbuffer, uint16_t opcode, uint8_t key_id, + const uint8_t* data, uint16_t length ); + +/*! + * @brief Returns the minimum of the operand given as parameter and the maximum allowed block size + * + * @param [in] operand Size to compare + * + * @returns Minimum between operand and @ref LR11XX_CRYPTO_FW_IMAGE_DATA_MAX_LENGTH_UINT32 + */ +static uint8_t lr11xx_crypto_get_min_from_operand_and_max_block_size( uint32_t operand ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_crypto_select( const void* context, const lr11xx_crypto_element_t element ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_SELECT_CMD_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_SELECT_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_SELECT_OC >> 0 ); + + cbuffer[2] = element; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_CRYPTO_SELECT_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_crypto_set_key( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const lr11xx_crypto_key_t key ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_SET_KEY_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_SET_KEY_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_SET_KEY_OC >> 0 ); + + cbuffer[2] = key_id; + + for( uint8_t index = 0; index < sizeof( lr11xx_crypto_key_t ); index++ ) + { + cbuffer[3 + index] = key[index]; + } + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_CRYPTO_SET_KEY_CMD_LENGTH, rbuffer, LR11XX_CRYPTO_STATUS_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_derive_key( const void* context, lr11xx_crypto_status_t* status, const uint8_t src_key_id, + const uint8_t dest_key_id, const lr11xx_crypto_nonce_t nonce ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_DERIVE_KEY_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_DERIVE_KEY_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_DERIVE_KEY_OC >> 0 ); + + cbuffer[2] = src_key_id; + cbuffer[3] = dest_key_id; + + for( uint8_t index = 0; index < LR11XX_CRYPTO_NONCE_LENGTH; index++ ) + { + cbuffer[4 + index] = nonce[index]; + } + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_CRYPTO_DERIVE_KEY_CMD_LENGTH, rbuffer, LR11XX_CRYPTO_STATUS_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_process_join_accept( const void* context, lr11xx_crypto_status_t* status, + const uint8_t dec_key_id, const uint8_t ver_key_id, + const lr11xx_crypto_lorawan_version_t lorawan_version, + const uint8_t* header, const uint8_t* data_in, const uint8_t length, + uint8_t* data_out ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_PROCESS_JOIN_ACCEPT_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH + 32] = { 0x00 }; + uint8_t header_length = ( lorawan_version == 0 ) ? 1 : 12; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_PROCESS_JOIN_ACCEPT_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_PROCESS_JOIN_ACCEPT_OC >> 0 ); + + cbuffer[2] = dec_key_id; + cbuffer[3] = ver_key_id; + cbuffer[4] = ( uint8_t ) lorawan_version; + + for( uint8_t index = 0; index < header_length; index++ ) + { + cbuffer[5 + index] = header[index]; + } + + for( uint8_t index = 0; index < length; index++ ) + { + cbuffer[5 + header_length + index] = data_in[index]; + } + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, 2 + 3 + header_length + length, rbuffer, 1 + length ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + + if( *status == LR11XX_CRYPTO_STATUS_SUCCESS ) + { + for( uint8_t index = 0; index < length; index++ ) + { + data_out[index] = rbuffer[1 + index]; + } + } + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_compute_aes_cmac( const void* context, lr11xx_crypto_status_t* status, + const uint8_t key_id, const uint8_t* data, const uint16_t length, + lr11xx_crypto_mic_t mic ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_COMPUTE_AES_CMAC_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_MIC_LENGTH] = { 0x00 }; + + lr11xx_crypto_fill_cbuffer_opcode_key_data( cbuffer, LR11XX_CRYPTO_COMPUTE_AES_CMAC_OC, key_id, data, length ); + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, 3 + length, rbuffer, + LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_MIC_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + + if( *status == LR11XX_CRYPTO_STATUS_SUCCESS ) + { + for( uint8_t index = 0; index < LR11XX_CRYPTO_MIC_LENGTH; index++ ) + { + mic[index] = rbuffer[1 + index]; + } + } + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_verify_aes_cmac( const void* context, lr11xx_crypto_status_t* status, + const uint8_t key_id, const uint8_t* data, const uint16_t length, + const lr11xx_crypto_mic_t mic ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_VERIFY_AES_CMAC_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_VERIFY_AES_CMAC_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_VERIFY_AES_CMAC_OC >> 0 ); + + cbuffer[2] = key_id; + + for( uint8_t index = 0; index < LR11XX_CRYPTO_MIC_LENGTH; index++ ) + { + cbuffer[3 + index] = mic[index]; + } + + for( uint16_t index = 0; index < length; index++ ) + { + cbuffer[3 + LR11XX_CRYPTO_MIC_LENGTH + index] = data[index]; + } + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, 3 + LR11XX_CRYPTO_MIC_LENGTH + length, + rbuffer, LR11XX_CRYPTO_STATUS_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_aes_encrypt_01( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const uint8_t* data, const uint16_t length, uint8_t* result ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_AES_ENCRYPT_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_DATA_MAX_LENGTH] = { 0x00 }; + + lr11xx_crypto_fill_cbuffer_opcode_key_data( cbuffer, LR11XX_CRYPTO_ENCRYPT_AES_01_OC, key_id, data, length ); + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, 3 + length, rbuffer, LR11XX_CRYPTO_STATUS_LENGTH + length ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + + if( *status == LR11XX_CRYPTO_STATUS_SUCCESS ) + { + for( uint16_t index = 0; index < length; index++ ) + { + result[index] = rbuffer[1 + index]; + } + } + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_aes_encrypt( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const uint8_t* data, const uint16_t length, uint8_t* result ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_AES_ENCRYPT_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_DATA_MAX_LENGTH] = { 0x00 }; + + lr11xx_crypto_fill_cbuffer_opcode_key_data( cbuffer, LR11XX_CRYPTO_ENCRYPT_AES_OC, key_id, data, length ); + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, 3 + length, rbuffer, LR11XX_CRYPTO_STATUS_LENGTH + length ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + + if( *status == LR11XX_CRYPTO_STATUS_SUCCESS ) + { + for( uint16_t index = 0; index < length; index++ ) + { + result[index] = rbuffer[1 + index]; + } + } + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_aes_decrypt( const void* context, lr11xx_crypto_status_t* status, const uint8_t key_id, + const uint8_t* data, const uint16_t length, uint8_t* result ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_AES_DECRYPT_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_DATA_MAX_LENGTH] = { 0x00 }; + + lr11xx_crypto_fill_cbuffer_opcode_key_data( cbuffer, LR11XX_CRYPTO_DECRYPT_AES_OC, key_id, data, length ); + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, 3 + length, rbuffer, LR11XX_CRYPTO_STATUS_LENGTH + length ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + + if( *status == LR11XX_CRYPTO_STATUS_SUCCESS ) + { + for( uint16_t index = 0; index < length; index++ ) + { + result[index] = rbuffer[1 + index]; + } + } + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_store_to_flash( const void* context, lr11xx_crypto_status_t* status ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_STORE_TO_FLASH_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_STORE_TO_FLASH_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_STORE_TO_FLASH_OC >> 0 ); + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, LR11XX_CRYPTO_STORE_TO_FLASH_CMD_LENGTH, + rbuffer, LR11XX_CRYPTO_STATUS_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_restore_from_flash( const void* context, lr11xx_crypto_status_t* status ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_RESTORE_FROM_FLASH_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_RESTORE_FROM_FLASH_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_RESTORE_FROM_FLASH_OC >> 0 ); + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( + context, cbuffer, LR11XX_CRYPTO_RESTORE_FROM_FLASH_CMD_LENGTH, rbuffer, LR11XX_CRYPTO_STATUS_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_set_parameter( const void* context, lr11xx_crypto_status_t* status, + const uint8_t param_id, const lr11xx_crypto_param_t parameter ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_SET_PARAMETER_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_SET_PARAMETER_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_SET_PARAMETER_OC >> 0 ); + + cbuffer[2] = param_id; + + for( uint8_t index = 0; index < LR11XX_CRYPTO_PARAMETER_LENGTH; index++ ) + { + cbuffer[3 + index] = parameter[index]; + } + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, LR11XX_CRYPTO_SET_PARAMETER_CMD_LENGTH, + rbuffer, LR11XX_CRYPTO_STATUS_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_get_parameter( const void* context, lr11xx_crypto_status_t* status, + const uint8_t param_id, lr11xx_crypto_param_t parameter ) +{ + uint8_t cbuffer[LR11XX_CRYPTO_GET_PARAMETER_CMD_LENGTH] = { 0x00 }; + uint8_t rbuffer[LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_PARAMETER_LENGTH] = { 0x00 }; + + cbuffer[0] = ( uint8_t ) ( LR11XX_CRYPTO_GET_PARAMETER_OC >> 8 ); + cbuffer[1] = ( uint8_t ) ( LR11XX_CRYPTO_GET_PARAMETER_OC >> 0 ); + + cbuffer[2] = param_id; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_CRYPTO_GET_PARAMETER_CMD_LENGTH, rbuffer, + LR11XX_CRYPTO_STATUS_LENGTH + LR11XX_CRYPTO_PARAMETER_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *status = ( lr11xx_crypto_status_t ) rbuffer[0]; + + if( *status == LR11XX_CRYPTO_STATUS_SUCCESS ) + { + for( uint8_t index = 0; index < LR11XX_CRYPTO_PARAMETER_LENGTH; index++ ) + { + parameter[index] = rbuffer[1 + index]; + } + } + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_crypto_check_encrypted_firmware_image( const void* context, const uint32_t offset_in_byte, + const uint32_t* data, const uint8_t length_in_word ) +{ + const uint8_t cbuffer[LR11XX_CRYPTO_CHECK_ENCRYPTED_FW_IMAGE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_CRYPTO_CHECK_ENCRYPTED_FW_IMAGE_OC >> 8 ), + ( uint8_t ) ( LR11XX_CRYPTO_CHECK_ENCRYPTED_FW_IMAGE_OC >> 0 ), + ( uint8_t ) ( offset_in_byte >> 24 ), + ( uint8_t ) ( offset_in_byte >> 16 ), + ( uint8_t ) ( offset_in_byte >> 8 ), + ( uint8_t ) ( offset_in_byte >> 0 ), + }; + + uint8_t cdata[256] = { 0 }; + for( uint8_t index = 0; index < length_in_word; index++ ) + { + uint8_t* cdata_local = &cdata[index * sizeof( uint32_t )]; + + cdata_local[0] = ( uint8_t ) ( data[index] >> 24 ); + cdata_local[1] = ( uint8_t ) ( data[index] >> 16 ); + cdata_local[2] = ( uint8_t ) ( data[index] >> 8 ); + cdata_local[3] = ( uint8_t ) ( data[index] >> 0 ); + } + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_CRYPTO_CHECK_ENCRYPTED_FW_IMAGE_CMD_LENGTH, + cdata, length_in_word * sizeof( uint32_t ) ); +} + +lr11xx_status_t lr11xx_crypto_check_encrypted_firmware_image_full( const void* context, const uint32_t offset_in_byte, + const uint32_t* buffer, + const uint32_t length_in_word ) +{ + uint32_t remaining_length = length_in_word; + uint32_t local_offset = offset_in_byte; + uint32_t loop = 0; + + while( remaining_length != 0 ) + { + const lr11xx_status_t status = lr11xx_crypto_check_encrypted_firmware_image( + context, local_offset, buffer + loop * LR11XX_CRYPTO_FW_IMAGE_DATA_MAX_LENGTH_UINT32, + lr11xx_crypto_get_min_from_operand_and_max_block_size( remaining_length ) ); + + if( status != LR11XX_STATUS_OK ) + { + return status; + } + + local_offset += LR11XX_CRYPTO_FW_IMAGE_DATA_MAX_LENGTH_UINT8; + remaining_length = ( remaining_length < LR11XX_CRYPTO_FW_IMAGE_DATA_MAX_LENGTH_UINT32 ) + ? 0 + : ( remaining_length - LR11XX_CRYPTO_FW_IMAGE_DATA_MAX_LENGTH_UINT32 ); + + loop++; + } + + return LR11XX_STATUS_OK; +} + +lr11xx_status_t lr11xx_crypto_get_check_encrypted_firmware_image_result( const void* context, + bool* is_encrypted_fw_image_ok ) +{ + const uint8_t cbuffer[LR11XX_CRYPTO_GET_CHECK_ENCRYPTED_FW_IMAGE_RESULT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_CRYPTO_GET_CHECK_ENCRYPTED_FW_IMAGE_RESULT_OC >> 8 ), + ( uint8_t ) ( LR11XX_CRYPTO_GET_CHECK_ENCRYPTED_FW_IMAGE_RESULT_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, + LR11XX_CRYPTO_GET_CHECK_ENCRYPTED_FW_IMAGE_RESULT_CMD_LENGTH, + ( uint8_t* ) is_encrypted_fw_image_ok, 1 ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static void lr11xx_crypto_fill_cbuffer_opcode_key_data( uint8_t* cbuffer, uint16_t opcode, uint8_t key_id, + const uint8_t* data, uint16_t length ) +{ + cbuffer[0] = ( uint8_t ) ( opcode >> 8 ); + cbuffer[1] = ( uint8_t ) ( opcode >> 0 ); + + cbuffer[2] = key_id; + + for( uint16_t index = 0; index < length; index++ ) + { + cbuffer[3 + index] = data[index]; + } +} + +uint8_t lr11xx_crypto_get_min_from_operand_and_max_block_size( uint32_t operand ) +{ + if( operand > LR11XX_CRYPTO_FW_IMAGE_DATA_MAX_LENGTH_UINT32 ) + { + return LR11XX_CRYPTO_FW_IMAGE_DATA_MAX_LENGTH_UINT32; + } + else + { + return ( uint8_t ) operand; // Downcast done on purpose given that the value is smaller than 64 + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr11xx_driver/lr11xx_driver_version.c b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_driver_version.c new file mode 100755 index 0000000..42355bf --- /dev/null +++ b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_driver_version.c @@ -0,0 +1,88 @@ +/*! + * @file lr11xx_driver_version.c + * + * @brief Placeholder to keep the version of LR11XX driver. + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_driver_version.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +#define STR_HELPER( x ) #x +#define STR( x ) STR_HELPER( x ) + +#define LR11XX_DRIVER_VERSION_FULL \ + "v" STR( LR11XX_DRIVER_VERSION_MAJOR ) "." STR( LR11XX_DRIVER_VERSION_MINOR ) "." STR( LR11XX_DRIVER_VERSION_PATCH ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +const char* lr11xx_driver_version_get_version_string( void ) +{ + return ( const char* ) LR11XX_DRIVER_VERSION_FULL; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr11xx_driver/lr11xx_gnss.c b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_gnss.c new file mode 100755 index 0000000..c3d6dc5 --- /dev/null +++ b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_gnss.c @@ -0,0 +1,1339 @@ +/*! + * @file lr11xx_gnss.c + * + * @brief GNSS scan driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_gnss.h" +#include "lr11xx_regmem.h" +#include "lr11xx_system_types.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_GNSS_READ_GNSS_RSSI_TEST_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_GNSS_SET_CONSTELLATION_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_GNSS_READ_CONSTELLATION_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_SET_ALMANAC_UPDATE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_GNSS_READ_ALMANAC_UPDATE_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_SET_FREQ_SEARCH_SPACE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_GNSS_READ_FREQ_SEARCH_SPACE_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_READ_FW_VERSION_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_READ_SUPPORTED_CONSTELLATION_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_SET_SCAN_MODE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_GNSS_SCAN_CMD_LENGTH ( 2 + 3 ) +#define LR11XX_GNSS_SCAN_GET_RES_SIZE_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_SCAN_READ_RES_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_ALMANAC_UPDATE_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_ALMANAC_READ_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_SET_ASSISTANCE_POSITION_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_GNSS_READ_ASSISTANCE_POSITION_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_PUSH_SOLVER_MSG_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_PUSH_DM_MSG_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_GET_CONTEXT_STATUS_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_GET_NB_SV_SATELLITES_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_GET_SV_SATELLITES_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_READ_ALMANAC_PER_SATELLITE_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_GNSS_GET_SV_VISIBLE_CMD_LENGTH ( 2 + 9 ) +#define LR11XX_GNSS_GET_SV_VISIBLE_DOPPLER_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_FETCH_TIME_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_GNSS_READ_TIME_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_RESET_TIME_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_RESET_POSITION_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_READ_WEEK_NUMBER_ROLLOVER_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_READ_DOPPLER_SOLVER_RESULT_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_READ_ALMANAC_STATUS_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_ALMANAC_UPDATE_FROM_SAT_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_GNSS_READ_DEMOD_STATUS_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_READ_CUMULATIVE_TIMING_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_SET_TIME_CMD_LENGTH ( 2 + 6 ) +#define LR11XX_GNSS_CONFIG_DELAY_RESET_AP_CMD_LENGTH ( 2 + 3 ) +#define LR11XX_GNSS_CONFIG_READ_RESET_AP_CMD_LENGTH ( 2 ) +#define LR11XX_GNSS_CONFIG_READ_KEEP_SYNC_STATUS_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_GNSS_CONFIG_ALMANAC_UPDATE_PERIOD_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_GNSS_READ_ALMANAC_UPDATE_PERIOD_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_GNSS_GET_SV_SYNC_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_GNSS_WRITE_GPS_BIT_MASK_SAT_ACTIVATED_CMD_LENGTH ( 2 + 5 ) +#define LR11XX_GNSS_WRITE_BEIDOU_BIT_MASK_SAT_ACTIVATED_CMD_LENGTH ( 2 + 9 ) +#define LR11XX_GNSS_READ_LAST_SCAN_MODE_LAUNCHED_CMD_LENGTH ( 2 ) + +#define LR11XX_GNSS_READ_GNSS_RSSI_TEST_READ_RBUFFER_LENGTH ( 2 ) +#define LR11XX_GNSS_ALMANAC_READ_RBUFFER_LENGTH ( 6 ) +#define LR11XX_GNSS_ALMANAC_DATE_LENGTH ( 2 ) +#define LR11XX_GNSS_ALMANAC_UPDATE_MAX_NB_OF_BLOCKS \ + ( ( LR11XX_CMD_LENGTH_MAX - LR11XX_GNSS_ALMANAC_UPDATE_CMD_LENGTH ) / LR11XX_GNSS_SINGLE_ALMANAC_WRITE_SIZE ) +#define LR11XX_GNSS_READ_ALMANAC_TEMPBUFFER_SIZE_BYTE ( 47 ) +#define LR11XX_GNSS_MAX_DETECTED_SV ( 32 ) +#define LR11XX_GNSS_DETECTED_SV_SINGLE_LENGTH ( 4 ) +#define LR11XX_GNSS_MAX_DETECTED_SV_BUFFER_LENGTH \ + ( LR11XX_GNSS_MAX_DETECTED_SV * LR11XX_GNSS_DETECTED_SV_SINGLE_LENGTH ) +#define LR11XX_GNSS_READ_FIRMWARE_VERSION_RBUFFER_LENGTH ( 2 ) +#define LR11XX_GNSS_READ_TIME_RBUFFER_LENGTH ( 12 ) +#define LR11XX_GNSS_READ_WEEK_NUMBER_ROLLOVER_RBUFFER_LENGTH ( 2 ) +#define LR11XX_GNSS_READ_DEMOD_STATUS_RBUFFER_LENGTH ( 2 ) +#define LR11XX_GNSS_READ_CUMULATIVE_TIMING_RBUFFER_LENGTH ( 125 ) +#define LR11XX_GNSS_DOPPLER_SOLVER_RES_RBUFFER_LENGTH ( 18 ) +#define LR11XX_GNSS_READ_DELAY_RESET_AP_RBUFFER_LENGTH ( 3 ) +#define LR11XX_GNSS_READ_ALMANAC_STATUS_RBUFFER_LENGTH ( 53 ) +#define LR11XX_GNSS_READ_KEEP_SYNC_STATUS_RBUFFER_LENGTH ( 5 ) +#define LR11XX_GNSS_READ_ALMANAC_UPDATE_PERIOD_RBUFFER_LENGTH ( 2 ) + +#define LR11XX_GNSS_SCALING_LATITUDE 90 +#define LR11XX_GNSS_SCALING_LONGITUDE 180 +#define LR11XX_GNSS_SNR_TO_CNR_OFFSET ( 31 ) + +#define LR11XX_GNSS_SCAN_RESULT_DESTINATION_INDEX ( 0 ) + +/*! + * @brief GNSS scan power consumption + * + * @note these numbers are given for information, it should be modified according to the used hardware. + */ +#define LR11XX_GNSS_RADIO_ACQUISITION_GPS_UA_DCDC ( 15000 ) +#define LR11XX_GNSS_RADIO_ACQUISITION_BEIDOU_UA_DCDC ( 16500 ) +#define LR11XX_GNSS_COMPUTATION_UA_DCDC ( 3100 ) +#define LR11XX_GNSS_RADIO_ACQUISITION_GPS_UA_LDO ( 24500 ) +#define LR11XX_GNSS_RADIO_ACQUISITION_BEIDOU_UA_LDO ( 27300 ) +#define LR11XX_GNSS_COMPUTATION_UA_LDO ( 5000 ) +#define LR11XX_GNSS_LF_CLOCK_VALUE ( 32768 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for GNSS-related operations + */ +enum +{ + LR11XX_GNSS_READ_GNSS_RSSI_TEST_OC = 0x0222, + LR11XX_GNSS_SET_CONSTELLATION_OC = 0x0400, //!< Set the constellation to use + LR11XX_GNSS_READ_CONSTELLATION_OC = 0x0401, //!< Read the used constellations + LR11XX_GNSS_SET_ALMANAC_UPDATE_OC = 0x0402, //!< Set almanac update configuration + LR11XX_GNSS_READ_ALMANAC_UPDATE_OC = 0x0403, //!< Read the almanac update configuration + LR11XX_GNSS_SET_FREQ_SEARCH_SPACE_OC = 0x0404, //!< Set the frequency search space + LR11XX_GNSS_READ_FREQ_SEARCH_SPACE_OC = 0x0405, //!< Read the frequency search space + LR11XX_GNSS_READ_FW_VERSION_OC = 0x0406, //!< Read the firmware version + LR11XX_GNSS_READ_SUPPORTED_CONSTELLATION_OC = 0x0407, //!< Read the supported constellations + LR11XX_GNSS_SET_SCAN_MODE_OC = 0x0408, //!< Define single or double capture + LR11XX_GNSS_SCAN_OC = 0x040B, //!< Launch the scan + LR11XX_GNSS_SCAN_GET_RES_SIZE_OC = 0x040C, //!< Get the size of the output payload + LR11XX_GNSS_SCAN_READ_RES_OC = 0x040D, //!< Read the byte stream + LR11XX_GNSS_ALMANAC_UPDATE_OC = 0x040E, //!< Update the almanac + LR11XX_GNSS_ALMANAC_READ_OC = 0x040F, //!< Read all almanacs + LR11XX_GNSS_SET_ASSISTANCE_POSITION_OC = 0x0410, //!< Set the assistance position + LR11XX_GNSS_READ_ASSISTANCE_POSITION_OC = 0x0411, //!< Read the assistance position + LR11XX_GNSS_PUSH_SOLVER_MSG_OC = 0x0414, //!< Push messages coming from the solver + LR11XX_GNSS_PUSH_DM_MSG_OC = 0x0415, //!< Push messages coming from the device management + LR11XX_GNSS_GET_CONTEXT_STATUS_OC = 0x0416, //!< Read the context + LR11XX_GNSS_GET_NB_SATELLITES_OC = 0x0417, //!< Get the number of satellites detected during a scan + LR11XX_GNSS_GET_SATELLITES_OC = 0x0418, //!< Get the list of satellites detected during a scan + LR11XX_GNSS_READ_ALMANAC_PER_SATELLITE_OC = 0x041A, //!< Read the almanac of given satellites + LR11XX_GNSS_GET_SV_VISIBLE_OC = 0x041F, //!< Get the number of visible SV from a date and a position + LR11XX_GNSS_GET_SV_VISIBLE_DOPPLER_OC = 0x0420, //!< Get visible SV ID and corresponding doppler value + LR11XX_GNSS_READ_LAST_SCAN_MODE_LAUNCHED_OC = 0x0426, //!< Get the type of scan launched during the last scan + LR11XX_GNSS_FETCH_TIME_OC = 0x0432, //!< Start the time acquisition/domulation + LR11XX_GNSS_READ_TIME_OC = 0x0434, //!< Read time from LR11XX + LR11XX_GNSS_RESET_TIME_OC = 0x0435, //!< Reset the internal time + LR11XX_GNSS_RESET_POSITION_OC = 0x0437, //!< Reset the location and the history Doppler buffer + LR11XX_GNSS_READ_WEEK_NUMBER_ROLLOVER_OC = 0x0438, //!< Read the week number rollover + LR11XX_GNSS_READ_DEMOD_STATUS_OC = 0x0439, //!< Read demod status + LR11XX_GNSS_READ_CUMULATIVE_TIMING_OC = 0x044A, //!< Read cumulative timing + LR11XX_GNSS_SET_TIME_OC = 0x044B, //!< Set the GPS time + LR11XX_GNSS_CONFIG_DELAY_RESET_AP_OC = 0x044D, //!< Configures the time delay in sec + LR11XX_GNSS_READ_DOPPLER_SOLVER_RESULT_OC = + 0x044F, //!< Read the assisted position based on the internal doppler solver + LR11XX_GNSS_READ_DELAY_RESET_AP_OC = 0x0453, //!< Read the time delay in sec + LR11XX_GNSS_ALMANAC_UPDATE_FROM_SAT_OC = + 0x0454, //!< Launches one scan to download from satellite almanac parameters broadcasted + LR11XX_GNSS_READ_KEEP_SYNC_STATUS_OC = 0x0456, //!< Read the number of visible satellites and the time elapsed from + //!< last detected satellite list update + LR11XX_GNSS_READ_ALMANAC_STATUS_OC = 0x0457, //!< Returns the actual state of almanac GPS and Beidou. + LR11XX_GNSS_CONFIG_ALMANAC_UPDATE_PERIOD_OC = 0x0463, //!< Configures the almanac update period + LR11XX_GNSS_READ_ALMANAC_UPDATE_PERIOD_OC = 0x0464, //!< Read the almanac update period + LR11XX_GNSS_GET_SV_SYNC_OC = 0x0466, //!< Returns the list of satellite for the next keep sync scan + LR11XX_GNSS_WRITE_BIT_MASK_SAT_ACTIVATED_OC = + 0x0472, //!< Configures the ability of the LR11xx to search almanac for each satellites +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Helper function that convert an array of uint8_t into a uint32_t single value + * + * @warning It is up to the caller to ensure that value points to an array of at least sizeof(uint32_t) elements. + * + * @param [in] value Array of uint8_t to be translated into a uint32_t + * + * @returns 32-bit value + */ +static uint32_t lr11xx_gnss_uint8_to_uint32( uint8_t value[4] ); + +/*! + * @brief Returns the minimum of the operand given as parameter and the maximum allowed number of blocks + * + * @param [in] operand Size to compare + * + * @returns Minimum between operand and @ref LR11XX_GNSS_ALMANAC_UPDATE_MAX_NB_OF_BLOCKS + */ +static uint16_t lr11xx_gnss_get_min_from_operand_and_max_nb_of_blocks( uint16_t operand ); + +/*! + * @brief Get the almanac base address and size + * + * @param [in] context Chip implementation context + * @param [out] address Start address of the almanac in memory + * @param [out] size Size of the almanac in byte + * + * @returns Operation status + */ +static lr11xx_status_t lr11xx_gnss_get_almanac_address_size( const void* context, uint32_t* address, uint16_t* size ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_gnss_get_result_size( const void* context, uint16_t* result_size ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SCAN_GET_RES_SIZE_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_SCAN_GET_RES_SIZE_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_SCAN_GET_RES_SIZE_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( uint16_t )] = { 0 }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_SCAN_GET_RES_SIZE_CMD_LENGTH, rbuffer, sizeof( uint16_t ) ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *result_size = ( ( uint16_t ) rbuffer[0] << 8 ) + ( ( uint16_t ) rbuffer[1] ); + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_read_results( const void* context, uint8_t* result_buffer, + const uint16_t result_buffer_size ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SCAN_READ_RES_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_SCAN_READ_RES_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_SCAN_READ_RES_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_SCAN_READ_RES_CMD_LENGTH, result_buffer, + result_buffer_size ); +} + +lr11xx_status_t lr11xx_gnss_almanac_update( const void* context, const uint8_t* blocks, const uint8_t nb_of_blocks ) +{ + const uint8_t cbuffer[LR11XX_GNSS_ALMANAC_UPDATE_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_ALMANAC_UPDATE_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_ALMANAC_UPDATE_OC >> 0 ), + }; + + uint16_t remaining_nb_of_blocks = nb_of_blocks; + + while( remaining_nb_of_blocks > 0 ) + { + const uint16_t nb_of_blocks_to_write = + lr11xx_gnss_get_min_from_operand_and_max_nb_of_blocks( remaining_nb_of_blocks ); + + const uint8_t* blocks_to_write = + blocks + ( nb_of_blocks - remaining_nb_of_blocks ) * LR11XX_GNSS_SINGLE_ALMANAC_WRITE_SIZE; + + const lr11xx_hal_status_t status = + lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_ALMANAC_UPDATE_CMD_LENGTH, blocks_to_write, + nb_of_blocks_to_write * LR11XX_GNSS_SINGLE_ALMANAC_WRITE_SIZE ); + + if( status != LR11XX_HAL_STATUS_OK ) + { + return ( lr11xx_status_t ) status; + } + + remaining_nb_of_blocks -= nb_of_blocks_to_write; + } + + return LR11XX_STATUS_OK; +} + +lr11xx_status_t lr11xx_gnss_read_almanac( const void* context, + lr11xx_gnss_almanac_full_read_bytestream_t almanac_bytestream ) +{ + uint32_t almanac_address = 0; + uint16_t almanac_size = 0; + lr11xx_status_t status = lr11xx_gnss_get_almanac_address_size( context, &almanac_address, &almanac_size ); + if( status != LR11XX_STATUS_OK ) + { + return status; + } + + const uint8_t N_READ_ALMANAC_REGMEM32 = 15; + + for( uint8_t index_regmem32 = 0; index_regmem32 < N_READ_ALMANAC_REGMEM32; index_regmem32++ ) + { + const uint16_t local_bytestream_index_burst = + index_regmem32 * LR11XX_GNSS_READ_ALMANAC_TEMPBUFFER_SIZE_BYTE * 4; + uint32_t temporary_buffer[LR11XX_GNSS_READ_ALMANAC_TEMPBUFFER_SIZE_BYTE] = { 0 }; + + const lr11xx_status_t local_status = lr11xx_regmem_read_regmem32( + context, almanac_address, temporary_buffer, LR11XX_GNSS_READ_ALMANAC_TEMPBUFFER_SIZE_BYTE ); + if( local_status != LR11XX_STATUS_OK ) + { + return local_status; + } + + almanac_address += ( LR11XX_GNSS_READ_ALMANAC_TEMPBUFFER_SIZE_BYTE * 4 ); + + for( uint8_t index_local_temp = 0; index_local_temp < LR11XX_GNSS_READ_ALMANAC_TEMPBUFFER_SIZE_BYTE; + index_local_temp++ ) + { + const uint16_t local_bytestream_index = local_bytestream_index_burst + ( index_local_temp * 4 ); + almanac_bytestream[local_bytestream_index + 0] = ( uint8_t )( temporary_buffer[index_local_temp] >> 0 ); + almanac_bytestream[local_bytestream_index + 1] = ( uint8_t )( temporary_buffer[index_local_temp] >> 8 ); + almanac_bytestream[local_bytestream_index + 2] = ( uint8_t )( temporary_buffer[index_local_temp] >> 16 ); + almanac_bytestream[local_bytestream_index + 3] = ( uint8_t )( temporary_buffer[index_local_temp] >> 24 ); + } + } + return status; +} + +lr11xx_status_t lr11xx_gnss_get_almanac_age_for_satellite( const void* context, const lr11xx_gnss_satellite_id_t sv_id, + uint16_t* almanac_age ) +{ + uint32_t almanac_base_address = 0; + uint16_t almanac_size = 0; + const lr11xx_status_t status_get_almanac_address_size = + lr11xx_gnss_get_almanac_address_size( context, &almanac_base_address, &almanac_size ); + + if( status_get_almanac_address_size != LR11XX_STATUS_OK ) + { + return status_get_almanac_address_size; + } + + const uint16_t offset_almanac_date = sv_id * LR11XX_GNSS_SINGLE_ALMANAC_READ_SIZE + 1; + uint8_t raw_almanac_date[LR11XX_GNSS_ALMANAC_DATE_LENGTH] = { 0 }; + + const lr11xx_status_t status_read_mem = lr11xx_regmem_read_mem8( + context, almanac_base_address + offset_almanac_date, raw_almanac_date, LR11XX_GNSS_ALMANAC_DATE_LENGTH ); + if( status_read_mem == LR11XX_STATUS_OK ) + { + // Note: the memory on LR11XX is LSB first. As the 2-byte wide almanac age is obtained by calling the _mem8, the + // conversion to uint16_t here is done LSB first + ( *almanac_age ) = ( ( ( uint16_t ) raw_almanac_date[1] ) << 8 ) + ( ( ( uint16_t ) raw_almanac_date[0] ) ); + } + return status_read_mem; +} + +lr11xx_status_t lr11xx_gnss_get_almanac_address_size( const void* context, uint32_t* address, uint16_t* size ) +{ + const uint8_t cbuffer[LR11XX_GNSS_ALMANAC_READ_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_ALMANAC_READ_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_ALMANAC_READ_OC >> 0 ), + }; + uint8_t rbuffer[LR11XX_GNSS_ALMANAC_READ_RBUFFER_LENGTH] = { 0 }; + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_ALMANAC_READ_CMD_LENGTH, + rbuffer, LR11XX_GNSS_ALMANAC_READ_RBUFFER_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *address = lr11xx_gnss_uint8_to_uint32( &rbuffer[0] ); + *size = ( ( ( uint16_t ) rbuffer[4] ) << 8 ) | ( ( uint16_t ) rbuffer[5] ); + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_push_solver_msg( const void* context, const uint8_t* payload, const uint16_t payload_size ) +{ + const uint8_t cbuffer[LR11XX_GNSS_PUSH_SOLVER_MSG_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_PUSH_SOLVER_MSG_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_PUSH_SOLVER_MSG_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_PUSH_SOLVER_MSG_CMD_LENGTH, payload, + payload_size ); +} + +lr11xx_status_t lr11xx_gnss_set_constellations_to_use( const void* context, + const lr11xx_gnss_constellation_mask_t constellation_to_use ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SET_CONSTELLATION_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_SET_CONSTELLATION_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_SET_CONSTELLATION_OC >> 0 ), + ( uint8_t ) constellation_to_use, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_SET_CONSTELLATION_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_read_used_constellations( const void* context, + lr11xx_gnss_constellation_mask_t* constellations_used ) +{ + const uint8_t cbuffer[LR11XX_GNSS_READ_CONSTELLATION_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_CONSTELLATION_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_CONSTELLATION_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_CONSTELLATION_CMD_LENGTH, + constellations_used, sizeof( *constellations_used ) ); +} + +lr11xx_status_t lr11xx_gnss_set_almanac_update( const void* context, + const lr11xx_gnss_constellation_mask_t constellations_to_update ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SET_ALMANAC_UPDATE_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_SET_ALMANAC_UPDATE_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_SET_ALMANAC_UPDATE_OC >> 0 ), + ( uint8_t ) constellations_to_update, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_SET_ALMANAC_UPDATE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_read_almanac_update( const void* context, + lr11xx_gnss_constellation_mask_t* constellations_to_update ) +{ + const uint8_t cbuffer[LR11XX_GNSS_READ_ALMANAC_UPDATE_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_ALMANAC_UPDATE_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_ALMANAC_UPDATE_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_ALMANAC_UPDATE_CMD_LENGTH, + constellations_to_update, sizeof( *constellations_to_update ) ); +} + +lr11xx_status_t lr11xx_gnss_set_freq_search_space( const void* radio, + const lr11xx_gnss_freq_search_space_t freq_search_space ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SET_FREQ_SEARCH_SPACE_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_SET_FREQ_SEARCH_SPACE_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_SET_FREQ_SEARCH_SPACE_OC >> 0 ), + ( uint8_t ) freq_search_space, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( radio, cbuffer, LR11XX_GNSS_SET_FREQ_SEARCH_SPACE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_read_freq_search_space( const void* radio, + lr11xx_gnss_freq_search_space_t* freq_search_space ) +{ + const uint8_t cbuffer[LR11XX_GNSS_READ_FREQ_SEARCH_SPACE_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_FREQ_SEARCH_SPACE_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_FREQ_SEARCH_SPACE_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( radio, cbuffer, LR11XX_GNSS_READ_FREQ_SEARCH_SPACE_CMD_LENGTH, + ( uint8_t* ) freq_search_space, sizeof( uint8_t ) ); +} + +lr11xx_status_t lr11xx_gnss_read_firmware_version( const void* context, lr11xx_gnss_version_t* version ) +{ + const uint8_t cbuffer[LR11XX_GNSS_READ_FW_VERSION_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_FW_VERSION_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_FW_VERSION_OC >> 0 ), + }; + uint8_t rbuffer[LR11XX_GNSS_READ_FIRMWARE_VERSION_RBUFFER_LENGTH] = { 0 }; + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_FW_VERSION_CMD_LENGTH, + rbuffer, LR11XX_GNSS_READ_FIRMWARE_VERSION_RBUFFER_LENGTH ); + + version->gnss_firmware = rbuffer[0]; + version->gnss_almanac = rbuffer[1]; + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_read_supported_constellations( const void* context, + lr11xx_gnss_constellation_mask_t* supported_constellations ) +{ + const uint8_t cbuffer[LR11XX_GNSS_READ_SUPPORTED_CONSTELLATION_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_SUPPORTED_CONSTELLATION_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_SUPPORTED_CONSTELLATION_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_SUPPORTED_CONSTELLATION_CMD_LENGTH, + supported_constellations, sizeof( *supported_constellations ) ); +} + +lr11xx_status_t lr11xx_gnss_set_scan_mode( const void* context, const lr11xx_gnss_scan_mode_t scan_mode ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SET_SCAN_MODE_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_SET_SCAN_MODE_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_SET_SCAN_MODE_OC >> 0 ), + ( uint8_t ) scan_mode, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_SET_SCAN_MODE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_scan( const void* context, const lr11xx_gnss_search_mode_t effort_mode, + const uint8_t gnss_input_parameters, const uint8_t nb_sat ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SCAN_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_SCAN_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_SCAN_OC >> 0 ), + ( uint8_t ) effort_mode, + gnss_input_parameters, + nb_sat, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_SCAN_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_set_assistance_position( + const void* context, const lr11xx_gnss_solver_assistance_position_t* assistance_position ) +{ + const int16_t latitude = ( ( assistance_position->latitude * 2048 ) / LR11XX_GNSS_SCALING_LATITUDE ); + const int16_t longitude = ( ( assistance_position->longitude * 2048 ) / LR11XX_GNSS_SCALING_LONGITUDE ); + const uint8_t cbuffer[LR11XX_GNSS_SET_ASSISTANCE_POSITION_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_SET_ASSISTANCE_POSITION_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_SET_ASSISTANCE_POSITION_OC >> 0 ), + ( uint8_t )( latitude >> 8 ), + ( uint8_t )( latitude >> 0 ), + ( uint8_t )( longitude >> 8 ), + ( uint8_t )( longitude >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_SET_ASSISTANCE_POSITION_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_gnss_read_assistance_position( const void* context, + lr11xx_gnss_solver_assistance_position_t* assistance_position ) +{ + uint8_t position_buffer[4] = { 0x00 }; + int16_t position_tmp; + const uint8_t cbuffer[LR11XX_GNSS_READ_ASSISTANCE_POSITION_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_ASSISTANCE_POSITION_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_ASSISTANCE_POSITION_OC >> 0 ), + }; + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( + context, cbuffer, LR11XX_GNSS_READ_ASSISTANCE_POSITION_CMD_LENGTH, position_buffer, sizeof( position_buffer ) ); + + position_tmp = ( int16_t )( ( ( uint16_t ) position_buffer[0] << 8 ) + position_buffer[1] ); + assistance_position->latitude = ( ( float ) ( position_tmp ) *LR11XX_GNSS_SCALING_LATITUDE ) / 2048; + + position_tmp = ( int16_t )( ( ( uint16_t ) position_buffer[2] << 8 ) + position_buffer[3] ); + assistance_position->longitude = ( ( float ) ( position_tmp ) *LR11XX_GNSS_SCALING_LONGITUDE ) / 2048; + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_push_dmc_msg( const void* context, uint8_t* dmc_msg, uint16_t dmc_msg_len ) +{ + const uint8_t cbuffer[LR11XX_GNSS_PUSH_DM_MSG_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_PUSH_DM_MSG_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_PUSH_DM_MSG_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_PUSH_DM_MSG_CMD_LENGTH, dmc_msg, + dmc_msg_len ); +} + +lr11xx_status_t lr11xx_gnss_get_context_status( const void* context, + lr11xx_gnss_context_status_bytestream_t context_status ) +{ + const uint8_t cbuffer[LR11XX_GNSS_GET_CONTEXT_STATUS_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_GET_CONTEXT_STATUS_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_GET_CONTEXT_STATUS_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_GET_CONTEXT_STATUS_CMD_LENGTH, + context_status, LR11XX_GNSS_CONTEXT_STATUS_LENGTH ); +} + +lr11xx_status_t lr11xx_gnss_get_nb_detected_satellites( const void* context, uint8_t* nb_detected_satellites ) +{ + const uint8_t cbuffer[LR11XX_GNSS_GET_NB_SV_SATELLITES_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_GET_NB_SATELLITES_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_GET_NB_SATELLITES_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_GET_NB_SV_SATELLITES_CMD_LENGTH, + nb_detected_satellites, 1 ); +} + +lr11xx_status_t lr11xx_gnss_get_detected_satellites( + const void* context, const uint8_t nb_detected_satellites, + lr11xx_gnss_detected_satellite_t* detected_satellite_id_snr_doppler ) +{ + const uint8_t max_satellites_to_fetch = + ( LR11XX_GNSS_MAX_DETECTED_SV > nb_detected_satellites ) ? nb_detected_satellites : LR11XX_GNSS_MAX_DETECTED_SV; + const uint16_t read_size = max_satellites_to_fetch * LR11XX_GNSS_DETECTED_SV_SINGLE_LENGTH; + uint8_t result_buffer[LR11XX_GNSS_MAX_DETECTED_SV_BUFFER_LENGTH] = { 0 }; + + const uint8_t cbuffer[LR11XX_GNSS_GET_SV_SATELLITES_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_GET_SATELLITES_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_GET_SATELLITES_OC >> 0 ), + }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_GET_SV_SATELLITES_CMD_LENGTH, result_buffer, read_size ); + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + for( uint8_t index_satellite = 0; index_satellite < max_satellites_to_fetch; index_satellite++ ) + { + const uint16_t local_result_buffer_index = index_satellite * LR11XX_GNSS_DETECTED_SV_SINGLE_LENGTH; + lr11xx_gnss_detected_satellite_t* local_satellite_result = + &detected_satellite_id_snr_doppler[index_satellite]; + + local_satellite_result->satellite_id = result_buffer[local_result_buffer_index]; + local_satellite_result->cnr = result_buffer[local_result_buffer_index + 1] + LR11XX_GNSS_SNR_TO_CNR_OFFSET; + local_satellite_result->doppler = ( int16_t )( ( result_buffer[local_result_buffer_index + 2] << 8 ) + + ( result_buffer[local_result_buffer_index + 3] ) ); + } + } + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_read_almanac_per_satellites( const void* context, uint8_t sv_id_init, uint8_t n_sv, + uint8_t* almanacs ) +{ + const uint8_t cbuffer[LR11XX_GNSS_READ_ALMANAC_PER_SATELLITE_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_ALMANAC_PER_SATELLITE_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_ALMANAC_PER_SATELLITE_OC >> 0 ), sv_id_init, n_sv + }; + + const uint16_t n_bytes_to_read = n_sv * LR11XX_GNSS_SINGLE_ALMANAC_READ_SIZE; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_ALMANAC_PER_SATELLITE_CMD_LENGTH, + almanacs, n_bytes_to_read ); +} + +lr11xx_status_t lr11xx_gnss_read_gnss_rssi_test( const void* context, int8_t* rssi_gnss_dbm ) +{ + const uint8_t gnss_rssi_path_test = 0x09; + const uint8_t cbuffer[LR11XX_GNSS_READ_GNSS_RSSI_TEST_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_GNSS_RSSI_TEST_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_GNSS_RSSI_TEST_OC >> 0 ), + gnss_rssi_path_test, + 0, + }; + + uint8_t rssi_gnss_raw[LR11XX_GNSS_READ_GNSS_RSSI_TEST_READ_RBUFFER_LENGTH] = { 0 }; + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_GNSS_RSSI_TEST_CMD_LENGTH, rssi_gnss_raw, + LR11XX_GNSS_READ_GNSS_RSSI_TEST_READ_RBUFFER_LENGTH ); + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *rssi_gnss_dbm = rssi_gnss_raw[1]; + } + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_parse_context_status_buffer( + const lr11xx_gnss_context_status_bytestream_t context_status_bytestream, + lr11xx_gnss_context_status_t* context_status ) +{ + lr11xx_status_t status = LR11XX_STATUS_ERROR; + + if( ( ( lr11xx_gnss_destination_t ) context_status_bytestream[0] == LR11XX_GNSS_DESTINATION_DMC ) && + ( ( lr11xx_gnss_message_dmc_opcode_t ) context_status_bytestream[1] == LR11XX_GNSS_DMC_STATUS ) ) + { + context_status->firmware_version = context_status_bytestream[2]; + + context_status->global_almanac_crc = + ( ( uint32_t ) context_status_bytestream[3] ) + ( ( uint32_t ) context_status_bytestream[4] << 8 ) + + ( ( uint32_t ) context_status_bytestream[5] << 16 ) + ( ( uint32_t ) context_status_bytestream[6] << 24 ); + + context_status->error_code = ( lr11xx_gnss_error_code_t )( context_status_bytestream[7] >> 4 ); + + context_status->almanac_update_gps = + ( ( context_status_bytestream[7] & LR11XX_GNSS_DMC_ALMANAC_UPDATE_GPS_MASK ) != 0 ) ? true : false; + + context_status->almanac_update_beidou = + ( ( context_status_bytestream[7] & LR11XX_GNSS_DMC_ALMANAC_UPDATE_BEIDOU_MASK ) != 0 ) ? true : false; + + context_status->freq_search_space = ( lr11xx_gnss_freq_search_space_t )( + ( ( ( context_status_bytestream[7] & LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_MSB_MASK ) >> + LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_MSB_POS ) + << 1 ) + + ( ( context_status_bytestream[8] & LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_LSB_MASK ) >> + LR11XX_GNSS_DMC_FREQUENCY_SEARCH_SPACE_LSB_POS ) ); + + status = LR11XX_STATUS_OK; + } + + return status; +} + +lr11xx_status_t lr11xx_gnss_get_result_destination( const uint8_t* result_buffer, const uint16_t result_buffer_size, + lr11xx_gnss_destination_t* destination ) +{ + lr11xx_status_t status = LR11XX_STATUS_ERROR; + + if( result_buffer_size != 0 ) + { + switch( result_buffer[LR11XX_GNSS_SCAN_RESULT_DESTINATION_INDEX] ) + { + case LR11XX_GNSS_DESTINATION_HOST: + { + *destination = LR11XX_GNSS_DESTINATION_HOST; + status = LR11XX_STATUS_OK; + break; + } + case LR11XX_GNSS_DESTINATION_SOLVER: + { + *destination = LR11XX_GNSS_DESTINATION_SOLVER; + status = LR11XX_STATUS_OK; + break; + } + case LR11XX_GNSS_DESTINATION_DMC: + { + *destination = LR11XX_GNSS_DESTINATION_DMC; + status = LR11XX_STATUS_OK; + break; + } + } + } + + return status; +} + +uint16_t lr11xx_gnss_compute_almanac_age( uint16_t almanac_date, + uint16_t nb_days_between_epoch_and_last_gps_time_rollover, + uint16_t nb_days_since_epoch ) +{ + return nb_days_since_epoch - ( almanac_date + nb_days_between_epoch_and_last_gps_time_rollover ); +} + +lr11xx_status_t lr11xx_gnss_get_nb_visible_satellites( + const void* context, const lr11xx_gnss_date_t date, + const lr11xx_gnss_solver_assistance_position_t* assistance_position, + const lr11xx_gnss_constellation_t constellation, uint8_t* nb_visible_sv ) +{ + const int16_t latitude = ( ( assistance_position->latitude * 2048 ) / LR11XX_GNSS_SCALING_LATITUDE ); + const int16_t longitude = ( ( assistance_position->longitude * 2048 ) / LR11XX_GNSS_SCALING_LONGITUDE ); + const uint8_t cbuffer[LR11XX_GNSS_GET_SV_VISIBLE_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_GET_SV_VISIBLE_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_GET_SV_VISIBLE_OC >> 0 ), + ( uint8_t )( date >> 24 ), + ( uint8_t )( date >> 16 ), + ( uint8_t )( date >> 8 ), + ( uint8_t )( date >> 0 ), + ( uint8_t )( latitude >> 8 ), + ( uint8_t )( latitude >> 0 ), + ( uint8_t )( longitude >> 8 ), + ( uint8_t )( longitude >> 0 ), + ( uint8_t )( constellation - 1 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_GET_SV_VISIBLE_CMD_LENGTH, nb_visible_sv, + 1 ); +} + +lr11xx_status_t lr11xx_gnss_get_visible_satellites( const void* context, const uint8_t nb_visible_satellites, + lr11xx_gnss_visible_satellite_t* visible_satellite_id_doppler ) +{ + uint8_t result_buffer[12 * 5] = { 0 }; + const uint16_t read_size = nb_visible_satellites * 5; + + const uint8_t cbuffer[LR11XX_GNSS_GET_SV_VISIBLE_DOPPLER_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_GET_SV_VISIBLE_DOPPLER_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_GET_SV_VISIBLE_DOPPLER_OC >> 0 ), + }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_GET_SV_VISIBLE_DOPPLER_CMD_LENGTH, result_buffer, read_size ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + for( uint8_t index_satellite = 0; index_satellite < nb_visible_satellites; index_satellite++ ) + { + const uint16_t local_result_buffer_index = index_satellite * 5; + lr11xx_gnss_visible_satellite_t* local_satellite_result = &visible_satellite_id_doppler[index_satellite]; + + local_satellite_result->satellite_id = result_buffer[local_result_buffer_index]; + local_satellite_result->doppler = ( int16_t )( ( result_buffer[local_result_buffer_index + 1] << 8 ) + + ( result_buffer[local_result_buffer_index + 2] ) ); + local_satellite_result->doppler_error = ( int16_t )( ( result_buffer[local_result_buffer_index + 3] << 8 ) + + ( result_buffer[local_result_buffer_index + 4] ) ); + } + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_fetch_time( const void* context, const lr11xx_gnss_search_mode_t effort_mode, + const lr11xx_gnss_fetch_time_option_t option ) +{ + const uint8_t cbuffer[LR11XX_GNSS_FETCH_TIME_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_FETCH_TIME_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_FETCH_TIME_OC >> 0 ), + effort_mode, + option, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_FETCH_TIME_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_read_time( const void* context, lr11xx_gnss_time_t* time ) +{ + uint8_t rbuffer[LR11XX_GNSS_READ_TIME_RBUFFER_LENGTH] = { 0x00 }; + const uint8_t cbuffer[LR11XX_GNSS_READ_TIME_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_TIME_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_TIME_OC >> 0 ), + }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_TIME_CMD_LENGTH, rbuffer, sizeof( rbuffer ) ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + time->error_code = ( lr11xx_gnss_read_time_status_t ) rbuffer[0]; + time->gps_time_s = lr11xx_gnss_uint8_to_uint32( &rbuffer[1] ); + time->nb_us_in_s = + ( uint32_t )( rbuffer[5] << 16 ) | ( uint32_t )( rbuffer[6] << 8 ) | ( uint32_t )( rbuffer[7] ); + time->nb_us_in_s /= 16; + time->time_accuracy = lr11xx_gnss_uint8_to_uint32( &rbuffer[8] ); + time->time_accuracy /= 16; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_reset_time( const void* context ) +{ + const uint8_t cbuffer[LR11XX_GNSS_RESET_TIME_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_RESET_TIME_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_RESET_TIME_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_RESET_TIME_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_reset_position( const void* context ) +{ + const uint8_t cbuffer[LR11XX_GNSS_RESET_POSITION_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_RESET_POSITION_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_RESET_POSITION_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_RESET_POSITION_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_read_week_number_rollover( const void* context, + lr11xx_gnss_week_number_rollover_status_t* wn_rollover_status, + uint8_t* wn_number_rollover ) +{ + uint8_t rbuffer[LR11XX_GNSS_READ_WEEK_NUMBER_ROLLOVER_RBUFFER_LENGTH] = { 0x00 }; + const uint8_t cbuffer[LR11XX_GNSS_READ_WEEK_NUMBER_ROLLOVER_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_WEEK_NUMBER_ROLLOVER_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_WEEK_NUMBER_ROLLOVER_OC >> 0 ), + }; + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( + context, cbuffer, LR11XX_GNSS_READ_WEEK_NUMBER_ROLLOVER_CMD_LENGTH, rbuffer, sizeof( rbuffer ) ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *wn_rollover_status = ( lr11xx_gnss_week_number_rollover_status_t ) rbuffer[0]; + *wn_number_rollover = rbuffer[1]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_read_demod_status( const void* context, lr11xx_gnss_demod_status_t* demod_status, + lr11xx_gnss_demod_info_t* demod_info ) +{ + uint8_t rbuffer[LR11XX_GNSS_READ_DEMOD_STATUS_RBUFFER_LENGTH] = { 0x00 }; + const uint8_t cbuffer[LR11XX_GNSS_READ_DEMOD_STATUS_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_DEMOD_STATUS_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_DEMOD_STATUS_OC >> 0 ), + }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_DEMOD_STATUS_CMD_LENGTH, rbuffer, sizeof( rbuffer ) ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *demod_status = ( lr11xx_gnss_demod_status_t ) rbuffer[0]; + demod_info->word_sync_found = rbuffer[1] & 0x01; + demod_info->first_tow_found = ( rbuffer[1] >> 1 ) & 0x01; + demod_info->wn_demodulated = ( rbuffer[1] >> 2 ) & 0x01; + demod_info->wn_found = ( rbuffer[1] >> 3 ) & 0x01; + demod_info->sub1_found = ( rbuffer[1] >> 4 ) & 0x01; + demod_info->sub4_found = ( rbuffer[1] >> 5 ) & 0x01; + demod_info->sub5_found = ( rbuffer[1] >> 6 ) & 0x01; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_read_cumulative_timing( const void* context, + lr11xx_gnss_cumulative_timing_t* cumulative_timing ) +{ + uint8_t rbuffer[LR11XX_GNSS_READ_CUMULATIVE_TIMING_RBUFFER_LENGTH] = { 0x00 }; + const uint8_t cbuffer[LR11XX_GNSS_READ_CUMULATIVE_TIMING_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_CUMULATIVE_TIMING_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_CUMULATIVE_TIMING_OC >> 0 ), + }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_CUMULATIVE_TIMING_CMD_LENGTH, rbuffer, sizeof( rbuffer ) ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + cumulative_timing->init = lr11xx_gnss_uint8_to_uint32( &rbuffer[0] ); + cumulative_timing->phase1_gps_capture = lr11xx_gnss_uint8_to_uint32( &rbuffer[4] ); + cumulative_timing->phase1_gps_process = lr11xx_gnss_uint8_to_uint32( &rbuffer[8] ); + cumulative_timing->multiscan_gps_capture = lr11xx_gnss_uint8_to_uint32( &rbuffer[12] ); + cumulative_timing->multiscan_gps_process = lr11xx_gnss_uint8_to_uint32( &rbuffer[16] ); + cumulative_timing->multiscan_gps_sleep_32k = lr11xx_gnss_uint8_to_uint32( &rbuffer[20] ); + cumulative_timing->phase1_beidou_capture = lr11xx_gnss_uint8_to_uint32( &rbuffer[24] ); + cumulative_timing->phase1_beidou_process = lr11xx_gnss_uint8_to_uint32( &rbuffer[28] ); + cumulative_timing->multiscan_beidou_capture = lr11xx_gnss_uint8_to_uint32( &rbuffer[32] ); + cumulative_timing->multiscan_beidou_process = lr11xx_gnss_uint8_to_uint32( &rbuffer[36] ); + cumulative_timing->multiscan_beidou_sleep_32k = lr11xx_gnss_uint8_to_uint32( &rbuffer[40] ); + cumulative_timing->demod_capture = lr11xx_gnss_uint8_to_uint32( &rbuffer[44] ); + cumulative_timing->demod_process = lr11xx_gnss_uint8_to_uint32( &rbuffer[48] ); + cumulative_timing->demod_sleep_32k = lr11xx_gnss_uint8_to_uint32( &rbuffer[52] ); + cumulative_timing->demod_sleep_32m = lr11xx_gnss_uint8_to_uint32( &rbuffer[56] ); + cumulative_timing->total_gps_capture = lr11xx_gnss_uint8_to_uint32( &rbuffer[60] ); + cumulative_timing->total_gps_process = lr11xx_gnss_uint8_to_uint32( &rbuffer[64] ); + cumulative_timing->total_gps_sleep_32k = lr11xx_gnss_uint8_to_uint32( &rbuffer[68] ); + cumulative_timing->total_gps_sleep_32m = lr11xx_gnss_uint8_to_uint32( &rbuffer[72] ); + cumulative_timing->total_gps = lr11xx_gnss_uint8_to_uint32( &rbuffer[76] ); + cumulative_timing->total_beidou_capture = lr11xx_gnss_uint8_to_uint32( &rbuffer[80] ); + cumulative_timing->total_beidou_process = lr11xx_gnss_uint8_to_uint32( &rbuffer[84] ); + cumulative_timing->total_beidou_sleep_32k = lr11xx_gnss_uint8_to_uint32( &rbuffer[88] ); + cumulative_timing->total_beidou_sleep_32m = lr11xx_gnss_uint8_to_uint32( &rbuffer[92] ); + cumulative_timing->total_beidou = lr11xx_gnss_uint8_to_uint32( &rbuffer[96] ); + cumulative_timing->total_capture = lr11xx_gnss_uint8_to_uint32( &rbuffer[100] ); + cumulative_timing->total_process = lr11xx_gnss_uint8_to_uint32( &rbuffer[104] ); + cumulative_timing->total_sleep_32k = lr11xx_gnss_uint8_to_uint32( &rbuffer[108] ); + cumulative_timing->total_sleep_32m = lr11xx_gnss_uint8_to_uint32( &rbuffer[112] ); + cumulative_timing->total = lr11xx_gnss_uint8_to_uint32( &rbuffer[116] ); + cumulative_timing->last_capture_size_32k_cnt = lr11xx_gnss_uint8_to_uint32( &rbuffer[120] ); + cumulative_timing->constellation_demod = rbuffer[124]; + } + + return ( lr11xx_status_t ) hal_status; +} + +void lr11xx_gnss_compute_power_consumption( + const lr11xx_gnss_cumulative_timing_t* cumulative_timing, + const lr11xx_gnss_instantaneous_power_consumption_ua_t* instantaneous_power_consumption_ua, + uint32_t* power_consumption_nah, uint32_t* power_consumption_nwh ) +{ + float e_init = + ( ( float ) instantaneous_power_consumption_ua->init_ua / 1000.0 * ( float ) cumulative_timing->init ) / + 32768.0; + + float e_gps_capture = ( ( ( float ) instantaneous_power_consumption_ua->phase1_gps_capture_ua / 1000.0 * + ( float ) cumulative_timing->phase1_gps_capture ) + + ( ( float ) instantaneous_power_consumption_ua->multiscan_gps_capture_ua / 1000.0 * + ( float ) cumulative_timing->multiscan_gps_capture ) ) / + 32768.0; + + float e_gps_process = ( ( ( float ) instantaneous_power_consumption_ua->phase1_gps_process_ua / 1000.0 * + ( float ) cumulative_timing->phase1_gps_process ) + + ( ( float ) instantaneous_power_consumption_ua->multiscan_gps_process_ua / 1000.0 * + ( float ) cumulative_timing->multiscan_gps_process ) ) / + 32768.0; + + float e_gps_sleep_32k = ( ( float ) instantaneous_power_consumption_ua->sleep_32k_ua / 1000.0 * + ( float ) cumulative_timing->multiscan_gps_sleep_32k ) / + 32768.0; + + float e_tot_gps = e_gps_capture + e_gps_process + e_gps_sleep_32k; + + float e_beidou_capture = ( ( ( float ) instantaneous_power_consumption_ua->phase1_beidou_capture_ua / 1000.0 * + ( float ) cumulative_timing->phase1_beidou_capture ) + + ( ( float ) instantaneous_power_consumption_ua->multiscan_beidou_capture_ua / 1000.0 * + ( float ) cumulative_timing->multiscan_beidou_capture ) ) / + 32768.0; + float e_beidou_process = ( ( ( float ) instantaneous_power_consumption_ua->phase1_beidou_process_ua / 1000.0 * + ( float ) cumulative_timing->phase1_beidou_process ) + + ( ( float ) instantaneous_power_consumption_ua->multiscan_beidou_process_ua / 1000.0 * + ( float ) cumulative_timing->multiscan_beidou_process ) ) / + 32768.0; + float e_beidou_sleep_32k = + ( instantaneous_power_consumption_ua->sleep_32k_ua / 1000.0 * cumulative_timing->multiscan_beidou_sleep_32k ) / + 32768.0; + + float e_tot_beidou = e_beidou_capture + e_beidou_process + e_beidou_sleep_32k; + + float e_demod = 0; + if( cumulative_timing->constellation_demod == 0 ) // Demodulation has been done on GPS constellation + { + e_demod = ( ( ( float ) instantaneous_power_consumption_ua->multiscan_gps_capture_ua / 1000.0 * + ( float ) cumulative_timing->demod_capture ) + + ( ( float ) instantaneous_power_consumption_ua->multiscan_gps_process_ua / 1000.0 * + ( float ) cumulative_timing->demod_process ) + + ( ( float ) instantaneous_power_consumption_ua->sleep_32k_ua / 1000.0 * + ( float ) cumulative_timing->demod_sleep_32k ) + + ( ( float ) instantaneous_power_consumption_ua->demod_sleep_32m_ua / 1000.0 * + ( float ) cumulative_timing->demod_sleep_32m ) ) / + 32768.0; + } + else // Domulation has been done on Beidou constellation + { + e_demod = ( ( ( float ) instantaneous_power_consumption_ua->multiscan_beidou_capture_ua / 1000.0 * + ( float ) cumulative_timing->demod_capture ) + + ( ( float ) instantaneous_power_consumption_ua->multiscan_beidou_process_ua / 1000.0 * + ( float ) cumulative_timing->demod_process ) + + ( ( float ) instantaneous_power_consumption_ua->sleep_32k_ua / 1000.0 * + ( float ) cumulative_timing->demod_sleep_32k ) + + ( ( float ) instantaneous_power_consumption_ua->demod_sleep_32m_ua / 1000.0 * + ( float ) cumulative_timing->demod_sleep_32m ) ) / + 32768.0; + } + + float power_consumption_uah_tmp = ( ( e_init + e_tot_gps + e_tot_beidou + e_demod ) * 1000 ) / + ( 3600.0 - ( ( float ) cumulative_timing->total / 32768.0 ) ); + + *power_consumption_nah = ( uint32_t )( power_consumption_uah_tmp * 1000 ); + *power_consumption_nwh = + ( uint32_t )( power_consumption_uah_tmp * instantaneous_power_consumption_ua->board_voltage_mv ); +} + +lr11xx_status_t lr11xx_gnss_set_time( const void* context, const uint32_t time, const uint16_t time_accuracy ) +{ + const uint8_t cbuffer[LR11XX_GNSS_SET_TIME_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_SET_TIME_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_SET_TIME_OC >> 0 ), + ( uint8_t )( time >> 24 ), + ( uint8_t )( time >> 16 ), + ( uint8_t )( time >> 8 ), + ( uint8_t ) time, + ( uint8_t )( time_accuracy >> 8 ), + ( uint8_t ) time_accuracy, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_SET_TIME_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_config_delay_reset_assistance_position( const void* context, const uint32_t delay ) +{ + const uint8_t cbuffer[LR11XX_GNSS_CONFIG_DELAY_RESET_AP_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_CONFIG_DELAY_RESET_AP_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_CONFIG_DELAY_RESET_AP_OC >> 0 ), + ( uint8_t )( delay >> 16 ), + ( uint8_t )( delay >> 8 ), + ( uint8_t ) delay, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_CONFIG_DELAY_RESET_AP_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_read_doppler_solver_result( const void* context, + lr11xx_gnss_doppler_solver_result_t* doppler_solver_result ) +{ + uint8_t rbuffer[LR11XX_GNSS_DOPPLER_SOLVER_RES_RBUFFER_LENGTH] = { 0x00 }; + const uint8_t cbuffer[LR11XX_GNSS_READ_DOPPLER_SOLVER_RESULT_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_DOPPLER_SOLVER_RESULT_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_DOPPLER_SOLVER_RESULT_OC >> 0 ), + }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_DOPPLER_SOLVER_RESULT_CMD_LENGTH, rbuffer, + LR11XX_GNSS_DOPPLER_SOLVER_RES_RBUFFER_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + doppler_solver_result->error_code = ( lr11xx_gnss_doppler_solver_error_code_t ) rbuffer[0]; + doppler_solver_result->nb_sv_used = rbuffer[1]; + doppler_solver_result->one_shot_latitude = ( uint16_t )( rbuffer[2] << 8 ) | ( uint16_t )( rbuffer[3] ); + doppler_solver_result->one_shot_longitude = ( uint16_t )( rbuffer[4] << 8 ) | ( uint16_t )( rbuffer[5] ); + doppler_solver_result->one_shot_accuracy = ( uint16_t )( rbuffer[6] << 8 ) | ( uint16_t )( rbuffer[7] ); + doppler_solver_result->one_shot_xtal_ppb = ( uint16_t )( rbuffer[8] << 8 ) | ( uint16_t )( rbuffer[9] ); + doppler_solver_result->filtered_latitude = ( uint16_t )( rbuffer[10] << 8 ) | ( uint16_t )( rbuffer[11] ); + doppler_solver_result->filtered_longitude = ( uint16_t )( rbuffer[12] << 8 ) | ( uint16_t )( rbuffer[13] ); + doppler_solver_result->one_shot_accuracy = ( uint16_t )( rbuffer[14] << 8 ) | ( uint16_t )( rbuffer[15] ); + doppler_solver_result->one_shot_xtal_ppb = ( uint16_t )( rbuffer[16] << 8 ) | ( uint16_t )( rbuffer[17] ); + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_read_delay_reset_assistance_position( const void* context, uint32_t* delay ) +{ + uint8_t rbuffer[LR11XX_GNSS_READ_DELAY_RESET_AP_RBUFFER_LENGTH] = { 0x00 }; + const uint8_t cbuffer[LR11XX_GNSS_CONFIG_READ_RESET_AP_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_DELAY_RESET_AP_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_DELAY_RESET_AP_OC >> 0 ), + }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_CONFIG_READ_RESET_AP_CMD_LENGTH, rbuffer, + LR11XX_GNSS_READ_DELAY_RESET_AP_RBUFFER_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *delay = ( uint32_t )( rbuffer[0] << 16 ) | ( uint32_t )( rbuffer[1] << 8 ) | ( uint32_t )( rbuffer[2] ); + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_almanac_update_from_sat( const void* context, + const lr11xx_gnss_constellation_mask_t constellation_mask, + const lr11xx_gnss_search_mode_t effort_mode ) +{ + const uint8_t cbuffer[LR11XX_GNSS_ALMANAC_UPDATE_FROM_SAT_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_ALMANAC_UPDATE_FROM_SAT_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_ALMANAC_UPDATE_FROM_SAT_OC >> 0 ), ( uint8_t ) effort_mode, + ( uint8_t ) constellation_mask + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_ALMANAC_UPDATE_FROM_SAT_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_gnss_read_keep_sync_status( const void* context, + const lr11xx_gnss_constellation_mask_t constellation_mask, + uint8_t* nb_visible_sat, uint32_t* time_elapsed ) +{ + uint8_t rbuffer[LR11XX_GNSS_READ_KEEP_SYNC_STATUS_RBUFFER_LENGTH] = { 0x00 }; + const uint8_t cbuffer[LR11XX_GNSS_CONFIG_READ_KEEP_SYNC_STATUS_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_KEEP_SYNC_STATUS_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_KEEP_SYNC_STATUS_OC >> 0 ), + ( uint8_t ) constellation_mask, + }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_CONFIG_READ_KEEP_SYNC_STATUS_CMD_LENGTH, rbuffer, + LR11XX_GNSS_READ_KEEP_SYNC_STATUS_RBUFFER_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *nb_visible_sat = rbuffer[0]; + *time_elapsed = lr11xx_gnss_uint8_to_uint32( &rbuffer[1] ); + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_read_almanac_status( const void* context, + lr11xx_gnss_read_almanac_status_t* almanac_status ) +{ + uint8_t rbuffer[LR11XX_GNSS_READ_ALMANAC_STATUS_RBUFFER_LENGTH] = { 0x00 }; + + const uint8_t cbuffer[LR11XX_GNSS_READ_ALMANAC_STATUS_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_ALMANAC_STATUS_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_ALMANAC_STATUS_OC >> 0 ), + }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_ALMANAC_STATUS_CMD_LENGTH, rbuffer, + LR11XX_GNSS_READ_ALMANAC_STATUS_RBUFFER_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + almanac_status->status_gps = ( lr11xx_gnss_almanac_status_t ) rbuffer[0]; + almanac_status->next_gps_time_sat_to_update = lr11xx_gnss_uint8_to_uint32( &rbuffer[1] ); + almanac_status->next_gps_nb_subframe_to_demodulate = rbuffer[5]; + almanac_status->next_gps_sat_id_to_update_in_sub_4 = rbuffer[6]; + almanac_status->next_gps_sat_id_to_update_in_sub_5 = rbuffer[7]; + almanac_status->next_gps_subframe_id_start = rbuffer[8]; + almanac_status->nb_sat_gps_to_update = rbuffer[9]; + almanac_status->sat_id_gps_to_update = lr11xx_gnss_uint8_to_uint32( &rbuffer[10] ); + almanac_status->sat_id_gps_activated = lr11xx_gnss_uint8_to_uint32( &rbuffer[14] ); + almanac_status->status_beidou = ( lr11xx_gnss_almanac_status_t ) rbuffer[18]; + almanac_status->next_beidou_time_sat_to_update = lr11xx_gnss_uint8_to_uint32( &rbuffer[19] ); + almanac_status->next_beidou_nb_subframe_to_demodulate = rbuffer[23]; + almanac_status->next_beidou_sat_id_to_update_in_sub_4 = rbuffer[24]; + almanac_status->next_beidou_sat_id_to_update_in_sub_5 = rbuffer[25]; + almanac_status->next_beidou_subframe_id_start = rbuffer[26]; + almanac_status->nb_sat_beidou_to_update = rbuffer[27]; + almanac_status->sat_id_beidou_to_update[0] = lr11xx_gnss_uint8_to_uint32( &rbuffer[28] ); + almanac_status->sat_id_beidou_to_update[1] = lr11xx_gnss_uint8_to_uint32( &rbuffer[32] ); + almanac_status->sat_id_beidou_activated[0] = lr11xx_gnss_uint8_to_uint32( &rbuffer[36] ); + almanac_status->sat_id_beidou_activated[1] = lr11xx_gnss_uint8_to_uint32( &rbuffer[40] ); + almanac_status->sat_id_beidou_black_list[0] = lr11xx_gnss_uint8_to_uint32( &rbuffer[44] ); + almanac_status->sat_id_beidou_black_list[1] = lr11xx_gnss_uint8_to_uint32( &rbuffer[48] ); + almanac_status->next_am_id = rbuffer[52]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_config_almanac_update_period( const void* context, + const lr11xx_gnss_constellation_mask_t constellation_mask, + const lr11xx_gnss_sv_type_t sv_type, const uint16_t period ) +{ + const uint8_t cbuffer[LR11XX_GNSS_CONFIG_ALMANAC_UPDATE_PERIOD_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_CONFIG_ALMANAC_UPDATE_PERIOD_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_CONFIG_ALMANAC_UPDATE_PERIOD_OC >> 0 ), + ( uint8_t ) constellation_mask, + ( uint8_t ) sv_type, + ( uint8_t )( period >> 8 ), + ( uint8_t )( period >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_GNSS_CONFIG_ALMANAC_UPDATE_PERIOD_CMD_LENGTH, + 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_read_almanac_update_period( const void* context, + const lr11xx_gnss_constellation_mask_t constellation_mask, + const lr11xx_gnss_sv_type_t sv_type, uint16_t* period ) +{ + uint8_t rbuffer[LR11XX_GNSS_READ_ALMANAC_UPDATE_PERIOD_RBUFFER_LENGTH] = { 0x00 }; + + const uint8_t cbuffer[LR11XX_GNSS_READ_ALMANAC_UPDATE_PERIOD_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_ALMANAC_UPDATE_PERIOD_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_ALMANAC_UPDATE_PERIOD_OC >> 0 ), + ( uint8_t ) constellation_mask, + ( uint8_t ) sv_type, + }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_READ_ALMANAC_UPDATE_PERIOD_CMD_LENGTH, rbuffer, + LR11XX_GNSS_READ_ALMANAC_UPDATE_PERIOD_RBUFFER_LENGTH ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + *period = ( rbuffer[0] << 8 ) | rbuffer[1]; + } + + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_gnss_get_sv_sync( const void* context, const lr11xx_gnss_constellation_mask_t constellation_mask, + const uint8_t nb_sv_to_get, uint8_t* sv_sync_list ) +{ + const uint8_t cbuffer[LR11XX_GNSS_GET_SV_SYNC_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_GET_SV_SYNC_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_GET_SV_SYNC_OC >> 0 ), + ( uint8_t ) constellation_mask, + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_GNSS_GET_SV_SYNC_CMD_LENGTH, sv_sync_list, + nb_sv_to_get ); +} + +lr11xx_status_t lr11xx_gnss_set_gps_bit_mask_sat_activated( const void* context, const uint32_t gps_sat_activated_1_32 ) +{ + const uint8_t cbuffer[LR11XX_GNSS_WRITE_GPS_BIT_MASK_SAT_ACTIVATED_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_WRITE_BIT_MASK_SAT_ACTIVATED_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_WRITE_BIT_MASK_SAT_ACTIVATED_OC >> 0 ), + ( uint8_t ) LR11XX_GNSS_GPS_MASK, + ( uint8_t )( gps_sat_activated_1_32 >> 24 ), + ( uint8_t )( gps_sat_activated_1_32 >> 16 ), + ( uint8_t )( gps_sat_activated_1_32 >> 8 ), + ( uint8_t )( gps_sat_activated_1_32 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, + LR11XX_GNSS_WRITE_GPS_BIT_MASK_SAT_ACTIVATED_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_set_beidou_bit_mask_sat_activated( const void* context, + const uint32_t beidou_sat_activated_1_32, + const uint32_t beidou_sat_activated_33_63 ) +{ + const uint8_t cbuffer[LR11XX_GNSS_WRITE_BEIDOU_BIT_MASK_SAT_ACTIVATED_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_WRITE_BIT_MASK_SAT_ACTIVATED_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_WRITE_BIT_MASK_SAT_ACTIVATED_OC >> 0 ), + ( uint8_t ) LR11XX_GNSS_BEIDOU_MASK, + ( uint8_t )( beidou_sat_activated_1_32 >> 24 ), + ( uint8_t )( beidou_sat_activated_1_32 >> 16 ), + ( uint8_t )( beidou_sat_activated_1_32 >> 8 ), + ( uint8_t )( beidou_sat_activated_1_32 ), + ( uint8_t )( beidou_sat_activated_33_63 >> 24 ), + ( uint8_t )( beidou_sat_activated_33_63 >> 16 ), + ( uint8_t )( beidou_sat_activated_33_63 >> 8 ), + ( uint8_t )( beidou_sat_activated_33_63 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, + LR11XX_GNSS_WRITE_BEIDOU_BIT_MASK_SAT_ACTIVATED_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_gnss_read_last_scan_mode_launched( const void* context, + lr11xx_gnss_scan_mode_launched_t* last_scan_mode ) +{ + uint8_t rbuffer[1] = { 0x00 }; + + const uint8_t cbuffer[LR11XX_GNSS_READ_LAST_SCAN_MODE_LAUNCHED_CMD_LENGTH] = { + ( uint8_t )( LR11XX_GNSS_READ_LAST_SCAN_MODE_LAUNCHED_OC >> 8 ), + ( uint8_t )( LR11XX_GNSS_READ_LAST_SCAN_MODE_LAUNCHED_OC >> 0 ), + }; + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( + context, cbuffer, LR11XX_GNSS_READ_LAST_SCAN_MODE_LAUNCHED_CMD_LENGTH, rbuffer, sizeof( rbuffer ) ); + + *last_scan_mode = ( lr11xx_gnss_scan_mode_launched_t ) rbuffer[0]; + + return ( lr11xx_status_t ) hal_status; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +uint32_t lr11xx_gnss_uint8_to_uint32( uint8_t value[4] ) +{ + return ( ( ( uint32_t ) value[0] ) << 24 ) + ( ( ( uint32_t ) value[1] ) << 16 ) + + ( ( ( uint32_t ) value[2] ) << 8 ) + ( ( ( uint32_t ) value[3] ) ); +} + +uint16_t lr11xx_gnss_get_min_from_operand_and_max_nb_of_blocks( uint16_t operand ) +{ + if( operand > LR11XX_GNSS_ALMANAC_UPDATE_MAX_NB_OF_BLOCKS ) + { + return LR11XX_GNSS_ALMANAC_UPDATE_MAX_NB_OF_BLOCKS; + } + else + { + return operand; + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr11xx_driver/lr11xx_lr_fhss.c b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_lr_fhss.c new file mode 100755 index 0000000..7aac7a4 --- /dev/null +++ b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_lr_fhss.c @@ -0,0 +1,216 @@ +/*! + * @file lr11xx_lr_fhss.c + * + * @brief LR_FHSS driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_lr_fhss.h" +#include "lr11xx_radio.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +#define LR11XX_LR_FHSS_BUILD_FRAME_LENGTH ( 2 + 9 ) +#define LR11XX_LR_FHSS_HEADER_BITS ( 114 ) +#define LR11XX_LR_FHSS_FRAG_BITS ( 48 ) +#define LR11XX_LR_FHSS_BLOCK_PREAMBLE_BITS ( 2 ) +#define LR11XX_LR_FHSS_BLOCK_BITS ( LR11XX_LR_FHSS_FRAG_BITS + LR11XX_LR_FHSS_BLOCK_PREAMBLE_BITS ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for radio-related operations + */ +enum +{ + LR11XX_LR_FHSS_BUILD_FRAME_OC = 0x022C, +}; + +/*! + * @brief Hopping enable/disabled enumerations for \ref lr11xx_lr_fhss_build_frame + */ +typedef enum +{ + LR11XX_LR_FHSS_HOPPING_DISABLE = 0x00, + LR11XX_LR_FHSS_HOPPING_ENABLE = 0x01, +} lr11xx_lr_fhss_hopping_configuration_t; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Get the bit count and block count for a LR-FHSS frame + * + * @param [in] params Parameter structure + * @param [in] payload_length Length of physical payload, in bytes + * + * @returns Length of physical payload, in bits + */ + +static uint16_t lr11xx_lr_fhss_get_nb_bits( const lr_fhss_v1_params_t* params, uint16_t payload_length ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_lr_fhss_init( const void* context ) +{ + const lr11xx_status_t set_packet_type_status = lr11xx_radio_set_pkt_type( context, LR11XX_RADIO_PKT_TYPE_LR_FHSS ); + if( set_packet_type_status != LR11XX_STATUS_OK ) + { + return set_packet_type_status; + } + + const lr11xx_radio_mod_params_lr_fhss_t mod_lr_fhss = { + .br_in_bps = LR11XX_RADIO_LR_FHSS_BITRATE_488_BPS, + .pulse_shape = LR11XX_RADIO_LR_FHSS_PULSE_SHAPE_BT_1, + }; + + const lr11xx_status_t set_modulation_param_status = lr11xx_radio_set_lr_fhss_mod_params( context, &mod_lr_fhss ); + return set_modulation_param_status; +} + +uint16_t lr11xx_lr_fhss_get_bit_delay_in_us( const lr11xx_lr_fhss_params_t* params, uint16_t payload_length ) +{ + const uint16_t nb_bits = lr11xx_lr_fhss_get_nb_bits( &( params->lr_fhss_params ), payload_length ); + + const uint8_t nb_padding_bits = 1 + ( ( 32768 - nb_bits ) & 0x07 ); + + return 1600 + nb_padding_bits * 2048; +} + +lr11xx_status_t lr11xx_lr_fhss_build_frame( const void* context, const lr11xx_lr_fhss_params_t* lr_fhss_params, + uint16_t hop_sequence_id, const uint8_t* payload, uint8_t payload_length ) +{ + // Since the build_frame command is last, it is possible to check status through stat1 + + lr11xx_status_t status = lr11xx_radio_set_lr_fhss_sync_word( context, lr_fhss_params->lr_fhss_params.sync_word ); + if( status != LR11XX_STATUS_OK ) + { + return status; + } + + const uint8_t cbuffer[LR11XX_LR_FHSS_BUILD_FRAME_LENGTH] = { + ( uint8_t ) ( LR11XX_LR_FHSS_BUILD_FRAME_OC >> 8 ), + ( uint8_t ) ( LR11XX_LR_FHSS_BUILD_FRAME_OC >> 0 ), + ( uint8_t ) lr_fhss_params->lr_fhss_params.header_count, + ( uint8_t ) lr_fhss_params->lr_fhss_params.cr, + ( uint8_t ) lr_fhss_params->lr_fhss_params.modulation_type, + ( uint8_t ) lr_fhss_params->lr_fhss_params.grid, + ( uint8_t ) ( lr_fhss_params->lr_fhss_params.enable_hopping ? LR11XX_LR_FHSS_HOPPING_ENABLE + : LR11XX_LR_FHSS_HOPPING_DISABLE ), + ( uint8_t ) lr_fhss_params->lr_fhss_params.bw, + ( uint8_t ) ( hop_sequence_id >> 8 ), + ( uint8_t ) ( hop_sequence_id >> 0 ), + ( uint8_t ) lr_fhss_params->device_offset, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_LR_FHSS_BUILD_FRAME_LENGTH, payload, + payload_length ); +} + +uint32_t lr11xx_lr_fhss_get_time_on_air_in_ms( const lr11xx_lr_fhss_params_t* params, uint16_t payload_length ) +{ + // Multiply by 1000 / 488.28125, or equivalently 256/125, rounding up + return ( ( lr11xx_lr_fhss_get_nb_bits( ¶ms->lr_fhss_params, payload_length ) << 8 ) + 124 ) / 125; +} + +unsigned int lr11xx_lr_fhss_get_hop_sequence_count( const lr11xx_lr_fhss_params_t* lr_fhss_params ) +{ + if( ( lr_fhss_params->lr_fhss_params.grid == LR_FHSS_V1_GRID_25391_HZ ) || + ( ( lr_fhss_params->lr_fhss_params.grid == LR_FHSS_V1_GRID_3906_HZ ) && + ( lr_fhss_params->lr_fhss_params.bw < LR_FHSS_V1_BW_335938_HZ ) ) ) + { + return 384; + } + return 512; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION --------------------------------------------- + */ + +uint16_t lr11xx_lr_fhss_get_nb_bits( const lr_fhss_v1_params_t* params, uint16_t payload_length ) +{ + uint16_t length_bits = ( payload_length + 2 ) * 8 + 6; + switch( params->cr ) + { + case LR_FHSS_V1_CR_5_6: + length_bits = ( ( length_bits * 6 ) + 4 ) / 5; + break; + + case LR_FHSS_V1_CR_2_3: + length_bits = length_bits * 3 / 2; + break; + + case LR_FHSS_V1_CR_1_2: + length_bits = length_bits * 2; + break; + + case LR_FHSS_V1_CR_1_3: + length_bits = length_bits * 3; + break; + } + + uint16_t payload_bits = ( length_bits / LR11XX_LR_FHSS_FRAG_BITS ) * LR11XX_LR_FHSS_BLOCK_BITS; + uint16_t last_block_bits = length_bits % LR11XX_LR_FHSS_FRAG_BITS; + if( last_block_bits > 0 ) + { + payload_bits += last_block_bits + 2; + } + + return LR11XX_LR_FHSS_HEADER_BITS * params->header_count + payload_bits; +} diff --git a/components/esp_lora_1121/src/lr11xx_driver/lr11xx_radio.c b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_radio.c new file mode 100755 index 0000000..092801f --- /dev/null +++ b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_radio.c @@ -0,0 +1,1349 @@ +/*! + * @file lr11xx_radio.c + * + * @brief Radio driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_radio.h" +#include "lr11xx_regmem.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/** + * @brief Management of the high ACP workaround + */ +#ifdef LR11XX_DISABLE_HIGH_ACP_WORKAROUND +#define LR11XX_RADIO_APPLY_HIGH_ACP_WORKAROUND( context ) LR11XX_STATUS_OK +#else +#define LR11XX_RADIO_APPLY_HIGH_ACP_WORKAROUND( context ) lr11xx_radio_apply_high_acp_workaround( context ) +#endif + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_RADIO_RESET_STATS_CMD_LENGTH ( 2 ) +#define LR11XX_RADIO_GET_STATS_CMD_LENGTH ( 2 ) +#define LR11XX_RADIO_GET_PKT_TYPE_CMD_LENGTH ( 2 ) +#define LR11XX_RADIO_GET_RXBUFFER_STATUS_CMD_LENGTH ( 2 ) +#define LR11XX_RADIO_GET_PKT_STATUS_CMD_LENGTH ( 2 ) +#define LR11XX_RADIO_GET_RSSI_INST_CMD_LENGTH ( 2 ) +#define LR11XX_RADIO_SET_GFSK_SYNC_WORD_CMD_LENGTH ( 2 + LR11XX_RADIO_GFSK_SYNC_WORD_LENGTH ) +#define LR11XX_RADIO_SET_LORA_PUBLIC_NETWORK_CMD_LENGTH ( 2 + 8 ) +#define LR11XX_RADIO_SET_LR_FHSS_SYNC_WORD_LENGTH ( 2 + 0 ) +#define LR11XX_RADIO_SET_RX_CMD_LENGTH ( 2 + 3 ) +#define LR11XX_RADIO_SET_TX_CMD_LENGTH ( 2 + 3 ) +#define LR11XX_RADIO_SET_RF_FREQUENCY_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_RADIO_SET_AUTO_TX_RX_CMD_LENGTH ( 2 + 7 ) +#define LR11XX_RADIO_SET_CAD_PARAMS_CMD_LENGTH ( 2 + 7 ) +#define LR11XX_RADIO_SET_PKT_TYPE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_RADIO_SET_MODULATION_PARAMS_GFSK_CMD_LENGTH ( 2 + 10 ) +#define LR11XX_RADIO_SET_MODULATION_PARAMS_BPSK_CMD_LENGTH ( 2 + 5 ) +#define LR11XX_RADIO_SET_MODULATION_PARAMS_LORA_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_RADIO_SET_MODULATION_PARAMS_LR_FHSS_CMD_LENGTH ( 2 + 5 ) +#define LR11XX_RADIO_SET_PKT_PARAM_GFSK_CMD_LENGTH ( 2 + 9 ) +#define LR11XX_RADIO_SET_PKT_PARAM_BPSK_CMD_LENGTH ( 2 + 7 ) +#define LR11XX_RADIO_SET_PKT_PARAM_LORA_CMD_LENGTH ( 2 + 6 ) +#define LR11XX_RADIO_SET_TX_PARAMS_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_RADIO_SET_PKT_ADDRESS_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_RADIO_SET_RX_TX_FALLBACK_MODE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_RADIO_SET_RX_DUTY_CYCLE_MODE_CMD_LENGTH ( 2 + 7 ) +#define LR11XX_RADIO_SET_PA_CFG_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_RADIO_STOP_TIMEOUT_ON_PREAMBLE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_RADIO_SET_CAD_CMD_LENGTH ( 2 ) +#define LR11XX_RADIO_SET_TX_CW_CMD_LENGTH ( 2 ) +#define LR11XX_RADIO_SET_TX_INFINITE_PREAMBLE_CMD_LENGTH ( 2 ) +#define LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_EXT_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_RADIO_SET_GFSK_CRC_PARAMS_CMD_LENGTH ( 2 + 8 ) +#define LR11XX_RADIO_SET_GFSK_WHITENING_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_RADIO_SET_RX_BOOSTED_LENGTH ( 2 + 1 ) +#define LR11XX_RADIO_SET_RSSI_CALIBRATION_LENGTH ( 2 + 11 ) +#define LR11XX_RADIO_SET_LORA_SYNC_WORD_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_RADIO_GET_LORA_RX_INFO_CMD_LENGTH ( 2 ) +#define LR11XX_RADIO_CFG_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_RADIO_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_SEND_CMD_LENGTH ( 2 + 1 ) + +/** + * @brief Internal RTC frequency + */ +#define LR11XX_RTC_FREQ_IN_HZ 32768UL + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for radio-related operations + */ +enum +{ + LR11XX_RADIO_RESET_STATS_OC = 0x0200, + LR11XX_RADIO_GET_STATS_OC = 0x0201, + LR11XX_RADIO_GET_PKT_TYPE_OC = 0x0202, + LR11XX_RADIO_GET_RXBUFFER_STATUS_OC = 0x0203, + LR11XX_RADIO_GET_PKT_STATUS_OC = 0x0204, + LR11XX_RADIO_GET_RSSI_INST_OC = 0x0205, + LR11XX_RADIO_SET_GFSK_SYNC_WORD_OC = 0x0206, + LR11XX_RADIO_SET_LORA_PUBLIC_NETWORK_OC = 0x0208, + LR11XX_RADIO_SET_RX_OC = 0x0209, + LR11XX_RADIO_SET_TX_OC = 0x020A, + LR11XX_RADIO_SET_RF_FREQUENCY_OC = 0x020B, + LR11XX_RADIO_AUTOTXRX_OC = 0x020C, + LR11XX_RADIO_SET_CAD_PARAMS_OC = 0x020D, + LR11XX_RADIO_SET_PKT_TYPE_OC = 0x020E, + LR11XX_RADIO_SET_MODULATION_PARAM_OC = 0x020F, + LR11XX_RADIO_SET_PKT_PARAM_OC = 0x0210, + LR11XX_RADIO_SET_TX_PARAMS_OC = 0x0211, + LR11XX_RADIO_SET_PKT_ADRS_OC = 0x0212, + LR11XX_RADIO_SET_RX_TX_FALLBACK_MODE_OC = 0x0213, + LR11XX_RADIO_SET_RX_DUTY_CYCLE_OC = 0x0214, + LR11XX_RADIO_SET_PA_CFG_OC = 0x0215, + LR11XX_RADIO_STOP_TIMEOUT_ON_PREAMBLE_OC = 0x0217, + LR11XX_RADIO_SET_CAD_OC = 0x0218, + LR11XX_RADIO_SET_TX_CW_OC = 0x0219, + LR11XX_RADIO_SET_TX_INFINITE_PREAMBLE_OC = 0x021A, + LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_OC = 0x021B, + LR11XX_RADIO_SET_GFSK_CRC_PARAMS_OC = 0x0224, + LR11XX_RADIO_SET_GFSK_WHITENING_PARAMS_OC = 0x0225, + LR11XX_RADIO_SET_RX_BOOSTED_OC = 0x0227, + LR11XX_RADIO_SET_RSSI_CALIBRATION_OC = 0x0229, + LR11XX_RADIO_SET_LORA_SYNC_WORD_OC = 0x022B, + LR11XX_RADIO_SET_LR_FHSS_SYNC_WORD_OC = 0x022D, + LR11XX_RADIO_CFG_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_OC = 0x022E, + LR11XX_RADIO_GET_LORA_RX_INFO_OC = 0x0230, + LR11XX_RADIO_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_SEND_OC = 0x0231, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Get the CRC length in byte from the corresponding GFSK radio parameter + * + * @param [in] crc_type GFSK CRC parameter + * + * @returns CRC length in byte + */ +static inline uint32_t lr11xx_radio_get_gfsk_crc_len_in_bytes( lr11xx_radio_gfsk_crc_type_t crc_type ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_radio_reset_stats( const void* context ) +{ + const uint8_t cbuffer[LR11XX_RADIO_RESET_STATS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_RESET_STATS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_RESET_STATS_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_RESET_STATS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_get_gfsk_stats( const void* context, lr11xx_radio_stats_gfsk_t* stats ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_STATS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_STATS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_STATS_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( lr11xx_radio_stats_gfsk_t )] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_RADIO_GET_STATS_CMD_LENGTH, rbuffer, sizeof( lr11xx_radio_stats_gfsk_t ) ); + + if( status == LR11XX_STATUS_OK ) + { + stats->nb_pkt_received = ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1]; + stats->nb_pkt_crc_error = ( ( uint16_t ) rbuffer[2] << 8 ) + ( uint16_t ) rbuffer[3]; + stats->nb_pkt_len_error = ( ( uint16_t ) rbuffer[4] << 8 ) + ( uint16_t ) rbuffer[5]; + } + + return status; +} + +lr11xx_status_t lr11xx_radio_get_lora_stats( const void* context, lr11xx_radio_stats_lora_t* stats ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_STATS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_STATS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_STATS_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( lr11xx_radio_stats_lora_t )] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_RADIO_GET_STATS_CMD_LENGTH, rbuffer, sizeof( lr11xx_radio_stats_lora_t ) ); + + if( status == LR11XX_STATUS_OK ) + { + stats->nb_pkt_received = ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1]; + stats->nb_pkt_crc_error = ( ( uint16_t ) rbuffer[2] << 8 ) + ( uint16_t ) rbuffer[3]; + stats->nb_pkt_header_error = ( ( uint16_t ) rbuffer[4] << 8 ) + ( uint16_t ) rbuffer[5]; + stats->nb_pkt_falsesync = ( ( uint16_t ) rbuffer[6] << 8 ) + ( uint16_t ) rbuffer[7]; + } + + return status; +} + +lr11xx_status_t lr11xx_radio_get_pkt_type( const void* context, lr11xx_radio_pkt_type_t* pkt_type ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_PKT_TYPE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_PKT_TYPE_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_PKT_TYPE_OC >> 0 ), + }; + uint8_t pkt_type_raw = 0; + + const lr11xx_status_t status = + ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_RADIO_GET_PKT_TYPE_CMD_LENGTH, &pkt_type_raw, 1 ); + + if( status == LR11XX_STATUS_OK ) + { + *pkt_type = ( lr11xx_radio_pkt_type_t ) pkt_type_raw; + } + + return status; +} + +lr11xx_status_t lr11xx_radio_get_rx_buffer_status( const void* context, + lr11xx_radio_rx_buffer_status_t* rx_buffer_status ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_RXBUFFER_STATUS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_RXBUFFER_STATUS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_RXBUFFER_STATUS_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( *rx_buffer_status )] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_RADIO_GET_RXBUFFER_STATUS_CMD_LENGTH, rbuffer, sizeof( *rx_buffer_status ) ); + + if( status == LR11XX_STATUS_OK ) + { + rx_buffer_status->pld_len_in_bytes = rbuffer[0]; + rx_buffer_status->buffer_start_pointer = rbuffer[1]; + } + + return status; +} + +lr11xx_status_t lr11xx_radio_get_gfsk_pkt_status( const void* context, lr11xx_radio_pkt_status_gfsk_t* pkt_status ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_PKT_STATUS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_PKT_STATUS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_PKT_STATUS_OC >> 0 ), + }; + uint8_t rbuffer[4] = { 0x00 }; + + const lr11xx_status_t status = + ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_RADIO_GET_PKT_STATUS_CMD_LENGTH, rbuffer, 4 ); + + if( status == LR11XX_STATUS_OK ) + { + pkt_status->rssi_sync_in_dbm = -( int8_t ) ( rbuffer[0] >> 1 ); + pkt_status->rssi_avg_in_dbm = -( int8_t ) ( rbuffer[1] >> 1 ); + pkt_status->rx_len_in_bytes = rbuffer[2]; + pkt_status->is_addr_err = ( ( rbuffer[3] & 0x20 ) != 0 ) ? true : false; + pkt_status->is_crc_err = ( ( rbuffer[3] & 0x10 ) != 0 ) ? true : false; + pkt_status->is_len_err = ( ( rbuffer[3] & 0x08 ) != 0 ) ? true : false; + pkt_status->is_abort_err = ( ( rbuffer[3] & 0x04 ) != 0 ) ? true : false; + pkt_status->is_received = ( ( rbuffer[3] & 0x02 ) != 0 ) ? true : false; + pkt_status->is_sent = ( ( rbuffer[3] & 0x01 ) != 0 ) ? true : false; + } + + return status; +} + +lr11xx_status_t lr11xx_radio_get_lora_pkt_status( const void* context, lr11xx_radio_pkt_status_lora_t* pkt_status ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_PKT_STATUS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_PKT_STATUS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_PKT_STATUS_OC >> 0 ), + }; + uint8_t rbuffer[3] = { 0x00 }; + + const lr11xx_status_t status = + ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_RADIO_GET_PKT_STATUS_CMD_LENGTH, rbuffer, 3 ); + + if( status == LR11XX_STATUS_OK ) + { + pkt_status->rssi_pkt_in_dbm = -( int8_t ) ( rbuffer[0] >> 1 ); + pkt_status->snr_pkt_in_db = ( ( ( int8_t ) rbuffer[1] ) + 2 ) >> 2; + pkt_status->signal_rssi_pkt_in_dbm = -( int8_t ) ( rbuffer[2] >> 1 ); + } + + return status; +} + +lr11xx_status_t lr11xx_radio_get_rssi_inst( const void* context, int8_t* rssi_in_dbm ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_RSSI_INST_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_RSSI_INST_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_RSSI_INST_OC >> 0 ), + }; + uint8_t rssi = 0; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_RADIO_GET_RSSI_INST_CMD_LENGTH, &rssi, sizeof( rssi ) ); + + if( status == LR11XX_STATUS_OK ) + { + *rssi_in_dbm = -( int8_t ) ( rssi >> 1 ); + } + + return status; +} + +lr11xx_status_t lr11xx_radio_set_gfsk_sync_word( const void* context, + const uint8_t gfsk_sync_word[LR11XX_RADIO_GFSK_SYNC_WORD_LENGTH] ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_GFSK_SYNC_WORD_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_GFSK_SYNC_WORD_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_GFSK_SYNC_WORD_OC >> 0 ), + gfsk_sync_word[0], + gfsk_sync_word[1], + gfsk_sync_word[2], + gfsk_sync_word[3], + gfsk_sync_word[4], + gfsk_sync_word[5], + gfsk_sync_word[6], + gfsk_sync_word[7], + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_GFSK_SYNC_WORD_CMD_LENGTH, 0, 0 ); +} + +#ifndef LR11XX_DISABLE_WARNINGS +#warning \ + "The function lr11xx_radio_set_lora_sync_word replaces the \ +deprecated function lr11xx_radio_set_lora_public_network. \ +lr11xx_radio_set_lora_sync_word, however, is incompatible \ +with chip firmware versions prior to 0x303. For those legacy chips \ +only, please use lr11xx_radio_set_lora_public_network. \ +To deactivate this warning, define C preprocessor symbol \ +LR11XX_DISABLE_WARNINGS." +#endif +lr11xx_status_t lr11xx_radio_set_lora_sync_word( const void* context, const uint8_t sync_word ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_LORA_SYNC_WORD_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_SYNC_WORD_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_SYNC_WORD_OC >> 0 ), + sync_word, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_LORA_SYNC_WORD_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_lora_public_network( const void* context, + const lr11xx_radio_lora_network_type_t network_type ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_LORA_PUBLIC_NETWORK_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_PUBLIC_NETWORK_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_PUBLIC_NETWORK_OC >> 0 ), + ( uint8_t ) network_type, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_LORA_SYNC_WORD_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_lr_fhss_sync_word( const void* context, + const uint8_t sync_word[LR11XX_RADIO_LR_FHSS_SYNC_WORD_LENGTH] ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_LR_FHSS_SYNC_WORD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_LR_FHSS_SYNC_WORD_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_LR_FHSS_SYNC_WORD_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_LR_FHSS_SYNC_WORD_LENGTH, sync_word, + LR11XX_RADIO_LR_FHSS_SYNC_WORD_LENGTH ); +} + +lr11xx_status_t lr11xx_radio_set_rx( const void* context, const uint32_t timeout_in_ms ) +{ + const uint32_t timeout_in_rtc_step = lr11xx_radio_convert_time_in_ms_to_rtc_step( timeout_in_ms ); + + return lr11xx_radio_set_rx_with_timeout_in_rtc_step( context, timeout_in_rtc_step ); +} + +lr11xx_status_t lr11xx_radio_set_rx_and_lna_mode( const void* context, const uint32_t timeout_in_ms, + lr11xx_radio_lna_mode_t lna_mode ) +{ + const uint32_t timeout_in_rtc_step = lr11xx_radio_convert_time_in_ms_to_rtc_step( timeout_in_ms ); + + return lr11xx_radio_set_rx_with_timeout_in_rtc_step_and_lna_mode( context, timeout_in_rtc_step, lna_mode ); +} + +lr11xx_status_t lr11xx_radio_set_rx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_ms ) +{ + return lr11xx_radio_set_rx_with_timeout_in_rtc_step_and_lna_mode( context, timeout_in_ms, + LR11XX_RADIO_LNA_MODE_DIFFERENTIAL_LF0 ); +} + +lr11xx_status_t lr11xx_radio_set_rx_with_timeout_in_rtc_step_and_lna_mode( const void* context, const uint32_t timeout, + lr11xx_radio_lna_mode_t lna_mode ) +{ + lr11xx_status_t status = LR11XX_STATUS_ERROR; + const uint8_t cbuffer[LR11XX_RADIO_SET_RX_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_RX_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_RX_OC >> 0 ), + ( uint8_t ) ( timeout >> 16 ), + ( uint8_t ) ( timeout >> 8 ), + ( uint8_t ) ( timeout >> 0 ), + }; + + do + { + status = LR11XX_RADIO_APPLY_HIGH_ACP_WORKAROUND( context ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + + status = ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_RX_CMD_LENGTH, 0, 0 ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + + if( ( lna_mode == LR11XX_RADIO_LNA_MODE_SINGLE_RFI_P_LF0 ) || + ( lna_mode == LR11XX_RADIO_LNA_MODE_SINGLE_RFI_N_LF0 ) ) + { + status = lr11xx_radio_set_lna_mode( context, lna_mode ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + } + + } while( 0 ); + + return status; +} + +lr11xx_status_t lr11xx_radio_set_tx( const void* context, const uint32_t timeout_in_ms ) +{ + const uint32_t timeout_in_rtc_step = lr11xx_radio_convert_time_in_ms_to_rtc_step( timeout_in_ms ); + + return lr11xx_radio_set_tx_with_timeout_in_rtc_step( context, timeout_in_rtc_step ); +} + +lr11xx_status_t lr11xx_radio_set_tx_with_timeout_in_rtc_step( const void* context, const uint32_t timeout_in_rtc_step ) +{ + lr11xx_status_t status = LR11XX_STATUS_ERROR; + + const uint8_t cbuffer[LR11XX_RADIO_SET_TX_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_TX_OC >> 8 ), ( uint8_t ) ( LR11XX_RADIO_SET_TX_OC >> 0 ), + ( uint8_t ) ( timeout_in_rtc_step >> 16 ), ( uint8_t ) ( timeout_in_rtc_step >> 8 ), + ( uint8_t ) ( timeout_in_rtc_step >> 0 ), + }; + + do + { + status = LR11XX_RADIO_APPLY_HIGH_ACP_WORKAROUND( context ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + + status = ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_TX_CMD_LENGTH, 0, 0 ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + } while( 0 ); + + return status; +} + +lr11xx_status_t lr11xx_radio_set_rf_freq( const void* context, const uint32_t freq_in_hz ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_RF_FREQUENCY_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_RF_FREQUENCY_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_RF_FREQUENCY_OC >> 0 ), + ( uint8_t ) ( freq_in_hz >> 24 ), + ( uint8_t ) ( freq_in_hz >> 16 ), + ( uint8_t ) ( freq_in_hz >> 8 ), + ( uint8_t ) ( freq_in_hz >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_RF_FREQUENCY_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_auto_tx_rx( const void* context, const uint32_t delay, + const lr11xx_radio_intermediary_mode_t intermediary_mode, + const uint32_t timeout ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_AUTO_TX_RX_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_AUTOTXRX_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_AUTOTXRX_OC >> 0 ), + ( uint8_t ) ( delay >> 16 ), + ( uint8_t ) ( delay >> 8 ), + ( uint8_t ) ( delay ), + ( uint8_t ) intermediary_mode, + ( uint8_t ) ( timeout >> 16 ), + ( uint8_t ) ( timeout >> 8 ), + ( uint8_t ) ( timeout ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_AUTO_TX_RX_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_cad_params( const void* context, const lr11xx_radio_cad_params_t* cad_params ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_CAD_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_CAD_PARAMS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_CAD_PARAMS_OC >> 0 ), + cad_params->cad_symb_nb, + cad_params->cad_detect_peak, + cad_params->cad_detect_min, + ( uint8_t ) cad_params->cad_exit_mode, + ( uint8_t ) ( cad_params->cad_timeout >> 16 ), + ( uint8_t ) ( cad_params->cad_timeout >> 8 ), + ( uint8_t ) ( cad_params->cad_timeout ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_CAD_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_pkt_type( const void* context, const lr11xx_radio_pkt_type_t pkt_type ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_PKT_TYPE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_TYPE_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_TYPE_OC >> 0 ), + ( uint8_t ) pkt_type, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_PKT_TYPE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_gfsk_mod_params( const void* context, + const lr11xx_radio_mod_params_gfsk_t* mod_params ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_MODULATION_PARAMS_GFSK_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_MODULATION_PARAM_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_MODULATION_PARAM_OC >> 0 ), + ( uint8_t ) ( mod_params->br_in_bps >> 24 ), + ( uint8_t ) ( mod_params->br_in_bps >> 16 ), + ( uint8_t ) ( mod_params->br_in_bps >> 8 ), + ( uint8_t ) ( mod_params->br_in_bps >> 0 ), + ( uint8_t ) mod_params->pulse_shape, + ( uint8_t ) mod_params->bw_dsb_param, + ( uint8_t ) ( mod_params->fdev_in_hz >> 24 ), + ( uint8_t ) ( mod_params->fdev_in_hz >> 16 ), + ( uint8_t ) ( mod_params->fdev_in_hz >> 8 ), + ( uint8_t ) ( mod_params->fdev_in_hz >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_MODULATION_PARAMS_GFSK_CMD_LENGTH, + 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_bpsk_mod_params( const void* context, + const lr11xx_radio_mod_params_bpsk_t* mod_params ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_MODULATION_PARAMS_BPSK_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_MODULATION_PARAM_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_MODULATION_PARAM_OC >> 0 ), + ( uint8_t ) ( mod_params->br_in_bps >> 24 ), + ( uint8_t ) ( mod_params->br_in_bps >> 16 ), + ( uint8_t ) ( mod_params->br_in_bps >> 8 ), + ( uint8_t ) ( mod_params->br_in_bps >> 0 ), + ( uint8_t ) mod_params->pulse_shape, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_MODULATION_PARAMS_BPSK_CMD_LENGTH, + 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_lora_mod_params( const void* context, + const lr11xx_radio_mod_params_lora_t* mod_params ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_MODULATION_PARAMS_LORA_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_MODULATION_PARAM_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_MODULATION_PARAM_OC >> 0 ), + ( uint8_t ) mod_params->sf, + ( uint8_t ) mod_params->bw, + ( uint8_t ) mod_params->cr, + ( uint8_t ) mod_params->ldro, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_MODULATION_PARAMS_LORA_CMD_LENGTH, + 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_lr_fhss_mod_params( const void* radio, + const lr11xx_radio_mod_params_lr_fhss_t* mod_params ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_MODULATION_PARAMS_LR_FHSS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_MODULATION_PARAM_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_MODULATION_PARAM_OC >> 0 ), + ( uint8_t ) ( mod_params->br_in_bps >> 24 ), + ( uint8_t ) ( mod_params->br_in_bps >> 16 ), + ( uint8_t ) ( mod_params->br_in_bps >> 8 ), + ( uint8_t ) ( mod_params->br_in_bps >> 0 ), + ( uint8_t ) mod_params->pulse_shape, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( radio, cbuffer, LR11XX_RADIO_SET_MODULATION_PARAMS_LR_FHSS_CMD_LENGTH, + 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_gfsk_pkt_params( const void* context, + const lr11xx_radio_pkt_params_gfsk_t* pkt_params ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_PKT_PARAM_GFSK_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_PARAM_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_PARAM_OC >> 0 ), + ( uint8_t ) ( pkt_params->preamble_len_in_bits >> 8 ), + ( uint8_t ) ( pkt_params->preamble_len_in_bits >> 0 ), + ( uint8_t ) ( pkt_params->preamble_detector ), + pkt_params->sync_word_len_in_bits, + ( uint8_t ) ( pkt_params->address_filtering ), + ( uint8_t ) ( pkt_params->header_type ), + pkt_params->pld_len_in_bytes, + ( uint8_t ) ( pkt_params->crc_type ), + ( uint8_t ) ( pkt_params->dc_free ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_PKT_PARAM_GFSK_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_bpsk_pkt_params( const void* context, + const lr11xx_radio_pkt_params_bpsk_t* pkt_params ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_PKT_PARAM_BPSK_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_PARAM_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_PARAM_OC >> 0 ), + pkt_params->pld_len_in_bytes, + ( uint8_t ) ( pkt_params->ramp_up_delay >> 8 ), + ( uint8_t ) ( pkt_params->ramp_up_delay >> 0 ), + ( uint8_t ) ( pkt_params->ramp_down_delay >> 8 ), + ( uint8_t ) ( pkt_params->ramp_down_delay >> 0 ), + ( uint8_t ) ( pkt_params->pld_len_in_bits >> 8 ), + ( uint8_t ) ( pkt_params->pld_len_in_bits >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_PKT_PARAM_BPSK_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_lora_pkt_params( const void* context, + const lr11xx_radio_pkt_params_lora_t* pkt_params ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_PKT_PARAM_LORA_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_PARAM_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_PARAM_OC >> 0 ), + ( uint8_t ) ( pkt_params->preamble_len_in_symb >> 8 ), + ( uint8_t ) ( pkt_params->preamble_len_in_symb >> 0 ), + ( uint8_t ) ( pkt_params->header_type ), + pkt_params->pld_len_in_bytes, + ( uint8_t ) ( pkt_params->crc ), + ( uint8_t ) ( pkt_params->iq ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_PKT_PARAM_LORA_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_tx_params( const void* context, const int8_t pwr_in_dbm, + const lr11xx_radio_ramp_time_t ramp_time ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_TX_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_TX_PARAMS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_TX_PARAMS_OC >> 0 ), + ( uint8_t ) pwr_in_dbm, + ( uint8_t ) ramp_time, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_TX_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_pkt_address( const void* context, const uint8_t node_address, + const uint8_t broadcast_address ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_PKT_ADDRESS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_ADRS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_PKT_ADRS_OC >> 0 ), + node_address, + broadcast_address, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_PKT_ADDRESS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_rx_tx_fallback_mode( const void* context, + const lr11xx_radio_fallback_modes_t fallback_mode ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_RX_TX_FALLBACK_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_RX_TX_FALLBACK_MODE_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_RX_TX_FALLBACK_MODE_OC >> 0 ), + fallback_mode, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_RX_TX_FALLBACK_MODE_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_radio_set_rx_duty_cycle( const void* context, const uint32_t rx_period_in_ms, + const uint32_t sleep_period_in_ms, + const lr11xx_radio_rx_duty_cycle_mode_t mode ) +{ + const uint32_t rx_period_in_rtc_step = lr11xx_radio_convert_time_in_ms_to_rtc_step( rx_period_in_ms ); + const uint32_t sleep_period_in_rtc_step = lr11xx_radio_convert_time_in_ms_to_rtc_step( sleep_period_in_ms ); + + return lr11xx_radio_set_rx_duty_cycle_with_timings_in_rtc_step( context, rx_period_in_rtc_step, + sleep_period_in_rtc_step, mode ); +} + +lr11xx_status_t lr11xx_radio_set_rx_duty_cycle_with_timings_in_rtc_step( const void* context, + const uint32_t rx_period_in_rtc_step, + const uint32_t sleep_period_in_rtc_step, + const lr11xx_radio_rx_duty_cycle_mode_t mode ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_RX_DUTY_CYCLE_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_RX_DUTY_CYCLE_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_RX_DUTY_CYCLE_OC >> 0 ), + ( uint8_t ) ( rx_period_in_rtc_step >> 16 ), + ( uint8_t ) ( rx_period_in_rtc_step >> 8 ), + ( uint8_t ) ( rx_period_in_rtc_step >> 0 ), + ( uint8_t ) ( sleep_period_in_rtc_step >> 16 ), + ( uint8_t ) ( sleep_period_in_rtc_step >> 8 ), + ( uint8_t ) ( sleep_period_in_rtc_step >> 0 ), + ( uint8_t ) mode, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_RX_DUTY_CYCLE_MODE_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_radio_set_pa_cfg( const void* context, const lr11xx_radio_pa_cfg_t* pa_cfg ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_PA_CFG_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_PA_CFG_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_PA_CFG_OC >> 0 ), + ( uint8_t ) pa_cfg->pa_sel, + ( uint8_t ) pa_cfg->pa_reg_supply, + pa_cfg->pa_duty_cycle, + pa_cfg->pa_hp_sel, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_PA_CFG_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_stop_timeout_on_preamble( const void* context, const bool stop_timeout_on_preamble ) +{ + const uint8_t cbuffer[LR11XX_RADIO_STOP_TIMEOUT_ON_PREAMBLE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_STOP_TIMEOUT_ON_PREAMBLE_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_STOP_TIMEOUT_ON_PREAMBLE_OC >> 0 ), + ( uint8_t ) stop_timeout_on_preamble, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_STOP_TIMEOUT_ON_PREAMBLE_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_radio_set_cad( const void* context ) +{ + lr11xx_status_t status = LR11XX_STATUS_ERROR; + const uint8_t cbuffer[LR11XX_RADIO_SET_CAD_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_CAD_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_CAD_OC >> 0 ), + }; + + do + { + status = LR11XX_RADIO_APPLY_HIGH_ACP_WORKAROUND( context ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + + status = ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_CAD_CMD_LENGTH, 0, 0 ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + } while( 0 ); + + return status; +} + +lr11xx_status_t lr11xx_radio_set_tx_cw( const void* context ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_TX_CW_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_TX_CW_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_TX_CW_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_TX_CW_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_tx_infinite_preamble( const void* context ) +{ + lr11xx_status_t status = LR11XX_STATUS_ERROR; + const uint8_t cbuffer[LR11XX_RADIO_SET_TX_INFINITE_PREAMBLE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_TX_INFINITE_PREAMBLE_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_TX_INFINITE_PREAMBLE_OC >> 0 ), + }; + + do + { + status = LR11XX_RADIO_APPLY_HIGH_ACP_WORKAROUND( context ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + + status = ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, + LR11XX_RADIO_SET_TX_INFINITE_PREAMBLE_CMD_LENGTH, 0, 0 ); + if( status != LR11XX_STATUS_OK ) + { + break; + } + } while( 0 ); + + return status; +} + +lr11xx_status_t lr11xx_radio_set_lora_sync_timeout( const void* context, const uint16_t nb_symbol ) +{ + if( nb_symbol <= 255 ) + { + const uint8_t cbuffer[LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_OC >> 0 ), + nb_symbol, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_CMD_LENGTH, 0, + 0 ); + } + else + { + uint8_t exp; + uint8_t mant; + + lr11xx_radio_convert_nb_symb_to_mant_exp( nb_symbol, &mant, &exp ); + + return lr11xx_radio_set_lora_sync_timeout_with_mantissa_exponent( context, mant, exp ); + } +} + +lr11xx_status_t lr11xx_radio_set_lora_sync_timeout_with_mantissa_exponent( const void* context, const uint8_t mantissa, + const uint8_t exponent ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_EXT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_OC >> 0 ), + mantissa << 3 | exponent, + 0x01, + }; + + if( ( mantissa > 31 ) || ( exponent > 7 ) ) + { + return LR11XX_STATUS_ERROR; + } + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_LORA_SYNC_TIMEOUT_EXT_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_radio_set_gfsk_crc_params( const void* context, const uint32_t seed, const uint32_t polynomial ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_GFSK_CRC_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_GFSK_CRC_PARAMS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_GFSK_CRC_PARAMS_OC >> 0 ), + ( uint8_t ) ( seed >> 24 ), + ( uint8_t ) ( seed >> 16 ), + ( uint8_t ) ( seed >> 8 ), + ( uint8_t ) ( seed >> 0 ), + ( uint8_t ) ( polynomial >> 24 ), + ( uint8_t ) ( polynomial >> 16 ), + ( uint8_t ) ( polynomial >> 8 ), + ( uint8_t ) ( polynomial >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_GFSK_CRC_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_gfsk_whitening_seed( const void* context, const uint16_t seed ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_GFSK_WHITENING_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_GFSK_WHITENING_PARAMS_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_GFSK_WHITENING_PARAMS_OC >> 0 ), + ( uint8_t ) ( seed >> 8 ), + ( uint8_t ) ( seed >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_GFSK_WHITENING_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_cfg_rx_boosted( const void* context, const bool enable_boost_mode ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_RX_BOOSTED_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_RX_BOOSTED_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_RX_BOOSTED_OC >> 0 ), + ( enable_boost_mode == true ) ? 0x01 : 0x00, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_RX_BOOSTED_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_set_rssi_calibration( const void* context, + const lr11xx_radio_rssi_calibration_table_t* rssi_cal_table ) +{ + const uint8_t cbuffer[LR11XX_RADIO_SET_RSSI_CALIBRATION_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_SET_RSSI_CALIBRATION_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_SET_RSSI_CALIBRATION_OC >> 0 ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g5 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g4 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g7 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g6 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g9 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g8 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g11 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g10 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g13 & 0x0F ) << 4 ) + ( rssi_cal_table->gain_tune.g12 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g13hp2 & 0x0F ) << 4 ) + + ( rssi_cal_table->gain_tune.g13hp1 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g13hp4 & 0x0F ) << 4 ) + + ( rssi_cal_table->gain_tune.g13hp3 & 0x0F ) ), + ( uint8_t ) ( ( ( rssi_cal_table->gain_tune.g13hp6 & 0x0F ) << 4 ) + + ( rssi_cal_table->gain_tune.g13hp5 & 0x0F ) ), + ( uint8_t ) ( rssi_cal_table->gain_tune.g13hp7 & 0x0F ), + ( uint8_t ) ( rssi_cal_table->gain_offset >> 8 ), + ( uint8_t ) ( rssi_cal_table->gain_offset >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RADIO_SET_RSSI_CALIBRATION_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_radio_get_gfsk_rx_bandwidth( uint32_t bw_in_hz, lr11xx_radio_gfsk_bw_t* bw_parameter ) +{ + if( bw_in_hz <= 4800 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_4800; + } + else if( bw_in_hz <= 5800 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_5800; + } + else if( bw_in_hz <= 7300 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_7300; + } + else if( bw_in_hz <= 9700 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_9700; + } + else if( bw_in_hz <= 11700 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_11700; + } + else if( bw_in_hz <= 14600 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_14600; + } + else if( bw_in_hz <= 19500 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_19500; + } + else if( bw_in_hz <= 23400 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_23400; + } + else if( bw_in_hz <= 29300 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_29300; + } + else if( bw_in_hz <= 39000 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_39000; + } + else if( bw_in_hz <= 46900 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_46900; + } + else if( bw_in_hz <= 58600 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_58600; + } + else if( bw_in_hz <= 78200 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_78200; + } + else if( bw_in_hz <= 93800 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_93800; + } + else if( bw_in_hz <= 117300 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_117300; + } + else if( bw_in_hz <= 156200 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_156200; + } + else if( bw_in_hz <= 187200 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_187200; + } + else if( bw_in_hz <= 234300 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_234300; + } + else if( bw_in_hz <= 312000 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_312000; + } + else if( bw_in_hz <= 373600 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_373600; + } + else if( bw_in_hz <= 467000 ) + { + *bw_parameter = LR11XX_RADIO_GFSK_BW_467000; + } + else + { + return LR11XX_STATUS_ERROR; + } + + return LR11XX_STATUS_OK; +} + +uint32_t lr11xx_radio_get_lora_time_on_air_numerator( const lr11xx_radio_pkt_params_lora_t* pkt_p, + const lr11xx_radio_mod_params_lora_t* mod_p ) +{ + const int32_t pld_len_in_bytes = pkt_p->pld_len_in_bytes; + const int32_t sf = mod_p->sf; + const bool pld_is_fix = pkt_p->header_type == LR11XX_RADIO_LORA_PKT_IMPLICIT; + + int32_t fine_synch = ( sf <= 6 ) ? 1 : 0; + bool long_interleaving = ( mod_p->cr > 4 ); + + int32_t total_bytes_nb = pld_len_in_bytes + ( ( pkt_p->crc == LR11XX_RADIO_LORA_CRC_ON ) ? 2 : 0 ); + int32_t tx_bits_symbol = sf - 2 * ( mod_p->ldro != 0 ? 1 : 0 ); + + int32_t ceil_numerator; + int32_t ceil_denominator; + + uint32_t intermed; + + int32_t symbols_nb_data; + int32_t tx_infobits_header; + int32_t tx_infobits_payload; + + if( long_interleaving ) + { + const int32_t fec_rate_numerator = 4; + const int32_t fec_rate_denominator = ( mod_p->cr + ( mod_p->cr == 7 ? 1 : 0 ) ); + + if( pld_is_fix ) + { + int32_t tx_bits_symbol_start = sf - 2 + 2 * fine_synch; + if( 8 * total_bytes_nb * fec_rate_denominator <= 7 * fec_rate_numerator * tx_bits_symbol_start ) + { + ceil_numerator = 8 * total_bytes_nb * fec_rate_denominator; + ceil_denominator = fec_rate_numerator * tx_bits_symbol_start; + } + else + { + int32_t tx_codedbits_header = tx_bits_symbol_start * 8; + ceil_numerator = 8 * fec_rate_numerator * tx_bits_symbol + 8 * total_bytes_nb * fec_rate_denominator - + fec_rate_numerator * tx_codedbits_header; + ceil_denominator = fec_rate_numerator * tx_bits_symbol; + } + } + else + { + tx_infobits_header = ( sf * 4 + fine_synch * 8 - 28 ) & ~0x07; + if( tx_infobits_header < 8 * total_bytes_nb ) + { + if( tx_infobits_header > 8 * pld_len_in_bytes ) + { + tx_infobits_header = 8 * pld_len_in_bytes; + } + } + tx_infobits_payload = 8 * total_bytes_nb - tx_infobits_header; + if( tx_infobits_payload < 0 ) + { + tx_infobits_payload = 0; + } + + ceil_numerator = tx_infobits_payload * fec_rate_denominator + 8 * fec_rate_numerator * tx_bits_symbol; + ceil_denominator = fec_rate_numerator * tx_bits_symbol; + } + } + else + { + tx_infobits_header = sf * 4 + fine_synch * 8 - 8; + + if( !pld_is_fix ) + { + tx_infobits_header -= 20; + } + + tx_infobits_payload = 8 * total_bytes_nb - tx_infobits_header; + + if( tx_infobits_payload < 0 ) + tx_infobits_payload = 0; + + ceil_numerator = tx_infobits_payload; + ceil_denominator = 4 * tx_bits_symbol; + } + + symbols_nb_data = ( ( ceil_numerator + ceil_denominator - 1 ) / ceil_denominator ); + if( !long_interleaving ) + { + symbols_nb_data = symbols_nb_data * ( mod_p->cr + 4 ) + 8; + } + intermed = pkt_p->preamble_len_in_symb + 4 + 2 * fine_synch + symbols_nb_data; + + return ( uint32_t ) ( ( 4 * intermed + 1 ) * ( 1 << ( sf - 2 ) ) ) - 1; +} + +uint32_t lr11xx_radio_get_lora_bw_in_hz( lr11xx_radio_lora_bw_t bw ) +{ + uint32_t bw_in_hz = 0; + + switch( bw ) + { + case LR11XX_RADIO_LORA_BW_10: + bw_in_hz = 10417UL; + break; + case LR11XX_RADIO_LORA_BW_15: + bw_in_hz = 15625UL; + break; + case LR11XX_RADIO_LORA_BW_20: + bw_in_hz = 20833UL; + break; + case LR11XX_RADIO_LORA_BW_31: + bw_in_hz = 31250UL; + break; + case LR11XX_RADIO_LORA_BW_41: + bw_in_hz = 41667UL; + break; + case LR11XX_RADIO_LORA_BW_62: + bw_in_hz = 62500UL; + break; + case LR11XX_RADIO_LORA_BW_125: + bw_in_hz = 125000UL; + break; + case LR11XX_RADIO_LORA_BW_250: + bw_in_hz = 250000UL; + break; + case LR11XX_RADIO_LORA_BW_500: + bw_in_hz = 500000UL; + break; + case LR11XX_RADIO_LORA_BW_200: + bw_in_hz = 203000UL; + break; + case LR11XX_RADIO_LORA_BW_400: + bw_in_hz = 406000UL; + break; + case LR11XX_RADIO_LORA_BW_800: + bw_in_hz = 812000UL; + break; + } + + return bw_in_hz; +} + +uint32_t lr11xx_radio_get_lora_time_on_air_in_ms( const lr11xx_radio_pkt_params_lora_t* pkt_p, + const lr11xx_radio_mod_params_lora_t* mod_p ) +{ + uint32_t numerator = 1000U * lr11xx_radio_get_lora_time_on_air_numerator( pkt_p, mod_p ); + uint32_t denominator = lr11xx_radio_get_lora_bw_in_hz( mod_p->bw ); + // Perform integral ceil() + return ( numerator + denominator - 1 ) / denominator; +} + +uint32_t lr11xx_radio_get_gfsk_time_on_air_numerator( const lr11xx_radio_pkt_params_gfsk_t* pkt_p ) +{ + uint8_t header_len_in_bits; + + switch( pkt_p->header_type ) + { + case LR11XX_RADIO_GFSK_PKT_FIX_LEN: + { + header_len_in_bits = 0; + break; + } + case LR11XX_RADIO_GFSK_PKT_VAR_LEN: + { + header_len_in_bits = 8; + break; + } + case LR11XX_RADIO_GFSK_PKT_VAR_LEN_SX128X_COMP: + { + header_len_in_bits = 9; + break; + } + default: + { + return 0; + } + } + + return pkt_p->preamble_len_in_bits + header_len_in_bits + pkt_p->sync_word_len_in_bits + + ( ( pkt_p->pld_len_in_bytes + + ( pkt_p->address_filtering == LR11XX_RADIO_GFSK_ADDRESS_FILTERING_DISABLE ? 0 : 1 ) + + lr11xx_radio_get_gfsk_crc_len_in_bytes( pkt_p->crc_type ) ) + << 3 ); +} + +uint32_t lr11xx_radio_get_gfsk_time_on_air_in_ms( const lr11xx_radio_pkt_params_gfsk_t* pkt_p, + const lr11xx_radio_mod_params_gfsk_t* mod_p ) +{ + uint32_t numerator = 1000U * lr11xx_radio_get_gfsk_time_on_air_numerator( pkt_p ); + uint32_t denominator = mod_p->br_in_bps; + + // Perform integral ceil() + return ( numerator + denominator - 1 ) / denominator; +} + +uint32_t lr11xx_radio_convert_time_in_ms_to_rtc_step( uint32_t time_in_ms ) +{ + return ( uint32_t ) ( time_in_ms * LR11XX_RTC_FREQ_IN_HZ / 1000 ); +} + +lr11xx_status_t lr11xx_radio_cfg_bluetooth_low_energy_beaconning_compatibility( const void* context, + const uint8_t channel_id, + const uint8_t* buffer, + const uint8_t length ) +{ + const uint8_t command[LR11XX_RADIO_CFG_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_CFG_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_CFG_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_OC >> 0 ), + channel_id, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( + context, command, LR11XX_RADIO_CFG_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_CMD_LENGTH, buffer, length ); +} + +lr11xx_status_t lr11xx_radio_get_lora_rx_info( const void* context, bool* is_crc_present, lr11xx_radio_lora_cr_t* cr ) +{ + const uint8_t cbuffer[LR11XX_RADIO_GET_LORA_RX_INFO_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_GET_LORA_RX_INFO_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_GET_LORA_RX_INFO_OC >> 0 ), + }; + uint8_t rbuffer = 0; + + const lr11xx_status_t status = + ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_RADIO_GET_LORA_RX_INFO_CMD_LENGTH, &rbuffer, 1 ); + + if( status == LR11XX_STATUS_OK ) + { + *is_crc_present = ( ( ( rbuffer & ( 0x01 << 4 ) ) != 0 ) ) ? true : false; + *cr = ( lr11xx_radio_lora_cr_t ) ( rbuffer & 0x07 ); + } + + return status; +} + +lr11xx_status_t lr11xx_radio_cfg_and_send_bluetooth_low_energy_beaconning_compatibility( const void* context, + const uint8_t channel_id, + const uint8_t* buffer, + const uint8_t length ) +{ + const uint8_t command[LR11XX_RADIO_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_SEND_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_RADIO_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_SEND_OC >> 8 ), + ( uint8_t ) ( LR11XX_RADIO_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_SEND_OC >> 0 ), + channel_id, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( + context, command, LR11XX_RADIO_BLUETOOTH_LOW_ENERGY_BEACONNING_COMPATIBILITY_SEND_CMD_LENGTH, buffer, length ); +} + +lr11xx_status_t lr11xx_radio_apply_high_acp_workaround( const void* context ) +{ + return lr11xx_regmem_write_regmem32_mask( context, 0x00F30054, 1 << 30, 0 << 30 ); +} + +uint16_t lr11xx_radio_convert_nb_symb_to_mant_exp( const uint16_t nb_symbol, uint8_t* mant, uint8_t* exp ) +{ + uint8_t exp_loc = 0; + uint16_t mant_loc = ( nb_symbol + 1 ) >> 1; + + while( mant_loc > 31 ) + { + mant_loc = ( mant_loc + 3 ) >> 2; + exp_loc++; + } + + *mant = ( uint8_t ) mant_loc; + *exp = exp_loc; + + return mant_loc << ( 2 * exp_loc + 1 ); +} + +lr11xx_status_t lr11xx_radio_set_lna_mode( const void* context, lr11xx_radio_lna_mode_t lna_config ) +{ + return lr11xx_regmem_write_regmem32_mask( context, 0x00F3008C, 0xF0, lna_config << 4 ); +} +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static inline uint32_t lr11xx_radio_get_gfsk_crc_len_in_bytes( lr11xx_radio_gfsk_crc_type_t crc_type ) +{ + switch( crc_type ) + { + case LR11XX_RADIO_GFSK_CRC_OFF: + return 0; + case LR11XX_RADIO_GFSK_CRC_1_BYTE: + return 1; + case LR11XX_RADIO_GFSK_CRC_2_BYTES: + return 2; + case LR11XX_RADIO_GFSK_CRC_1_BYTE_INV: + return 1; + case LR11XX_RADIO_GFSK_CRC_2_BYTES_INV: + return 2; + } + + return 0; +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr11xx_driver/lr11xx_radio_timings.c b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_radio_timings.c new file mode 100755 index 0000000..a27eeeb --- /dev/null +++ b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_radio_timings.c @@ -0,0 +1,233 @@ +/** + * @file lr11xx_radio_timings.c + * + * @brief LR11XX timing helper functions implementation + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_radio_timings.h" +#include "lr11xx_radio.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +/** + * @brief Time in microsecond taken by the chip to process the Rx done interrupt + */ +#define RX_DONE_IRQ_PROCESSING_TIME_IN_US 74 + +/** + * @brief Time in microsecond taken by the chip to process the Tx done interrupt + */ +#define TX_DONE_IRQ_PROCESSING_TIME_IN_US 111 + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/** + * @brief Get the power amplifier ramp time for a given power amplifier ramp time parameter + * + * @param [in] ramp_time Power amplifier ramp time parameter + * + * @returns Ramp time in microsecond + */ +static uint32_t lr11xx_radio_timings_get_pa_ramp_time_in_us( const lr11xx_radio_ramp_time_t ramp_time ); + +/** + * @brief Get the LoRa reception input delay + * + * @param [in] bw LoRa bandwidth + * + * @returns LoRa reception input delay in microsecond + */ +static uint32_t lr11xx_radio_timings_get_lora_rx_input_delay_in_us( lr11xx_radio_lora_bw_t bw ); + +/** + * @brief Get the LoRa symbol time + * + * @param [in] bw LoRa bandwidth + * @param [in] sf LoRa spreading factor + * + * @returns LoRa symbol time in microsecond + */ +static uint32_t lr11xx_radio_timings_get_lora_symb_time_in_us( const lr11xx_radio_lora_sf_t sf, + const lr11xx_radio_lora_bw_t bw ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +uint32_t lr11xx_radio_timings_get_delay_between_last_bit_sent_and_rx_done_in_us( + const lr11xx_radio_mod_params_lora_t* mod_params ) +{ + return lr11xx_radio_timings_get_lora_rx_input_delay_in_us( mod_params->bw ) + + 2 * lr11xx_radio_timings_get_lora_symb_time_in_us( mod_params->sf, mod_params->bw ) + + RX_DONE_IRQ_PROCESSING_TIME_IN_US; +} + +uint32_t lr11xx_radio_timings_get_delay_between_last_bit_sent_and_tx_done_in_us( + const lr11xx_radio_ramp_time_t ramp_time ) +{ + return lr11xx_radio_timings_get_pa_ramp_time_in_us( ramp_time ) + TX_DONE_IRQ_PROCESSING_TIME_IN_US; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static uint32_t lr11xx_radio_timings_get_pa_ramp_time_in_us( const lr11xx_radio_ramp_time_t ramp_time ) +{ + switch( ramp_time ) + { + case LR11XX_RADIO_RAMP_16_US: + { + return 16; + } + case LR11XX_RADIO_RAMP_32_US: + { + return 32; + } + case LR11XX_RADIO_RAMP_48_US: + { + return 48; + } + case LR11XX_RADIO_RAMP_64_US: + { + return 64; + } + case LR11XX_RADIO_RAMP_80_US: + { + return 80; + } + case LR11XX_RADIO_RAMP_96_US: + { + return 96; + } + case LR11XX_RADIO_RAMP_112_US: + { + return 112; + } + case LR11XX_RADIO_RAMP_128_US: + { + return 128; + } + case LR11XX_RADIO_RAMP_144_US: + { + return 144; + } + case LR11XX_RADIO_RAMP_160_US: + { + return 160; + } + case LR11XX_RADIO_RAMP_176_US: + { + return 176; + } + case LR11XX_RADIO_RAMP_192_US: + { + return 192; + } + case LR11XX_RADIO_RAMP_208_US: + { + return 208; + } + case LR11XX_RADIO_RAMP_240_US: + { + return 240; + } + case LR11XX_RADIO_RAMP_272_US: + { + return 272; + } + case LR11XX_RADIO_RAMP_304_US: + { + return 304; + } + default: + return 0; + } +} + +static uint32_t lr11xx_radio_timings_get_lora_rx_input_delay_in_us( lr11xx_radio_lora_bw_t bw ) +{ + switch( bw ) + { + case LR11XX_RADIO_LORA_BW_500: + { + return 16; + } + case LR11XX_RADIO_LORA_BW_250: + { + return 31; + } + case LR11XX_RADIO_LORA_BW_125: + { + return 57; + } + default: + { + return 0; + } + } +} + +static uint32_t lr11xx_radio_timings_get_lora_symb_time_in_us( const lr11xx_radio_lora_sf_t sf, + const lr11xx_radio_lora_bw_t bw ) +{ + return ( 1 << ( uint8_t ) sf ) * 1000000 / lr11xx_radio_get_lora_bw_in_hz( bw ); +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr11xx_driver/lr11xx_regmem.c b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_regmem.c new file mode 100755 index 0000000..0d3ac02 --- /dev/null +++ b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_regmem.c @@ -0,0 +1,323 @@ +/*! + * @file lr11xx_regmem.c + * + * @brief Register/memory driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_regmem.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_REGMEM_CLEAR_RXBUFFER_CMD_LENGTH 2 +#define LR11XX_REGMEM_WRITE_REGMEM32_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_REGMEM_READ_REGMEM32_CMD_LENGTH ( 2 + 4 + 1 ) +#define LR11XX_REGMEM_WRITE_MEM8_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_REGMEM_READ_MEM8_CMD_LENGTH ( 2 + 4 + 1 ) +#define LR11XX_REGMEM_WRITE_BUFFER8_CMD_LENGTH ( 2 ) +#define LR11XX_REGMEM_READ_BUFFER8_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH ( 2 + 4 + 4 + 4 ) + +#define LR11XX_REGMEM_BUFFER_SIZE_MAX ( 256 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for register and memory related operations + */ +enum +{ + LR11XX_REGMEM_WRITE_REGMEM32_OC = 0x0105, + LR11XX_REGMEM_READ_REGMEM32_OC = 0x0106, + LR11XX_REGMEM_WRITE_MEM8_OC = 0x0107, + LR11XX_REGMEM_READ_MEM8_OC = 0x0108, + LR11XX_REGMEM_WRITE_BUFFER8_OC = 0x0109, + LR11XX_REGMEM_READ_BUFFER8_OC = 0x010A, + LR11XX_REGMEM_CLEAR_RXBUFFER_OC = 0x010B, + LR11XX_REGMEM_WRITE_REGMEM32_MASK_OC = 0x010C, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Helper function that fill both cbuffer with opcode and memory address + * + * It is typically used in read/write regmem32 functions. + * + * @warning It is up to the caller to ensure cbuffer is big enough to contain opcode and address! + */ +static void lr11xx_regmem_fill_cbuffer_opcode_address( uint8_t* cbuffer, uint16_t opcode, uint32_t address ); + +/*! + * @brief Helper function that fill both cbuffer with opcode memory address, and data length to read + * + * It is typically used in read functions. + * + * @warning It is up to the caller to ensure cbuffer is big enough to contain opcode and address! + */ +static void lr11xx_regmem_fill_cbuffer_opcode_address_length( uint8_t* cbuffer, uint16_t opcode, uint32_t address, + uint8_t length ); + +/*! + * @brief Helper function that fill both cbuffer with data + * + * It is typically used in write write regmem32 functions. + * + * @warning It is up to the caller to ensure cdata is big enough to contain all data! + */ +static void lr11xx_regmem_fill_cdata( uint8_t* cdata, const uint32_t* data, uint8_t data_length ); + +/*! + * @brief Helper function that fill both cbuffer and cdata buffers with opcode, memory address and data + * + * It is typically used to factorize and write regmem32 operations. Behind the scene it calls the other helpers + * lr11xx_regmem_fill_cbuffer_opcode_address and lr11xx_regmem_fill_cdata. + * + * @warning It is up to the caller to ensure cbuffer and cdata are big enough to contain their respective information! + */ +static void lr11xx_regmem_fill_cbuffer_cdata_opcode_address_data( uint8_t* cbuffer, uint8_t* cdata, uint16_t opcode, + uint32_t address, const uint32_t* data, + uint8_t data_length ); + +/*! + * @brief Helper function that convert an array of uint8_t into an array of uint32_t + * + * Typically used in the read function returning uint32_t array. + * + * @warning It is up to the caller to ensure the raw_buffer is of length at least "out_buffer_length * + * sizeof(uint32_t)"! + */ +static void lr11xx_regmem_fill_out_buffer_from_raw_buffer( uint32_t* out_buffer, const uint8_t* raw_buffer, + uint8_t out_buffer_length ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_regmem_write_regmem32( const void* context, const uint32_t address, const uint32_t* buffer, + const uint8_t length ) +{ + uint8_t cbuffer[LR11XX_REGMEM_WRITE_REGMEM32_CMD_LENGTH]; + uint8_t cdata[LR11XX_REGMEM_BUFFER_SIZE_MAX]; + + if( length > LR11XX_REGMEM_MAX_WRITE_READ_WORDS ) + { + return LR11XX_STATUS_ERROR; + } + + lr11xx_regmem_fill_cbuffer_cdata_opcode_address_data( cbuffer, cdata, LR11XX_REGMEM_WRITE_REGMEM32_OC, address, + buffer, length ); + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_REGMEM_WRITE_REGMEM32_CMD_LENGTH, cdata, + length * sizeof( uint32_t ) ); +} + +lr11xx_status_t lr11xx_regmem_read_regmem32( const void* context, const uint32_t address, uint32_t* buffer, + const uint8_t length ) +{ + uint8_t cbuffer[LR11XX_REGMEM_READ_REGMEM32_CMD_LENGTH] = { 0 }; + + if( length > LR11XX_REGMEM_MAX_WRITE_READ_WORDS ) + { + return LR11XX_STATUS_ERROR; + } + + lr11xx_regmem_fill_cbuffer_opcode_address_length( cbuffer, LR11XX_REGMEM_READ_REGMEM32_OC, address, length ); + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_REGMEM_READ_REGMEM32_CMD_LENGTH, ( uint8_t* ) buffer, length * sizeof( uint32_t ) ); + + if( status == LR11XX_STATUS_OK ) + { + lr11xx_regmem_fill_out_buffer_from_raw_buffer( buffer, ( const uint8_t* ) buffer, length ); + } + + return status; +} + +lr11xx_status_t lr11xx_regmem_write_mem8( const void* context, const uint32_t address, const uint8_t* buffer, + const uint8_t length ) +{ + uint8_t cbuffer[LR11XX_REGMEM_WRITE_MEM8_CMD_LENGTH]; + + lr11xx_regmem_fill_cbuffer_opcode_address( cbuffer, LR11XX_REGMEM_WRITE_MEM8_OC, address ); + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_REGMEM_WRITE_MEM8_CMD_LENGTH, buffer, + length ); +} + +lr11xx_status_t lr11xx_regmem_read_mem8( const void* context, const uint32_t address, uint8_t* buffer, + const uint8_t length ) +{ + uint8_t cbuffer[LR11XX_REGMEM_READ_MEM8_CMD_LENGTH]; + + lr11xx_regmem_fill_cbuffer_opcode_address_length( cbuffer, LR11XX_REGMEM_READ_MEM8_OC, address, length ); + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_REGMEM_READ_MEM8_CMD_LENGTH, buffer, length ); +} + +lr11xx_status_t lr11xx_regmem_write_buffer8( const void* context, const uint8_t* buffer, const uint8_t length ) +{ + const uint8_t cbuffer[LR11XX_REGMEM_WRITE_BUFFER8_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_REGMEM_WRITE_BUFFER8_OC >> 8 ), + ( uint8_t ) ( LR11XX_REGMEM_WRITE_BUFFER8_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_REGMEM_WRITE_BUFFER8_CMD_LENGTH, buffer, + length ); +} + +lr11xx_status_t lr11xx_regmem_read_buffer8( const void* context, uint8_t* buffer, const uint8_t offset, + const uint8_t length ) +{ + const uint8_t cbuffer[LR11XX_REGMEM_READ_BUFFER8_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_REGMEM_READ_BUFFER8_OC >> 8 ), + ( uint8_t ) ( LR11XX_REGMEM_READ_BUFFER8_OC >> 0 ), + offset, + length, + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_REGMEM_READ_BUFFER8_CMD_LENGTH, buffer, + length ); +} + +lr11xx_status_t lr11xx_regmem_clear_rxbuffer( const void* context ) +{ + const uint8_t cbuffer[LR11XX_REGMEM_CLEAR_RXBUFFER_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_REGMEM_CLEAR_RXBUFFER_OC >> 8 ), + ( uint8_t ) ( LR11XX_REGMEM_CLEAR_RXBUFFER_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_REGMEM_CLEAR_RXBUFFER_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_regmem_write_regmem32_mask( const void* context, const uint32_t address, const uint32_t mask, + const uint32_t data ) +{ + uint8_t cbuffer[LR11XX_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH]; + + lr11xx_regmem_fill_cbuffer_opcode_address( cbuffer, LR11XX_REGMEM_WRITE_REGMEM32_MASK_OC, address ); + + cbuffer[6] = ( uint8_t ) ( mask >> 24 ); + cbuffer[7] = ( uint8_t ) ( mask >> 16 ); + cbuffer[8] = ( uint8_t ) ( mask >> 8 ); + cbuffer[9] = ( uint8_t ) ( mask >> 0 ); + + cbuffer[10] = ( uint8_t ) ( data >> 24 ); + cbuffer[11] = ( uint8_t ) ( data >> 16 ); + cbuffer[12] = ( uint8_t ) ( data >> 8 ); + cbuffer[13] = ( uint8_t ) ( data >> 0 ); + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_REGMEM_WRITE_REGMEM32_MASK_CMD_LENGTH, 0, 0 ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +void lr11xx_regmem_fill_cbuffer_opcode_address( uint8_t* cbuffer, uint16_t opcode, uint32_t address ) +{ + cbuffer[0] = ( uint8_t ) ( opcode >> 8 ); + cbuffer[1] = ( uint8_t ) ( opcode >> 0 ); + + cbuffer[2] = ( uint8_t ) ( address >> 24 ); + cbuffer[3] = ( uint8_t ) ( address >> 16 ); + cbuffer[4] = ( uint8_t ) ( address >> 8 ); + cbuffer[5] = ( uint8_t ) ( address >> 0 ); +} + +void lr11xx_regmem_fill_cbuffer_opcode_address_length( uint8_t* cbuffer, uint16_t opcode, uint32_t address, + uint8_t length ) +{ + lr11xx_regmem_fill_cbuffer_opcode_address( cbuffer, opcode, address ); + cbuffer[6] = length; +} + +void lr11xx_regmem_fill_cdata( uint8_t* cdata, const uint32_t* data, uint8_t data_length ) +{ + for( uint16_t index = 0; index < data_length; index++ ) + { + uint8_t* cdata_local = &cdata[index * sizeof( uint32_t )]; + + cdata_local[0] = ( uint8_t ) ( data[index] >> 24 ); + cdata_local[1] = ( uint8_t ) ( data[index] >> 16 ); + cdata_local[2] = ( uint8_t ) ( data[index] >> 8 ); + cdata_local[3] = ( uint8_t ) ( data[index] >> 0 ); + } +} + +void lr11xx_regmem_fill_cbuffer_cdata_opcode_address_data( uint8_t* cbuffer, uint8_t* cdata, uint16_t opcode, + uint32_t address, const uint32_t* data, uint8_t data_length ) +{ + lr11xx_regmem_fill_cbuffer_opcode_address( cbuffer, opcode, address ); + lr11xx_regmem_fill_cdata( cdata, data, data_length ); +} + +void lr11xx_regmem_fill_out_buffer_from_raw_buffer( uint32_t* out_buffer, const uint8_t* raw_buffer, + uint8_t out_buffer_length ) +{ + for( uint8_t out_index = 0; out_index < out_buffer_length; out_index++ ) + { + const uint8_t* raw_buffer_local = &raw_buffer[out_index * 4]; + + out_buffer[out_index] = ( ( uint32_t ) raw_buffer_local[0] << 24 ) + + ( ( uint32_t ) raw_buffer_local[1] << 16 ) + ( ( uint32_t ) raw_buffer_local[2] << 8 ) + + ( ( uint32_t ) raw_buffer_local[3] << 0 ); + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr11xx_driver/lr11xx_rttof.c b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_rttof.c new file mode 100755 index 0000000..4d06b48 --- /dev/null +++ b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_rttof.c @@ -0,0 +1,194 @@ +/** + * @file lr11xx_rttof.c + * + * @brief Round-Trip Time of Flight (RTToF) driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include "lr11xx_rttof.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_RTTOF_SET_ADDRESS_CMD_LENGTH ( 2 + 5 ) +#define LR11XX_RTTOF_SET_REQUEST_ADDRESS_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_RTTOF_SET_RX_TX_DELAY_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_RTTOF_SET_PARAMETERS_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_RTTOF_GET_RESULT_CMD_LENGTH ( 2 + 1 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/** + * @brief Operating codes for RTToF-related operations + */ +enum +{ + LR11XX_RTTOF_SET_ADDRESS = 0x021C, + LR11XX_RTTOF_SET_REQUEST_ADDRESS = 0x021D, + LR11XX_RTTOF_SET_RX_TX_DELAY = 0x021F, + LR11XX_RTTOF_SET_PARAMETERS = 0x0228, + LR11XX_RTTOF_GET_RESULT = 0x021E, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTION DECLARATIONS ------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTION DEFINITIONS --------------------------------------------- + */ + +lr11xx_status_t lr11xx_rttof_set_address( const void* context, const uint32_t address, const uint8_t check_length ) +{ + const uint8_t cbuffer[LR11XX_RTTOF_SET_ADDRESS_CMD_LENGTH] = { + ( uint8_t )( LR11XX_RTTOF_SET_ADDRESS >> 8 ), + ( uint8_t )( LR11XX_RTTOF_SET_ADDRESS >> 0 ), + ( uint8_t )( address >> 24 ), + ( uint8_t )( address >> 16 ), + ( uint8_t )( address >> 8 ), + ( uint8_t )( address >> 0 ), + check_length, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RTTOF_SET_ADDRESS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_rttof_set_request_address( const void* context, const uint32_t request_address ) +{ + const uint8_t cbuffer[LR11XX_RTTOF_SET_REQUEST_ADDRESS_CMD_LENGTH] = { + ( uint8_t )( LR11XX_RTTOF_SET_REQUEST_ADDRESS >> 8 ), + ( uint8_t )( LR11XX_RTTOF_SET_REQUEST_ADDRESS >> 0 ), + ( uint8_t )( request_address >> 24 ), + ( uint8_t )( request_address >> 16 ), + ( uint8_t )( request_address >> 8 ), + ( uint8_t )( request_address >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RTTOF_SET_REQUEST_ADDRESS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_rttof_set_rx_tx_delay_indicator( const void* context, const uint32_t delay_indicator ) +{ + const uint8_t cbuffer[LR11XX_RTTOF_SET_RX_TX_DELAY_CMD_LENGTH] = { + ( uint8_t )( LR11XX_RTTOF_SET_RX_TX_DELAY >> 8 ), + ( uint8_t )( LR11XX_RTTOF_SET_RX_TX_DELAY >> 0 ), + ( uint8_t )( delay_indicator >> 24 ), + ( uint8_t )( delay_indicator >> 16 ), + ( uint8_t )( delay_indicator >> 8 ), + ( uint8_t )( delay_indicator >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RTTOF_SET_RX_TX_DELAY_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_rttof_set_parameters( const void* context, const uint8_t nb_symbols ) +{ + const uint8_t cbuffer[LR11XX_RTTOF_SET_PARAMETERS_CMD_LENGTH] = { ( uint8_t )( LR11XX_RTTOF_SET_PARAMETERS >> 8 ), + ( uint8_t )( LR11XX_RTTOF_SET_PARAMETERS >> 0 ), + 0x00, nb_symbols }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_RTTOF_SET_PARAMETERS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_rttof_get_raw_result( const void* context, const lr11xx_rttof_result_type_t type, + uint8_t result[LR11XX_RTTOF_RESULT_LENGTH] ) +{ + const uint8_t cbuffer[LR11XX_RTTOF_GET_RESULT_CMD_LENGTH] = { + ( uint8_t )( LR11XX_RTTOF_GET_RESULT >> 8 ), + ( uint8_t )( LR11XX_RTTOF_GET_RESULT >> 0 ), + ( uint8_t ) type, + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_RTTOF_GET_RESULT_CMD_LENGTH, result, + LR11XX_RTTOF_RESULT_LENGTH ); +} + +int32_t lr11xx_rttof_distance_raw_to_meter( lr11xx_radio_lora_bw_t rttof_bw, + const uint8_t raw_distance_buf[LR11XX_RTTOF_RESULT_LENGTH] ) +{ + const uint8_t bitcnt = 24u; + uint8_t bw_scaling = 0u; + + const uint32_t raw_distance = + ( ( uint32_t ) raw_distance_buf[3] << 0 ) + ( ( uint32_t ) raw_distance_buf[2] << 8 ) + + ( ( uint32_t ) raw_distance_buf[1] << 16 ) + ( ( uint32_t ) raw_distance_buf[0] << 24 ); + + if( rttof_bw == LR11XX_RADIO_LORA_BW_500 ) + { + bw_scaling = 1u; + } + else if( rttof_bw == LR11XX_RADIO_LORA_BW_250 ) + { + bw_scaling = 2u; + } + else if( rttof_bw == LR11XX_RADIO_LORA_BW_125 ) + { + bw_scaling = 4u; + } + + int32_t retval = raw_distance; + if( raw_distance >= ( 1 << ( bitcnt - 1 ) ) ) + { + retval -= ( 1 << bitcnt ); + } + + return 300 * bw_scaling * retval / 4096; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTION DEFINITIONS -------------------------------------------- + */ + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr11xx_driver/lr11xx_system.c b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_system.c new file mode 100755 index 0000000..eeda3f9 --- /dev/null +++ b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_system.c @@ -0,0 +1,664 @@ +/*! + * @file lr11xx_system.c + * + * @brief System driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include + +#include "lr11xx_system.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_SYSTEM_GET_VERSION_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_GET_ERRORS_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_CLEAR_ERRORS_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_CALIBRATE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_SYSTEM_SET_REGMODE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_SYSTEM_CALIBRATE_IMAGE_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_SYSTEM_SET_DIO_AS_RF_SWITCH_CMD_LENGTH ( 2 + 8 ) +#define LR11XX_SYSTEM_SET_DIO_IRQ_PARAMS_CMD_LENGTH ( 2 + 8 ) +#define LR11XX_SYSTEM_CLEAR_IRQ_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_SYSTEM_CFG_LFCLK_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_SYSTEM_SET_TCXO_MODE_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_SYSTEM_REBOOT_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_SYSTEM_GET_VBAT_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_GET_TEMP_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_SET_SLEEP_CMD_LENGTH ( 2 + 5 ) +#define LR11XX_SYSTEM_SET_STANDBY_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_SYSTEM_SET_FS_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_ERASE_INFOPAGE_CMD_LENGTH ( 2 + 1 ) +#define LR11XX_SYSTEM_WRITE_INFOPAGE_CMD_LENGTH ( 2 + 3 ) +#define LR11XX_SYSTEM_READ_INFOPAGE_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_SYSTEM_READ_UID_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_READ_JOIN_EUI_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_READ_PIN_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_READ_PIN_CUSTOM_EUI_CMD_LENGTH ( LR11XX_SYSTEM_READ_PIN_CMD_LENGTH + 17 ) +#define LR11XX_SYSTEM_GET_RANDOM_CMD_LENGTH ( 2 ) +#define LR11XX_SYSTEM_ENABLE_SPI_CRC_CMD_LENGTH ( 3 ) +#define LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_CMD_LENGTH ( 3 ) + +#define LR11XX_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH ( 6 ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for system-related operations + */ +enum +{ + LR11XX_SYSTEM_GET_STATUS_OC = 0x0100, + LR11XX_SYSTEM_GET_VERSION_OC = 0x0101, + LR11XX_SYSTEM_GET_ERRORS_OC = 0x010D, + LR11XX_SYSTEM_CLEAR_ERRORS_OC = 0x010E, + LR11XX_SYSTEM_CALIBRATE_OC = 0x010F, + LR11XX_SYSTEM_SET_REGMODE_OC = 0x0110, + LR11XX_SYSTEM_CALIBRATE_IMAGE_OC = 0x0111, + LR11XX_SYSTEM_SET_DIO_AS_RF_SWITCH_OC = 0x0112, + LR11XX_SYSTEM_SET_DIOIRQPARAMS_OC = 0x0113, + LR11XX_SYSTEM_CLEAR_IRQ_OC = 0x0114, + LR11XX_SYSTEM_CFG_LFCLK_OC = 0x0116, + LR11XX_SYSTEM_SET_TCXO_MODE_OC = 0x0117, + LR11XX_SYSTEM_REBOOT_OC = 0x0118, + LR11XX_SYSTEM_GET_VBAT_OC = 0x0119, + LR11XX_SYSTEM_GET_TEMP_OC = 0x011A, + LR11XX_SYSTEM_SET_SLEEP_OC = 0x011B, + LR11XX_SYSTEM_SET_STANDBY_OC = 0x011C, + LR11XX_SYSTEM_SET_FS_OC = 0x011D, + LR11XX_SYSTEM_GET_RANDOM_OC = 0x0120, + LR11XX_SYSTEM_ERASE_INFOPAGE_OC = 0x0121, + LR11XX_SYSTEM_WRITE_INFOPAGE_OC = 0x0122, + LR11XX_SYSTEM_READ_INFOPAGE_OC = 0x0123, + LR11XX_SYSTEM_READ_UID_OC = 0x0125, + LR11XX_SYSTEM_READ_JOIN_EUI_OC = 0x0126, + LR11XX_SYSTEM_READ_PIN_OC = 0x0127, + LR11XX_SYSTEM_ENABLE_SPI_CRC_OC = 0x0128, + LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_OC = 0x012A, +}; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Fill stat1 structure with data from stat1_byte + * + * @param [in] stat1_byte stat1 byte + * @param [out] stat1 stat1 structure + */ +static void lr11xx_system_convert_stat1_byte_to_enum( uint8_t stat1_byte, lr11xx_system_stat1_t* stat1 ); + +/*! + * @brief Fill stat2 structure with data from stat2_byte + * + * @param [in] stat2_byte stat2 byte + * @param [out] stat2 stat2 structure + */ +static void lr11xx_system_convert_stat2_byte_to_enum( uint8_t stat2_byte, lr11xx_system_stat2_t* stat2 ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_system_reset( const void* context ) +{ + return ( lr11xx_status_t ) lr11xx_hal_reset( context ); +} + +lr11xx_status_t lr11xx_system_get_status( const void* context, lr11xx_system_stat1_t* stat1, + lr11xx_system_stat2_t* stat2, lr11xx_system_irq_mask_t* irq_status ) +{ + uint8_t data[LR11XX_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH] = { 0 }; + + const lr11xx_status_t status = + ( lr11xx_status_t ) lr11xx_hal_direct_read( context, data, LR11XX_SYSTEM_GET_STATUS_DIRECT_READ_LENGTH ); + + if( status == LR11XX_STATUS_OK ) + { + lr11xx_system_convert_stat1_byte_to_enum( data[0], stat1 ); + lr11xx_system_convert_stat2_byte_to_enum( data[1], stat2 ); + if( irq_status != NULL ) + { + *irq_status = ( ( lr11xx_system_irq_mask_t ) data[2] << 24 ) + + ( ( lr11xx_system_irq_mask_t ) data[3] << 16 ) + + ( ( lr11xx_system_irq_mask_t ) data[4] << 8 ) + ( ( lr11xx_system_irq_mask_t ) data[5] << 0 ); + } + } + + return status; +} + +lr11xx_status_t lr11xx_system_clear_reset_status_info( const void* context ) +{ + uint8_t cbuffer[2] = { + ( uint8_t ) ( LR11XX_SYSTEM_GET_STATUS_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_GET_STATUS_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, sizeof( cbuffer ), 0, 0 ); +} + +lr11xx_status_t lr11xx_system_get_version( const void* context, lr11xx_system_version_t* version ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_GET_VERSION_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_GET_VERSION_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_GET_VERSION_OC >> 0 ), + }; + uint8_t rbuffer[LR11XX_SYSTEM_VERSION_LENGTH] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_SYSTEM_GET_VERSION_CMD_LENGTH, rbuffer, LR11XX_SYSTEM_VERSION_LENGTH ); + + if( status == LR11XX_STATUS_OK ) + { + version->hw = rbuffer[0]; + version->type = ( lr11xx_system_version_type_t ) rbuffer[1]; + version->fw = ( ( uint16_t ) rbuffer[2] << 8 ) + ( uint16_t ) rbuffer[3]; + } + + return status; +} + +lr11xx_status_t lr11xx_system_get_errors( const void* context, lr11xx_system_errors_t* errors ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_GET_ERRORS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_GET_ERRORS_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_GET_ERRORS_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( errors )] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_SYSTEM_GET_ERRORS_CMD_LENGTH, rbuffer, sizeof( *errors ) ); + + if( status == LR11XX_STATUS_OK ) + { + *errors = ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1]; + } + + return status; +} + +lr11xx_status_t lr11xx_system_clear_errors( const void* context ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_CLEAR_ERRORS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_CLEAR_ERRORS_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_CLEAR_ERRORS_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_CLEAR_ERRORS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_calibrate( const void* context, const uint8_t calib_param ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_CALIBRATE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_CALIBRATE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_CALIBRATE_OC >> 0 ), + calib_param, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_CALIBRATE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_set_reg_mode( const void* context, const lr11xx_system_reg_mode_t reg_mode ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_REGMODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_REGMODE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_REGMODE_OC >> 0 ), + ( uint8_t ) reg_mode, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_REGMODE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_calibrate_image( const void* context, const uint8_t freq1, const uint8_t freq2 ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_CALIBRATE_IMAGE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_CALIBRATE_IMAGE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_CALIBRATE_IMAGE_OC >> 0 ), + freq1, + freq2, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_CALIBRATE_IMAGE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_calibrate_image_in_mhz( const void* context, const uint16_t freq1_in_mhz, + const uint16_t freq2_in_mhz ) +{ + // Perform a floor() to get a value for freq1 corresponding to a frequency lower than or equal to freq1_in_mhz + const uint8_t freq1 = freq1_in_mhz / LR11XX_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ; + + // Perform a ceil() to get a value for freq2 corresponding to a frequency higher than or equal to freq2_in_mhz + const uint8_t freq2 = ( freq2_in_mhz + LR11XX_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ - 1 ) / + LR11XX_SYSTEM_IMAGE_CALIBRATION_STEP_IN_MHZ; + + return lr11xx_system_calibrate_image( context, freq1, freq2 ); +} + +lr11xx_status_t lr11xx_system_set_dio_as_rf_switch( const void* context, + const lr11xx_system_rfswitch_cfg_t* rf_switch_cfg ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_DIO_AS_RF_SWITCH_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_DIO_AS_RF_SWITCH_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_DIO_AS_RF_SWITCH_OC >> 0 ), + rf_switch_cfg->enable, + rf_switch_cfg->standby, + rf_switch_cfg->rx, + rf_switch_cfg->tx, + rf_switch_cfg->tx_hp, + rf_switch_cfg->tx_hf, + rf_switch_cfg->gnss, + rf_switch_cfg->wifi, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_DIO_AS_RF_SWITCH_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_system_set_dio_irq_params( const void* context, + const lr11xx_system_irq_mask_t irqs_to_enable_dio1, + const lr11xx_system_irq_mask_t irqs_to_enable_dio2 ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_DIO_IRQ_PARAMS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_DIOIRQPARAMS_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_DIOIRQPARAMS_OC >> 0 ), + ( uint8_t ) ( irqs_to_enable_dio1 >> 24 ), + ( uint8_t ) ( irqs_to_enable_dio1 >> 16 ), + ( uint8_t ) ( irqs_to_enable_dio1 >> 8 ), + ( uint8_t ) ( irqs_to_enable_dio1 >> 0 ), + ( uint8_t ) ( irqs_to_enable_dio2 >> 24 ), + ( uint8_t ) ( irqs_to_enable_dio2 >> 16 ), + ( uint8_t ) ( irqs_to_enable_dio2 >> 8 ), + ( uint8_t ) ( irqs_to_enable_dio2 >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_DIO_IRQ_PARAMS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_clear_irq_status( const void* context, const lr11xx_system_irq_mask_t irqs_to_clear ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_CLEAR_IRQ_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_CLEAR_IRQ_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_CLEAR_IRQ_OC >> 0 ), + ( uint8_t ) ( irqs_to_clear >> 24 ), + ( uint8_t ) ( irqs_to_clear >> 16 ), + ( uint8_t ) ( irqs_to_clear >> 8 ), + ( uint8_t ) ( irqs_to_clear >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_CLEAR_IRQ_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_get_and_clear_irq_status( const void* context, lr11xx_system_irq_mask_t* irq ) +{ + lr11xx_system_irq_mask_t lr11xx_irq_mask = LR11XX_SYSTEM_IRQ_NONE; + + lr11xx_status_t status = lr11xx_system_get_irq_status( context, &lr11xx_irq_mask ); + + if( ( status == LR11XX_STATUS_OK ) && ( lr11xx_irq_mask != 0 ) ) + { + status = lr11xx_system_clear_irq_status( context, lr11xx_irq_mask ); + } + if( ( status == LR11XX_STATUS_OK ) && ( irq != NULL ) ) + { + *irq = lr11xx_irq_mask; + } + + return status; +} + +lr11xx_status_t lr11xx_system_cfg_lfclk( const void* context, const lr11xx_system_lfclk_cfg_t lfclock_cfg, + const bool wait_for_32k_ready ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_CFG_LFCLK_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_CFG_LFCLK_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_CFG_LFCLK_OC >> 0 ), + ( uint8_t ) ( lfclock_cfg | ( wait_for_32k_ready << 2 ) ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_CFG_LFCLK_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_set_tcxo_mode( const void* context, const lr11xx_system_tcxo_supply_voltage_t tune, + const uint32_t timeout ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_TCXO_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_TCXO_MODE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_TCXO_MODE_OC >> 0 ), + ( uint8_t ) tune, + ( uint8_t ) ( timeout >> 16 ), + ( uint8_t ) ( timeout >> 8 ), + ( uint8_t ) ( timeout >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_TCXO_MODE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_reboot( const void* context, const bool stay_in_bootloader ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_REBOOT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_REBOOT_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_REBOOT_OC >> 0 ), + ( stay_in_bootloader == true ) ? 0x03 : 0x00, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_REBOOT_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_get_vbat( const void* context, uint8_t* vbat ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_GET_VBAT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_GET_VBAT_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_GET_VBAT_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_SYSTEM_GET_VBAT_CMD_LENGTH, vbat, + sizeof( *vbat ) ); +} + +lr11xx_status_t lr11xx_system_get_temp( const void* context, uint16_t* temp ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_GET_TEMP_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_GET_TEMP_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_GET_TEMP_OC >> 0 ), + }; + uint8_t rbuffer[sizeof( uint16_t )] = { 0x00 }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_SYSTEM_GET_TEMP_CMD_LENGTH, rbuffer, sizeof( uint16_t ) ); + + if( status == LR11XX_STATUS_OK ) + { + *temp = ( ( uint16_t ) rbuffer[0] << 8 ) + ( uint16_t ) rbuffer[1]; + } + + return status; +} + +lr11xx_status_t lr11xx_system_set_sleep( const void* context, const lr11xx_system_sleep_cfg_t sleep_cfg, + const uint32_t sleep_time ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_SLEEP_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_SLEEP_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_SLEEP_OC >> 0 ), + ( sleep_cfg.is_rtc_timeout << 1 ) + sleep_cfg.is_warm_start, + ( uint8_t ) ( sleep_time >> 24 ), + ( uint8_t ) ( sleep_time >> 16 ), + ( uint8_t ) ( sleep_time >> 8 ), + ( uint8_t ) ( sleep_time >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_SLEEP_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_set_standby( const void* context, const lr11xx_system_standby_cfg_t standby_cfg ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_STANDBY_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_STANDBY_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_STANDBY_OC >> 0 ), + ( uint8_t ) standby_cfg, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_STANDBY_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_wakeup( const void* context ) +{ + return ( lr11xx_status_t ) lr11xx_hal_wakeup( context ); +} + +lr11xx_status_t lr11xx_system_abort_blocking_cmd( const void* context ) +{ + return ( lr11xx_status_t ) lr11xx_hal_abort_blocking_cmd( context ); +} + +lr11xx_status_t lr11xx_system_set_fs( const void* context ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_SET_FS_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_SET_FS_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_SET_FS_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_SET_FS_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_erase_infopage( const void* context, const lr11xx_system_infopage_id_t infopage_id ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_ERASE_INFOPAGE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_ERASE_INFOPAGE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_ERASE_INFOPAGE_OC >> 0 ), + ( uint8_t ) infopage_id, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_ERASE_INFOPAGE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_write_infopage( const void* context, const lr11xx_system_infopage_id_t infopage_id, + const uint16_t address, const uint32_t* data, const uint8_t length ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_WRITE_INFOPAGE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_WRITE_INFOPAGE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_WRITE_INFOPAGE_OC >> 0 ), + ( uint8_t ) infopage_id, + ( uint8_t ) ( address >> 8 ), + ( uint8_t ) ( address >> 0 ), + }; + uint8_t cdata[256]; + + for( uint16_t index = 0; index < length; index++ ) + { + uint8_t* cdata_local = &cdata[index * sizeof( uint32_t )]; + + cdata_local[0] = ( uint8_t ) ( data[index] >> 24 ); + cdata_local[1] = ( uint8_t ) ( data[index] >> 16 ); + cdata_local[2] = ( uint8_t ) ( data[index] >> 8 ); + cdata_local[3] = ( uint8_t ) ( data[index] >> 0 ); + } + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_WRITE_INFOPAGE_CMD_LENGTH, cdata, + length * sizeof( uint32_t ) ); +} + +lr11xx_status_t lr11xx_system_read_infopage( const void* context, const lr11xx_system_infopage_id_t infopage_id, + const uint16_t address, uint32_t* data, const uint8_t length ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_READ_INFOPAGE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_READ_INFOPAGE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_READ_INFOPAGE_OC >> 0 ), + ( uint8_t ) infopage_id, + ( uint8_t ) ( address >> 8 ), + ( uint8_t ) ( address >> 0 ), + length, + }; + + const lr11xx_status_t status = ( lr11xx_status_t ) lr11xx_hal_read( + context, cbuffer, LR11XX_SYSTEM_READ_INFOPAGE_CMD_LENGTH, ( uint8_t* ) data, length * sizeof( *data ) ); + + if( status == LR11XX_STATUS_OK ) + { + for( uint8_t index = 0; index < length; index++ ) + { + uint8_t* buffer_local = ( uint8_t* ) &data[index]; + + data[index] = ( ( uint32_t ) buffer_local[0] << 24 ) + ( ( uint32_t ) buffer_local[1] << 16 ) + + ( ( uint32_t ) buffer_local[2] << 8 ) + ( ( uint32_t ) buffer_local[3] << 0 ); + } + } + + return status; +} + +lr11xx_status_t lr11xx_system_read_uid( const void* context, lr11xx_system_uid_t unique_identifier ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_READ_UID_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_READ_UID_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_READ_UID_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_SYSTEM_READ_UID_CMD_LENGTH, unique_identifier, + LR11XX_SYSTEM_UID_LENGTH ); +} + +lr11xx_status_t lr11xx_system_read_join_eui( const void* context, lr11xx_system_join_eui_t join_eui ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_READ_JOIN_EUI_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_READ_JOIN_EUI_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_READ_JOIN_EUI_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_SYSTEM_READ_JOIN_EUI_CMD_LENGTH, join_eui, + LR11XX_SYSTEM_JOIN_EUI_LENGTH ); +} + +lr11xx_status_t lr11xx_system_read_pin( const void* context, lr11xx_system_pin_t pin ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_READ_PIN_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_READ_PIN_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_READ_PIN_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_SYSTEM_READ_PIN_CMD_LENGTH, pin, + LR11XX_SYSTEM_PIN_LENGTH ); +} + +lr11xx_status_t lr11xx_system_read_pin_custom_eui( const void* context, lr11xx_system_uid_t device_eui, + lr11xx_system_join_eui_t join_eui, uint8_t rfu, + lr11xx_system_pin_t pin ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_READ_PIN_CUSTOM_EUI_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_READ_PIN_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_READ_PIN_OC >> 0 ), + device_eui[0], + device_eui[1], + device_eui[2], + device_eui[3], + device_eui[4], + device_eui[5], + device_eui[6], + device_eui[7], + join_eui[0], + join_eui[1], + join_eui[2], + join_eui[3], + join_eui[4], + join_eui[5], + join_eui[6], + join_eui[7], + rfu, + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_SYSTEM_READ_PIN_CUSTOM_EUI_CMD_LENGTH, pin, + LR11XX_SYSTEM_PIN_LENGTH ); +} + +lr11xx_status_t lr11xx_system_get_random_number( const void* context, uint32_t* random_number ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_GET_RANDOM_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_GET_RANDOM_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_GET_RANDOM_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_SYSTEM_GET_RANDOM_CMD_LENGTH, + ( uint8_t* ) random_number, sizeof( uint32_t ) ); +} + +lr11xx_status_t lr11xx_system_enable_spi_crc( const void* context, bool enable_crc ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_ENABLE_SPI_CRC_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_ENABLE_SPI_CRC_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_ENABLE_SPI_CRC_OC >> 0 ), + ( enable_crc == true ) ? 0x01 : 0x00, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_ENABLE_SPI_CRC_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_system_drive_dio_in_sleep_mode( const void* context, bool enable_drive ) +{ + const uint8_t cbuffer[LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_OC >> 8 ), + ( uint8_t ) ( LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_OC >> 0 ), + ( enable_drive == true ) ? 0x01 : 0x00, + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_SYSTEM_DRIVE_DIO_IN_SLEEP_MODE_CMD_LENGTH, 0, + 0 ); +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static void lr11xx_system_convert_stat1_byte_to_enum( uint8_t stat1_byte, lr11xx_system_stat1_t* stat1 ) +{ + if( stat1 != NULL ) + { + stat1->is_interrupt_active = ( ( stat1_byte & 0x01 ) != 0 ) ? true : false; + stat1->command_status = ( lr11xx_system_command_status_t ) ( stat1_byte >> 1 ); + } +} + +static void lr11xx_system_convert_stat2_byte_to_enum( uint8_t stat2_byte, lr11xx_system_stat2_t* stat2 ) +{ + if( stat2 != NULL ) + { + stat2->is_running_from_flash = ( ( stat2_byte & 0x01 ) != 0 ) ? true : false; + stat2->chip_mode = ( lr11xx_system_chip_modes_t ) ( ( stat2_byte & 0x0F ) >> 1 ); + stat2->reset_status = ( lr11xx_system_reset_status_t ) ( ( stat2_byte & 0xF0 ) >> 4 ); + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr11xx_driver/lr11xx_wifi.c b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_wifi.c new file mode 100755 index 0000000..9c1312a --- /dev/null +++ b/components/esp_lora_1121/src/lr11xx_driver/lr11xx_wifi.c @@ -0,0 +1,940 @@ +/*! + * @file lr11xx_wifi.c + * + * @brief Wi-Fi passive scan driver implementation for LR11XX + * + * The Clear BSD License + * Copyright Semtech Corporation 2021. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include "lr11xx_wifi.h" +#include "lr11xx_system_types.h" +#include "lr11xx_hal.h" + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE MACROS----------------------------------------------------------- + */ + +#ifndef MIN +#define MIN( a, b ) ( ( a > b ) ? b : a ) +#endif // MIN + + +/*! + * @brief Check if a value is inferior to another one - included + */ +#define IS_INFERIOR( value, max ) ( value <= max ) + +/*! + * @brief Check if a value is in between min and max - included + */ +#define IS_BETWEEN( value, min, max ) ( IS_INFERIOR(min, value) && IS_INFERIOR(value, max) ) + +/*! + * @brief Check if a value is in between 0x80 and 0xBF - included + */ +#define IS_BETWEEN_0x80_AND_0xBF( value ) IS_BETWEEN( value, 0x80, 0xBF ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE CONSTANTS ------------------------------------------------------- + */ + +#define LR11XX_WIFI_BASIC_COMPLETE_RESULT_SIZE ( 22 ) +#define LR11XX_WIFI_BASIC_MAC_TYPE_CHANNEL_RESULT_SIZE ( 9 ) + +#define LR11XX_WIFI_MAX_SIZE_PER_SPI( single_size ) \ + ( single_size * ( LR11XX_WIFI_MAX_RESULT_PER_TRANSACTION( single_size ) ) ) + +#define LR11XX_WIFI_MAX_RESULT_PER_TRANSACTION( single_size ) \ + ( MIN( ( LR11XX_WIFI_READ_RESULT_LIMIT ) / ( single_size ), LR11XX_WIFI_N_RESULTS_MAX_PER_CHUNK ) ) + +#define LR11XX_WIFI_ALL_CUMULATIVE_TIMING_SIZE ( 16 ) +#define LR11XX_WIFI_VERSION_SIZE ( 2 ) +#define LR11XX_WIFI_READ_RESULT_LIMIT ( 1020 ) +#define LR11XX_WIFI_COUNTRY_RESULT_LENGTH_SIZE ( 1 ) +#define LR11XX_WIFI_EXTENDED_COMPLETE_RESULT_SIZE ( 79 ) +#define LR11XX_WIFI_SCAN_SINGLE_COUNTRY_CODE_RESULT_SIZE ( 10 ) +#define LR11XX_WIFI_MAX_COUNTRY_CODE_RESULT_SIZE \ + ( LR11XX_WIFI_MAX_COUNTRY_CODE * LR11XX_WIFI_SCAN_SINGLE_COUNTRY_CODE_RESULT_SIZE ) + +// Command length +#define LR11XX_WIFI_SCAN_CMD_LENGTH ( 2 + 9 ) +#define LR11XX_WIFI_SEARCH_COUNTRY_CODE_CMD_LENGTH ( 2 + 7 ) +#define LR11XX_WIFI_SCAN_TIME_LIMIT_CMD_LENGTH ( 2 + 9 ) +#define LR11XX_WIFI_COUNTRY_CODE_TIME_LIMIT_CMD_LENGTH ( 2 + 7 ) +#define LR11XX_WIFI_GET_RESULT_SIZE_CMD_LENGTH ( 2 ) +#define LR11XX_WIFI_READ_RESULT_CMD_LENGTH ( 2 + 3 ) +#define LR11XX_WIFI_RESET_CUMUL_TIMING_CMD_LENGTH ( 2 ) +#define LR11XX_WIFI_READ_CUMUL_TIMING_CMD_LENGTH ( 2 ) +#define LR11XX_WIFI_GET_SIZE_COUNTRY_RESULT_CMD_LENGTH ( 2 ) +#define LR11XX_WIFI_READ_COUNTRY_CODE_CMD_LENGTH ( 2 + 2 ) +#define LR11XX_WIFI_CFG_TIMESTAMP_AP_PHONE_CMD_LENGTH ( 2 + 4 ) +#define LR11XX_WIFI_GET_VERSION_CMD_LENGTH ( 2 ) + +/*! + * @brief Wi-Fi scan power consumption in nA + * + * @note these numbers are given for information, it should be modified according to the used hardware. + */ +#define LR11XX_WIFI_CORRELATION_NA ( 12000000ULL ) +#define LR11XX_WIFI_CAPTURE_NA ( 12000000ULL ) +#define LR11XX_WIFI_DEMODULATION_NA ( 4000000ULL ) + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE TYPES ----------------------------------------------------------- + */ + +/*! + * @brief Operating codes for Wi-Fi-related operations + */ +enum +{ + LR11XX_WIFI_SCAN_OC = 0x0300, + LR11XX_WIFI_SCAN_TIME_LIMIT = 0x0301, + LR11XX_WIFI_SEARCH_COUNTRY_CODE_OC = 0x0302, + LR11XX_WIFI_COUNTRY_CODE_TIME_LIMIT_OC = 0x0303, + LR11XX_WIFI_GET_RESULT_SIZE_OC = 0x0305, + LR11XX_WIFI_READ_RESULT_OC = 0x0306, + LR11XX_WIFI_RESET_CUMUL_TIMING_OC = 0x0307, + LR11XX_WIFI_READ_CUMUL_TIMING_OC = 0x0308, + LR11XX_WIFI_GET_SIZE_COUNTRY_RESULT_OC = 0x0309, + LR11XX_WIFI_READ_COUNTRY_CODE_OC = 0x030A, + LR11XX_WIFI_CONFIGURE_TIMESTAMP_AP_PHONE_OC = 0x030B, + LR11XX_WIFI_GET_VERSION_OC = 0x0320, +}; + +/*! + * @brief Wi-Fi scan results interface + */ +typedef union +{ + lr11xx_wifi_basic_complete_result_t* basic_complete; + lr11xx_wifi_basic_mac_type_channel_result_t* basic_mac_type_channel; + lr11xx_wifi_extended_full_result_t* extended_complete; +} lr11xx_wifi_result_interface_t; + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE VARIABLES ------------------------------------------------------- + */ + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DECLARATION ------------------------------------------- + */ + +/*! + * @brief Return a uint16 value by reading a buffer of uint8 from index. + * + * This function interpret the array MSB first. It is equivalent to: + * return array[index] * 256 + array[index+1] + * + * @returns The uint16 value + */ +static uint16_t uint16_from_array( const uint8_t* array, const uint16_t index ); + +/*! + * @brief Return a uint64 value by reading a buffer of uint8 from index. + * + * This function interpret the array MSB first. + * + * @returns The uint64 value + */ +static uint64_t uint64_from_array( const uint8_t* array, const uint16_t index ); + +/*! + * @brief Propagate the result buffer interpretation depending on the format_code selected + * + * @see interpret_basic_complete_result_from_buffer, interpret_basic_mac_type_channel_result_from_buffer, + * interpret_extended_full_result_from_buffer + */ +static void generic_results_interpreter( const uint8_t n_result_to_parse, const uint8_t index_result_start_writing, + const uint8_t* buffer, lr11xx_wifi_result_interface_t result_interface, + const lr11xx_wifi_result_format_t format_code ); + +/*! + * @brief Parse basic complete result + */ +static void interpret_basic_complete_result_from_buffer( const uint8_t nb_results, + const uint8_t index_result_start_writing, + const uint8_t* buffer, + lr11xx_wifi_basic_complete_result_t* result ); + +/*! + * @brief Parse basic MAC - type - channel result + */ +static void interpret_basic_mac_type_channel_result_from_buffer( const uint8_t nb_results, + const uint8_t index_result_start_writing, + const uint8_t* buffer, + lr11xx_wifi_basic_mac_type_channel_result_t* result ); + +/*! + * @brief Parse extended full result + */ +static void interpret_extended_full_result_from_buffer( const uint8_t nb_results, + const uint8_t index_result_start_writing, const uint8_t* buffer, + lr11xx_wifi_extended_full_result_t* result ); + +/*! + * @brief Parse basic MAC - type - channel result + */ +static lr11xx_status_t fetch_and_aggregate_all_results( const void* context, const uint8_t index_result_start, + const uint8_t nb_results, + const uint8_t nb_results_per_chunk_max, + const lr11xx_wifi_result_format_t result_format_code, + uint8_t* result_buffer, + lr11xx_wifi_result_interface_t result_structures ); + +/*! + * @brief Share the size of a result format + * + * @returns Size in byte of the format given as parameter + */ +static uint8_t lr11xx_wifi_get_result_size_from_format( const lr11xx_wifi_result_format_t format ); + +/*! + * @brief Fetch results from the radio after a successful Wi-Fi passive scan + * + * @returns Operation status + */ +static lr11xx_hal_status_t lr11xx_wifi_read_results_helper( const void* context, const uint8_t start_index, + const uint8_t n_elem, uint8_t* buffer, + const lr11xx_wifi_result_format_t result_format ); + +/*! + * @brief Extract Wi-Fi MAC address from a buffer + */ +static void lr11xx_wifi_read_mac_address_from_buffer( const uint8_t* buffer, const uint16_t index_in_buffer, + lr11xx_wifi_mac_address_t mac_address ); + +/*! + * @brief Share the format code corresponding to a result format + * + * @returns Format code + */ +static uint8_t lr11xx_wifi_get_format_code( const lr11xx_wifi_result_format_t format ); + +/* + * ----------------------------------------------------------------------------- + * --- PUBLIC FUNCTIONS DEFINITION --------------------------------------------- + */ + +lr11xx_status_t lr11xx_wifi_scan( const void* context, const lr11xx_wifi_signal_type_scan_t signal_type, + const lr11xx_wifi_channel_mask_t channels, const lr11xx_wifi_mode_t scan_mode, + const uint8_t max_results, const uint8_t nb_scan_per_channel, + const uint16_t timeout_in_ms, const bool abort_on_timeout ) +{ + const uint8_t cbuffer[LR11XX_WIFI_SCAN_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_SCAN_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_SCAN_OC >> 0 ), + ( uint8_t ) signal_type, + ( uint8_t ) ( channels >> 8 ), + ( uint8_t ) ( channels >> 0 ), + ( uint8_t ) scan_mode, + max_results, + nb_scan_per_channel, + ( uint8_t ) ( timeout_in_ms >> 8 ), + ( uint8_t ) ( timeout_in_ms >> 0 ), + ( uint8_t ) ( ( abort_on_timeout == true ) ? 1 : 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_WIFI_SCAN_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_wifi_search_country_code( const void* context, const lr11xx_wifi_channel_mask_t channels_mask, + const uint8_t nb_max_results, const uint8_t nb_scan_per_channel, + const uint16_t timeout_in_ms, const bool abort_on_timeout ) +{ + const uint8_t cbuffer[LR11XX_WIFI_SEARCH_COUNTRY_CODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_SEARCH_COUNTRY_CODE_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_SEARCH_COUNTRY_CODE_OC >> 0 ), + ( uint8_t ) ( channels_mask >> 8 ), + ( uint8_t ) ( channels_mask >> 0 ), + nb_max_results, + nb_scan_per_channel, + ( uint8_t ) ( timeout_in_ms >> 8 ), + ( uint8_t ) ( timeout_in_ms >> 0 ), + ( uint8_t ) ( ( abort_on_timeout == true ) ? 1 : 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_WIFI_SEARCH_COUNTRY_CODE_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_wifi_scan_time_limit( const void* radio, const lr11xx_wifi_signal_type_scan_t signal_type, + const lr11xx_wifi_channel_mask_t channels, + const lr11xx_wifi_mode_t scan_mode, const uint8_t max_results, + const uint16_t timeout_per_channel_ms, const uint16_t timeout_per_scan_ms ) +{ + const uint8_t cbuffer[LR11XX_WIFI_SCAN_TIME_LIMIT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_SCAN_TIME_LIMIT >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_SCAN_TIME_LIMIT >> 0 ), + ( uint8_t ) signal_type, + ( uint8_t ) ( channels >> 8 ), + ( uint8_t ) ( channels >> 0 ), + ( uint8_t ) scan_mode, + max_results, + ( uint8_t ) ( timeout_per_channel_ms >> 8 ), + ( uint8_t ) ( timeout_per_channel_ms >> 0 ), + ( uint8_t ) ( timeout_per_scan_ms >> 8 ), + ( uint8_t ) ( timeout_per_scan_ms >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( radio, cbuffer, LR11XX_WIFI_SCAN_TIME_LIMIT_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_wifi_search_country_code_time_limit( const void* radio, + const lr11xx_wifi_channel_mask_t channels, + const uint8_t max_results, + const uint16_t timeout_per_channel_ms, + const uint16_t timeout_per_scan_ms ) +{ + const uint8_t cbuffer[LR11XX_WIFI_COUNTRY_CODE_TIME_LIMIT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_COUNTRY_CODE_TIME_LIMIT_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_COUNTRY_CODE_TIME_LIMIT_OC >> 0 ), + ( uint8_t ) ( channels >> 8 ), + ( uint8_t ) ( channels >> 0 ), + ( uint8_t ) max_results, + ( uint8_t ) ( timeout_per_channel_ms >> 8 ), + ( uint8_t ) ( timeout_per_channel_ms >> 0 ), + ( uint8_t ) ( timeout_per_scan_ms >> 8 ), + ( uint8_t ) ( timeout_per_scan_ms >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( radio, cbuffer, LR11XX_WIFI_COUNTRY_CODE_TIME_LIMIT_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_wifi_get_nb_results( const void* context, uint8_t* nb_results ) +{ + const uint8_t cbuffer[LR11XX_WIFI_GET_RESULT_SIZE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_GET_RESULT_SIZE_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_GET_RESULT_SIZE_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_read( context, cbuffer, LR11XX_WIFI_GET_RESULT_SIZE_CMD_LENGTH, nb_results, + sizeof( *nb_results ) ); +} + +lr11xx_status_t lr11xx_wifi_read_basic_complete_results( const void* context, const uint8_t start_result_index, + const uint8_t nb_results, + lr11xx_wifi_basic_complete_result_t* results ) +{ + uint8_t result_buffer[LR11XX_WIFI_MAX_SIZE_PER_SPI( LR11XX_WIFI_BASIC_COMPLETE_RESULT_SIZE )] = { 0 }; + const uint8_t nb_results_per_chunk_max = + LR11XX_WIFI_MAX_RESULT_PER_TRANSACTION( LR11XX_WIFI_BASIC_COMPLETE_RESULT_SIZE ); + + lr11xx_wifi_result_interface_t result_interface = { 0 }; + result_interface.basic_complete = results; + + return fetch_and_aggregate_all_results( context, start_result_index, nb_results, nb_results_per_chunk_max, + LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE, result_buffer, result_interface ); +} + +lr11xx_status_t lr11xx_wifi_read_basic_mac_type_channel_results( const void* context, const uint8_t start_result_index, + const uint8_t nb_results, + lr11xx_wifi_basic_mac_type_channel_result_t* results ) +{ + uint8_t result_buffer[LR11XX_WIFI_MAX_SIZE_PER_SPI( LR11XX_WIFI_BASIC_MAC_TYPE_CHANNEL_RESULT_SIZE )] = { 0 }; + const uint8_t nb_results_per_chunk_max = + LR11XX_WIFI_MAX_RESULT_PER_TRANSACTION( LR11XX_WIFI_BASIC_MAC_TYPE_CHANNEL_RESULT_SIZE ); + + lr11xx_wifi_result_interface_t result_interface = { 0 }; + result_interface.basic_mac_type_channel = results; + + return fetch_and_aggregate_all_results( context, start_result_index, nb_results, nb_results_per_chunk_max, + LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL, result_buffer, + result_interface ); +} + +lr11xx_status_t lr11xx_wifi_read_extended_full_results( const void* radio, const uint8_t start_result_index, + const uint8_t nb_results, + lr11xx_wifi_extended_full_result_t* results ) +{ + uint8_t result_buffer[LR11XX_WIFI_MAX_SIZE_PER_SPI( LR11XX_WIFI_EXTENDED_COMPLETE_RESULT_SIZE )] = { 0 }; + const uint8_t nb_results_per_chunk_max = + LR11XX_WIFI_MAX_RESULT_PER_TRANSACTION( LR11XX_WIFI_EXTENDED_COMPLETE_RESULT_SIZE ); + + lr11xx_wifi_result_interface_t result_interface = { 0 }; + result_interface.extended_complete = results; + + return fetch_and_aggregate_all_results( radio, start_result_index, nb_results, nb_results_per_chunk_max, + LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL, result_buffer, result_interface ); +} + +lr11xx_status_t lr11xx_wifi_reset_cumulative_timing( const void* context ) +{ + const uint8_t cbuffer[LR11XX_WIFI_RESET_CUMUL_TIMING_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_RESET_CUMUL_TIMING_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_RESET_CUMUL_TIMING_OC >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_WIFI_RESET_CUMUL_TIMING_CMD_LENGTH, 0, 0 ); +} + +lr11xx_status_t lr11xx_wifi_read_cumulative_timing( const void* context, lr11xx_wifi_cumulative_timings_t* timing ) +{ + const uint8_t cbuffer[LR11XX_WIFI_READ_CUMUL_TIMING_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_READ_CUMUL_TIMING_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_READ_CUMUL_TIMING_OC >> 0 ), + }; + uint8_t buffer_out[LR11XX_WIFI_ALL_CUMULATIVE_TIMING_SIZE] = { 0 }; + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, LR11XX_WIFI_READ_CUMUL_TIMING_CMD_LENGTH, + buffer_out, LR11XX_WIFI_ALL_CUMULATIVE_TIMING_SIZE ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + timing->rx_detection_us = + ( buffer_out[0] << 24 ) + ( buffer_out[1] << 16 ) + ( buffer_out[2] << 8 ) + buffer_out[3]; + timing->rx_correlation_us = + ( buffer_out[4] << 24 ) + ( buffer_out[5] << 16 ) + ( buffer_out[6] << 8 ) + buffer_out[7]; + timing->rx_capture_us = + ( buffer_out[8] << 24 ) + ( buffer_out[9] << 16 ) + ( buffer_out[10] << 8 ) + buffer_out[11]; + timing->demodulation_us = + ( buffer_out[12] << 24 ) + ( buffer_out[13] << 16 ) + ( buffer_out[14] << 8 ) + buffer_out[15]; + } + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_wifi_get_nb_country_code_results( const void* context, uint8_t* country_result_size ) +{ + const uint8_t cbuffer[LR11XX_WIFI_GET_SIZE_COUNTRY_RESULT_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_GET_SIZE_COUNTRY_RESULT_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_GET_SIZE_COUNTRY_RESULT_OC >> 0 ), + }; + uint8_t rbuffer[LR11XX_WIFI_COUNTRY_RESULT_LENGTH_SIZE] = { 0 }; + + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_WIFI_GET_SIZE_COUNTRY_RESULT_CMD_LENGTH, rbuffer, + LR11XX_WIFI_COUNTRY_RESULT_LENGTH_SIZE ); + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + ( *country_result_size ) = rbuffer[0]; + } + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_wifi_read_country_code_results( const void* context, const uint8_t start_result_index, + const uint8_t nb_country_results, + lr11xx_wifi_country_code_t* country_code_results ) +{ + const uint8_t cbuffer[LR11XX_WIFI_READ_COUNTRY_CODE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_READ_COUNTRY_CODE_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_READ_COUNTRY_CODE_OC >> 0 ), + start_result_index, + nb_country_results, + }; + uint8_t rbuffer[LR11XX_WIFI_MAX_COUNTRY_CODE_RESULT_SIZE] = { 0 }; + const uint16_t country_code_result_size_to_read = + nb_country_results * LR11XX_WIFI_SCAN_SINGLE_COUNTRY_CODE_RESULT_SIZE; + + const lr11xx_hal_status_t hal_status = lr11xx_hal_read( context, cbuffer, LR11XX_WIFI_READ_COUNTRY_CODE_CMD_LENGTH, + rbuffer, country_code_result_size_to_read ); + + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + for( uint8_t result_index = 0; result_index < nb_country_results; result_index++ ) + { + const uint8_t local_index = result_index * LR11XX_WIFI_SCAN_SINGLE_COUNTRY_CODE_RESULT_SIZE; + lr11xx_wifi_country_code_t* local_country_code_result = &country_code_results[result_index]; + + local_country_code_result->country_code[0] = rbuffer[local_index + 0]; + local_country_code_result->country_code[1] = rbuffer[local_index + 1]; + local_country_code_result->io_regulation = rbuffer[local_index + 2]; + local_country_code_result->channel_info_byte = rbuffer[local_index + 3]; + + for( uint8_t field_mac_index = 0; field_mac_index < LR11XX_WIFI_MAC_ADDRESS_LENGTH; field_mac_index++ ) + { + local_country_code_result->mac_address[field_mac_index] = + rbuffer[local_index + ( LR11XX_WIFI_MAC_ADDRESS_LENGTH - field_mac_index - 1 ) + 4]; + } + } + } + return ( lr11xx_status_t ) hal_status; +} + +lr11xx_status_t lr11xx_wifi_cfg_timestamp_ap_phone( const void* context, uint32_t timestamp_in_s ) +{ + const uint8_t cbuffer[LR11XX_WIFI_CFG_TIMESTAMP_AP_PHONE_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_CONFIGURE_TIMESTAMP_AP_PHONE_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_CONFIGURE_TIMESTAMP_AP_PHONE_OC >> 0 ), + ( uint8_t ) ( timestamp_in_s >> 24 ), + ( uint8_t ) ( timestamp_in_s >> 16 ), + ( uint8_t ) ( timestamp_in_s >> 8 ), + ( uint8_t ) ( timestamp_in_s >> 0 ), + }; + + return ( lr11xx_status_t ) lr11xx_hal_write( context, cbuffer, LR11XX_WIFI_CFG_TIMESTAMP_AP_PHONE_CMD_LENGTH, 0, + 0 ); +} + +lr11xx_status_t lr11xx_wifi_read_version( const void* context, lr11xx_wifi_version_t* wifi_version ) +{ + const uint8_t cbuffer[LR11XX_WIFI_GET_VERSION_CMD_LENGTH] = { + ( uint8_t ) ( LR11XX_WIFI_GET_VERSION_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_GET_VERSION_OC >> 0 ), + }; + uint8_t rbuffer[LR11XX_WIFI_VERSION_SIZE] = { 0 }; + const lr11xx_hal_status_t hal_status = + lr11xx_hal_read( context, cbuffer, LR11XX_WIFI_GET_VERSION_CMD_LENGTH, rbuffer, LR11XX_WIFI_VERSION_SIZE ); + if( hal_status == LR11XX_HAL_STATUS_OK ) + { + wifi_version->major = rbuffer[0]; + wifi_version->minor = rbuffer[1]; + } + return ( lr11xx_status_t ) hal_status; +} + +uint8_t lr11xx_wifi_get_nb_results_max_per_chunk( void ) +{ + return ( uint8_t ) LR11XX_WIFI_N_RESULTS_MAX_PER_CHUNK; +} + +void lr11xx_wifi_parse_channel_info( const lr11xx_wifi_channel_info_byte_t channel_info, lr11xx_wifi_channel_t* channel, + bool* rssi_validity, lr11xx_wifi_mac_origin_t* mac_origin_estimation ) +{ + ( *channel ) = lr11xx_wifi_extract_channel_from_info_byte( channel_info ); + ( *mac_origin_estimation ) = ( lr11xx_wifi_mac_origin_t ) ( ( channel_info & 0x30 ) >> 4 ); + ( *rssi_validity ) = ( ( channel_info & 0x40 ) == 0 ) ? true : false; +} + +lr11xx_wifi_channel_t lr11xx_wifi_extract_channel_from_info_byte( const lr11xx_wifi_channel_info_byte_t channel_info ) +{ + return ( lr11xx_wifi_channel_t ) ( channel_info & 0x0F ); +} + +void lr11xx_wifi_parse_frame_type_info( const lr11xx_wifi_frame_type_info_byte_t frame_type_info, + lr11xx_wifi_frame_type_t* frame_type, + lr11xx_wifi_frame_sub_type_t* frame_sub_type, bool* to_ds, bool* from_ds ) +{ + ( *frame_type ) = ( lr11xx_wifi_frame_type_t ) ( ( frame_type_info >> 6 ) & 0x03 ); + ( *frame_sub_type ) = ( lr11xx_wifi_frame_sub_type_t ) ( ( frame_type_info >> 2 ) & 0x0F ); + ( *to_ds ) = ( bool ) ( ( frame_type_info >> 1 ) & 0x01 ); + ( *from_ds ) = ( bool ) ( frame_type_info & 0x01 ); +} + +void lr11xx_wifi_parse_data_rate_info( const lr11xx_wifi_datarate_info_byte_t data_rate_info, + lr11xx_wifi_signal_type_result_t* wifi_signal_type, + lr11xx_wifi_datarate_t* wifi_data_rate ) +{ + ( *wifi_signal_type ) = lr11xx_wifi_extract_signal_type_from_data_rate_info( data_rate_info ); + ( *wifi_data_rate ) = ( lr11xx_wifi_datarate_t ) ( data_rate_info >> 2 ); +} + +lr11xx_wifi_signal_type_result_t lr11xx_wifi_extract_signal_type_from_data_rate_info( + const lr11xx_wifi_datarate_info_byte_t data_rate_info ) +{ + return ( lr11xx_wifi_signal_type_result_t ) ( data_rate_info & 0x03 ); +} + +uint32_t lr11xx_wifi_get_consumption_nah( lr11xx_system_reg_mode_t regulator, lr11xx_wifi_cumulative_timings_t timing ) +{ + float wifi_scan_consumption_nah = 0; + + wifi_scan_consumption_nah = ( ( float ) timing.rx_capture_us * LR11XX_WIFI_CAPTURE_NA ) + + ( ( float ) timing.demodulation_us * LR11XX_WIFI_DEMODULATION_NA ) + + ( ( float ) timing.rx_correlation_us * LR11XX_WIFI_CORRELATION_NA ); + + wifi_scan_consumption_nah = wifi_scan_consumption_nah / + ( 3600000000.0 - ( ( float ) timing.rx_capture_us + ( float ) timing.demodulation_us + + ( float ) timing.rx_correlation_us ) ); + + if( regulator == LR11XX_SYSTEM_REG_MODE_LDO ) + { + wifi_scan_consumption_nah *= 2.0; + } + + return ( uint32_t ) wifi_scan_consumption_nah; +} + +/* + * ----------------------------------------------------------------------------- + * --- PRIVATE FUNCTIONS DEFINITION -------------------------------------------- + */ + +static lr11xx_hal_status_t lr11xx_wifi_read_results_helper( const void* context, const uint8_t start_index, + const uint8_t n_elem, uint8_t* buffer, + const lr11xx_wifi_result_format_t result_format ) +{ + const uint8_t size_single_elem = lr11xx_wifi_get_result_size_from_format( result_format ); + const uint8_t result_format_code = lr11xx_wifi_get_format_code( result_format ); + const uint8_t cbuffer[LR11XX_WIFI_READ_RESULT_CMD_LENGTH] = { ( uint8_t ) ( LR11XX_WIFI_READ_RESULT_OC >> 8 ), + ( uint8_t ) ( LR11XX_WIFI_READ_RESULT_OC & 0x00FF ), + start_index, n_elem, result_format_code }; + const uint16_t size_total = n_elem * size_single_elem; + return lr11xx_hal_read( context, cbuffer, LR11XX_WIFI_READ_RESULT_CMD_LENGTH, buffer, size_total ); +} + +static uint16_t uint16_from_array( const uint8_t* array, const uint16_t index ) +{ + return ( uint16_t ) ( array[index] << 8 ) + ( ( uint16_t ) ( array[index + 1] ) ); +} + +static uint64_t uint64_from_array( const uint8_t* array, const uint16_t index ) +{ + return ( ( uint64_t ) ( array[index] ) << 56 ) + ( ( uint64_t ) ( array[index + 1] ) << 48 ) + + ( ( uint64_t ) ( array[index + 2] ) << 40 ) + ( ( uint64_t ) ( array[index + 3] ) << 32 ) + + ( ( uint64_t ) ( array[index + 4] ) << 24 ) + ( ( uint64_t ) ( array[index + 5] ) << 16 ) + + ( ( uint64_t ) ( array[index + 6] ) << 8 ) + ( uint64_t ) ( array[index + 7] ); +} + +static void lr11xx_wifi_read_mac_address_from_buffer( const uint8_t* buffer, const uint16_t index_in_buffer, + lr11xx_wifi_mac_address_t mac_address ) +{ + for( uint8_t field_mac_index = 0; field_mac_index < LR11XX_WIFI_MAC_ADDRESS_LENGTH; field_mac_index++ ) + { + mac_address[field_mac_index] = buffer[index_in_buffer + field_mac_index]; + } +} + +static uint8_t lr11xx_wifi_get_format_code( const lr11xx_wifi_result_format_t format ) +{ + uint8_t format_code = 0x00; + switch( format ) + { + case LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE: + { + format_code = 0x01; + break; + } + case LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL: + { + format_code = 0x04; + break; + } + case LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL: + { + format_code = 0x01; + break; + } + } + return format_code; +} + +static uint8_t lr11xx_wifi_get_result_size_from_format( const lr11xx_wifi_result_format_t format ) +{ + uint8_t result_size = 0; + switch( format ) + { + case LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE: + { + result_size = LR11XX_WIFI_BASIC_COMPLETE_RESULT_SIZE; + break; + } + case LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL: + { + result_size = LR11XX_WIFI_BASIC_MAC_TYPE_CHANNEL_RESULT_SIZE; + break; + } + case LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL: + { + result_size = LR11XX_WIFI_EXTENDED_COMPLETE_RESULT_SIZE; + break; + } + } + return result_size; +} + +static lr11xx_status_t fetch_and_aggregate_all_results( const void* context, const uint8_t index_result_start, + const uint8_t nb_results, + const uint8_t nb_results_per_chunk_max, + const lr11xx_wifi_result_format_t result_format_code, + uint8_t* result_buffer, + lr11xx_wifi_result_interface_t result_structures ) +{ + uint8_t index_to_read = index_result_start; + uint8_t index_result_start_writing = 0; + uint8_t remaining_results = nb_results; + + lr11xx_hal_status_t hal_status = LR11XX_HAL_STATUS_OK; + while( remaining_results > 0 ) + { + uint8_t results_to_read = MIN( remaining_results, nb_results_per_chunk_max ); + + lr11xx_hal_status_t local_hal_status = lr11xx_wifi_read_results_helper( context, index_to_read, results_to_read, + result_buffer, result_format_code ); + if( local_hal_status != LR11XX_HAL_STATUS_OK ) + { + return ( lr11xx_status_t ) local_hal_status; + } + + generic_results_interpreter( results_to_read, index_result_start_writing, result_buffer, result_structures, + result_format_code ); + + // Reset the content of the result_buffer in case there are still results to fetch + { + const uint16_t result_buffer_size = + LR11XX_WIFI_MAX_SIZE_PER_SPI( lr11xx_wifi_get_result_size_from_format( result_format_code ) ); + for( uint16_t index = 0; index < result_buffer_size; index++ ) + { + result_buffer[index] = 0; + } + } + + index_to_read += results_to_read; + index_result_start_writing += results_to_read; + remaining_results -= results_to_read; + } + return ( lr11xx_status_t ) hal_status; +} + +static void generic_results_interpreter( const uint8_t n_result_to_parse, const uint8_t index_result_start_writing, + const uint8_t* buffer, lr11xx_wifi_result_interface_t result_interface, + const lr11xx_wifi_result_format_t format_code ) +{ + switch( format_code ) + { + case LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE: + { + interpret_basic_complete_result_from_buffer( n_result_to_parse, index_result_start_writing, buffer, + result_interface.basic_complete ); + break; + } + + case LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL: + { + interpret_basic_mac_type_channel_result_from_buffer( n_result_to_parse, index_result_start_writing, buffer, + result_interface.basic_mac_type_channel ); + break; + } + + case LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL: + { + interpret_extended_full_result_from_buffer( n_result_to_parse, index_result_start_writing, buffer, + result_interface.extended_complete ); + break; + } + } +} + +static void interpret_basic_complete_result_from_buffer( const uint8_t nb_results, + const uint8_t index_result_start_writing, + const uint8_t* buffer, + lr11xx_wifi_basic_complete_result_t* result ) +{ + for( uint8_t result_index = 0; result_index < nb_results; result_index++ ) + { + const uint16_t local_index_start = LR11XX_WIFI_BASIC_COMPLETE_RESULT_SIZE * result_index; + lr11xx_wifi_basic_complete_result_t* local_wifi_result = &result[index_result_start_writing + result_index]; + local_wifi_result->data_rate_info_byte = buffer[local_index_start + 0]; + local_wifi_result->channel_info_byte = buffer[local_index_start + 1]; + local_wifi_result->rssi = buffer[local_index_start + 2]; + local_wifi_result->frame_type_info_byte = buffer[local_index_start + 3]; + lr11xx_wifi_read_mac_address_from_buffer( buffer, local_index_start + 4, local_wifi_result->mac_address ); + local_wifi_result->phi_offset = uint16_from_array( buffer, local_index_start + 10 ); + local_wifi_result->timestamp_us = uint64_from_array( buffer, local_index_start + 12 ); + local_wifi_result->beacon_period_tu = uint16_from_array( buffer, local_index_start + 20 ); + } +} + +static void interpret_basic_mac_type_channel_result_from_buffer( const uint8_t nb_results, + const uint8_t index_result_start_writing, + const uint8_t* buffer, + lr11xx_wifi_basic_mac_type_channel_result_t* result ) +{ + for( uint8_t result_index = 0; result_index < nb_results; result_index++ ) + { + const uint16_t local_index_start = LR11XX_WIFI_BASIC_MAC_TYPE_CHANNEL_RESULT_SIZE * result_index; + lr11xx_wifi_basic_mac_type_channel_result_t* local_wifi_result = + &result[index_result_start_writing + result_index]; + local_wifi_result->data_rate_info_byte = buffer[local_index_start + 0]; + local_wifi_result->channel_info_byte = buffer[local_index_start + 1]; + local_wifi_result->rssi = buffer[local_index_start + 2]; + lr11xx_wifi_read_mac_address_from_buffer( buffer, local_index_start + 3, local_wifi_result->mac_address ); + } +} + +void interpret_extended_full_result_from_buffer( const uint8_t nb_results, const uint8_t index_result_start_writing, + const uint8_t* buffer, lr11xx_wifi_extended_full_result_t* result ) +{ + for( uint8_t result_index = 0; result_index < nb_results; result_index++ ) + { + const uint16_t local_index_start = LR11XX_WIFI_EXTENDED_COMPLETE_RESULT_SIZE * result_index; + lr11xx_wifi_extended_full_result_t* local_wifi_result = &result[index_result_start_writing + result_index]; + + local_wifi_result->data_rate_info_byte = buffer[local_index_start + 0]; + local_wifi_result->channel_info_byte = buffer[local_index_start + 1]; + local_wifi_result->rssi = buffer[local_index_start + 2]; + local_wifi_result->rate = buffer[local_index_start + 3]; + local_wifi_result->service = uint16_from_array( buffer, local_index_start + 4 ); + local_wifi_result->length = uint16_from_array( buffer, local_index_start + 6 ); + local_wifi_result->frame_control = uint16_from_array( buffer, local_index_start + 8 ); + lr11xx_wifi_read_mac_address_from_buffer( buffer, local_index_start + 10, local_wifi_result->mac_address_1 ); + lr11xx_wifi_read_mac_address_from_buffer( buffer, local_index_start + 16, local_wifi_result->mac_address_2 ); + lr11xx_wifi_read_mac_address_from_buffer( buffer, local_index_start + 22, local_wifi_result->mac_address_3 ); + local_wifi_result->timestamp_us = uint64_from_array( buffer, local_index_start + 28 ); + local_wifi_result->beacon_period_tu = uint16_from_array( buffer, local_index_start + 36 ); + local_wifi_result->seq_control = uint16_from_array( buffer, local_index_start + 38 ); + for( uint8_t ssid_index = 0; ssid_index < LR11XX_WIFI_RESULT_SSID_LENGTH; ssid_index++ ) + { + local_wifi_result->ssid_bytes[ssid_index] = buffer[local_index_start + ssid_index + 40]; + } + local_wifi_result->current_channel = ( lr11xx_wifi_channel_t ) buffer[local_index_start + 72]; + local_wifi_result->country_code[0] = buffer[local_index_start + 73]; + local_wifi_result->country_code[1] = buffer[local_index_start + 74]; + local_wifi_result->io_regulation = buffer[local_index_start + 75]; + local_wifi_result->fcs_check_byte.is_fcs_checked = ( ( buffer[local_index_start + 76] & 0x01 ) == 0x01 ); + local_wifi_result->fcs_check_byte.is_fcs_ok = ( ( buffer[local_index_start + 76] & 0x02 ) == 0x02 ); + local_wifi_result->phi_offset = uint16_from_array( buffer, local_index_start + 77 ); + } +} + +bool lr11xx_wifi_is_well_formed_utf8_byte_sequence( const uint8_t* buffer, const uint8_t length ) +{ + uint8_t index = 0; + + while( index < length ) + { + if( IS_INFERIOR( buffer[index], 0x7F ) ) + { + index += 1; + continue; + } + + if( length - index >= 2 ) + { + if( IS_BETWEEN( buffer[index], 0xC2, 0xDF ) && IS_BETWEEN_0x80_AND_0xBF( buffer[index + 1] ) ) + { + index += 2; + continue; + } + + if( length - index >= 3 ) + { + if( ( buffer[index] == 0xE0 ) && IS_BETWEEN( buffer[index + 1], 0xA0, 0xBF ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) ) + { + index += 3; + continue; + } + else if( IS_BETWEEN( buffer[index], 0xE1, 0xEC ) && IS_BETWEEN_0x80_AND_0xBF( buffer[index + 1] ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) ) + { + index += 3; + continue; + } + else if( ( buffer[index] == 0xED ) && IS_BETWEEN( buffer[index + 1], 0x80, 0x9F ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) ) + { + index += 3; + continue; + } + else if( IS_BETWEEN( buffer[index], 0xEE, 0xEF ) && IS_BETWEEN_0x80_AND_0xBF( buffer[index + 1] ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) ) + { + index += 3; + continue; + } + + if( length - index >= 4 ) + { + if( ( buffer[index] == 0xF0 ) && IS_BETWEEN( buffer[index + 1], 0x90, 0xBF ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) && IS_BETWEEN_0x80_AND_0xBF( buffer[index + 3] ) ) + { + index += 4; + continue; + } + else if( IS_BETWEEN( buffer[index], 0xF1, 0xF3 ) && IS_BETWEEN_0x80_AND_0xBF( buffer[index + 1] ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 3] ) ) + { + index += 4; + continue; + } + else if( ( buffer[index] == 0xF4 ) && IS_BETWEEN( buffer[index + 1], 0x80, 0x8F ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 2] ) && + IS_BETWEEN_0x80_AND_0xBF( buffer[index + 3] ) ) + { + index += 4; + continue; + } + } + } + } + + return false; + } + + return true; +} + +bool lr11xx_wifi_are_scan_mode_result_format_compatible( lr11xx_wifi_mode_t scan_mode, + lr11xx_wifi_result_format_t result_format ) +{ + switch( scan_mode ) + { + case LR11XX_WIFI_SCAN_MODE_BEACON: + case LR11XX_WIFI_SCAN_MODE_BEACON_AND_PKT: + { + switch( result_format ) + { + case LR11XX_WIFI_RESULT_FORMAT_BASIC_COMPLETE: + case LR11XX_WIFI_RESULT_FORMAT_BASIC_MAC_TYPE_CHANNEL: + { + return true; + } + + default: + { + return false; + } + } + break; + } + + case LR11XX_WIFI_SCAN_MODE_FULL_BEACON: + case LR11XX_WIFI_SCAN_MODE_UNTIL_SSID: + { + switch( result_format ) + { + case LR11XX_WIFI_RESULT_FORMAT_EXTENDED_FULL: + { + return true; + } + default: + { + return false; + } + } + } + + default: + { + return false; + } + } +} + +/* --- EOF ------------------------------------------------------------------ */ diff --git a/components/esp_lora_1121/src/lr11xx_hal.c b/components/esp_lora_1121/src/lr11xx_hal.c new file mode 100755 index 0000000..65e41da --- /dev/null +++ b/components/esp_lora_1121/src/lr11xx_hal.c @@ -0,0 +1,220 @@ +/*! + * @file lr11xx_hal.c + * + * @brief Hardware Abstraction Layer (HAL) implementation for lr1121 + * + * The Clear BSD License + * Copyright Semtech Corporation 2024. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ----------------------------------------------------------------------------- + * --- DEPENDENCIES ------------------------------------------------------------ + */ + +#include +#include +#include "esp_lora_1121.h" + +/*! + * @brief lr11xx_hal.h API implementation + */ + +/*! + * @brief Function to wait the that lr1121 modem-e busy line raise to high + * + * @param [in] context Chip implementation context + * @param [in] timeout_ms timeout in millisec before leave the function + * + * @returns lr1121_hal_status_t + */ +static lr11xx_hal_status_t lr11xx_hal_wait_on_unbusy(const void *context, uint32_t timeout_ms); + +lr11xx_hal_status_t lr11xx_hal_write(const void *context, const uint8_t *command, + const uint16_t command_length, const uint8_t *data, + const uint16_t data_length) +{ +#if defined(USE_LR11XX_CRC_OVER_SPI) + uint8_t cmd_crc = lr11xx_hal_compute_crc(0xFF, command, command_length); + if (data_length > 0){ + cmd_crc = lr11xx_hal_compute_crc(cmd_crc, data, data_length); + } + +#endif + + if (lr11xx_hal_wait_on_unbusy(context, 10000) == LR11XX_HAL_STATUS_OK) + { + /* NSS low */ + gpio_set_level(((lr1121_t *)context)->cs, 0); + /* Send CMD */ + lora_spi_write_bytes(context, (uint8_t *)command, command_length); + /* Send Data */ + if (data_length > 0) + { + lora_spi_write_bytes(context, (uint8_t *)data, data_length); + } +#if defined(USE_LR11XX_CRC_OVER_SPI) + lora_spi_write_bytes(context, &cmd_crc, 1); +#endif + /* NSS high */ + gpio_set_level(((lr1121_t *)context)->cs, 1); + + return LR11XX_HAL_STATUS_OK; + } + return LR11XX_HAL_STATUS_ERROR; +} + +lr11xx_hal_status_t lr11xx_hal_read(const void *context, const uint8_t *command, + const uint16_t command_length, uint8_t *data, + const uint16_t data_length) +{ +#if defined(USE_LR11XX_CRC_OVER_SPI) + const uint8_t cmd_crc = lr11xx_hal_compute_crc(0xFF, command, command_length); +#endif + uint8_t dummy_byte_rx = LR11XX_NOP; + + if (lr11xx_hal_wait_on_unbusy(context, 10000) == LR11XX_HAL_STATUS_OK) + { + /* NSS low */ + gpio_set_level(((lr1121_t *)context)->cs, 0); + /* Send CMD */ + // uint8_t rx_data[16] = {0}; + lora_spi_write_bytes(context, (uint8_t *)command, command_length); +#if defined(USE_LR11XX_CRC_OVER_SPI) + lora_spi_write_bytes(context, &cmd_crc, 1); +#endif + /* NSS high */ + gpio_set_level(((lr1121_t *)context)->cs, 1); + + /* Wait on busy pin up to 1000 ms */ + if (lr11xx_hal_wait_on_unbusy(context, 1000) != LR11XX_HAL_STATUS_OK) + { + return LR11XX_HAL_STATUS_ERROR; + } + + /* NSS low */ + gpio_set_level(((lr1121_t *)context)->cs, 0); + + /* dummy read */ + lora_spi_read_bytes(context, &dummy_byte_rx, 1); + lora_spi_read_bytes(context, data, data_length); + +#if defined(USE_LR11XX_CRC_OVER_SPI) + uint8_t crc_rx; + lora_spi_read_bytes(context, &crc_rx, 1); +#endif + /* NSS high */ + gpio_set_level(((lr1121_t *)context)->cs, 1); + +#if defined( USE_LR11XX_CRC_OVER_SPI ) + uint8_t crc_computed = lr11xx_hal_compute_crc( 0xFF, &dummy_byte_rx, 1 ); + if(data_length > 0) + { + crc_computed = lr11xx_hal_compute_crc( crc_computed, data, data_length ); + } + + if( crc_rx != crc_computed ) + { + return LR11XX_HAL_STATUS_ERROR; + } +#endif + return LR11XX_HAL_STATUS_OK; + } + return LR11XX_HAL_STATUS_ERROR; +} + +lr11xx_hal_status_t lr11xx_hal_direct_read(const void *context, uint8_t *data, + const uint16_t data_length) +{ + if (lr11xx_hal_wait_on_unbusy(context, 10000) == LR11XX_HAL_STATUS_OK) + { + /* NSS low */ + gpio_set_level(((lr1121_t *)context)->cs, 0); + + lora_spi_read_bytes(context, data, data_length); + + /* NSS high */ + gpio_set_level(((lr1121_t *)context)->cs, 1); + + return LR11XX_HAL_STATUS_OK; + } + return LR11XX_HAL_STATUS_ERROR; +} + +lr11xx_hal_status_t lr11xx_hal_reset(const void *context) +{ + + if (((lr1121_t *)context)->reset < 0) + { + return LR11XX_HAL_STATUS_OK; + } + + gpio_set_level(((lr1121_t *)context)->reset, 0); + vTaskDelay(10 / portTICK_PERIOD_MS); + gpio_set_level(((lr1121_t *)context)->reset, 1); + + return LR11XX_HAL_STATUS_OK; +} + +lr11xx_hal_status_t lr11xx_hal_wakeup(const void *context) +{ + /* Wakeup radio */ + gpio_set_level(((lr1121_t *)context)->cs, 0); + vTaskDelay(10 / portTICK_PERIOD_MS); + gpio_set_level(((lr1121_t *)context)->cs, 1); + + /* Wait on busy pin for 1000 ms */ + return LR11XX_HAL_STATUS_OK; +} + +static lr11xx_hal_status_t lr11xx_hal_wait_on_unbusy(const void *context, uint32_t timeout_ms) +{ +#if 0 + while( gpio_get_level( ( ( lr1121_t* ) context )->busy ) == 1 ) + { + ; + } +#else + if (((lr1121_t *)context)->busy < 0) + { + return LR11XX_HAL_STATUS_OK; + } + uint32_t start = esp_timer_get_time() / 1000; + uint32_t current = 0; + while (gpio_get_level(((lr1121_t *)context)->busy) == 1) + { + + current = esp_timer_get_time() / 1000; + if ((int32_t)(current - start) > (int32_t)timeout_ms) + { + return LR11XX_HAL_STATUS_ERROR; + } + } +#endif + return LR11XX_HAL_STATUS_OK; +} diff --git a/components/esp_lora_1121/test_app/CMakeLists.txt b/components/esp_lora_1121/test_app/CMakeLists.txt new file mode 100755 index 0000000..d10b1a2 --- /dev/null +++ b/components/esp_lora_1121/test_app/CMakeLists.txt @@ -0,0 +1,8 @@ +# 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.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(test_lr1121) diff --git a/components/esp_lora_1121/test_app/main/CMakeLists.txt b/components/esp_lora_1121/test_app/main/CMakeLists.txt new file mode 100755 index 0000000..34b2eca --- /dev/null +++ b/components/esp_lora_1121/test_app/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "lr1121_config.c" "test_lr1121.c" + PRIV_REQUIRES spi_flash driver + INCLUDE_DIRS "") diff --git a/components/esp_lora_1121/test_app/main/idf_component.yml b/components/esp_lora_1121/test_app/main/idf_component.yml new file mode 100755 index 0000000..aa11120 --- /dev/null +++ b/components/esp_lora_1121/test_app/main/idf_component.yml @@ -0,0 +1,8 @@ +## IDF Component Manager Manifest File +dependencies: + ## Required IDF version + idf: ">=5.3.0" + + esp_lora_1121: + version: "*" + path: "../../../esp_lora_1121" \ No newline at end of file diff --git a/components/esp_lora_1121/test_app/main/lr1121_config.c b/components/esp_lora_1121/test_app/main/lr1121_config.c new file mode 100755 index 0000000..6dbbac7 --- /dev/null +++ b/components/esp_lora_1121/test_app/main/lr1121_config.c @@ -0,0 +1,394 @@ +/*! + * @file lr1121_config.c + * + * @brief Common functions shared by the examples + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lr1121_config.h" + +lr1121_t lr1121; + +// LoRa modulation parameters +static lr11xx_radio_mod_params_lora_t lora_mod_params = { + .sf = LORA_SPREADING_FACTOR, // Spreading factor + .bw = LORA_BANDWIDTH, // Bandwidth + .cr = LORA_CODING_RATE, // Coding rate + .ldro = 0 // Low Data Rate Optimization (initialized in radio init) +}; + +// LoRa packet parameters +static const lr11xx_radio_pkt_params_lora_t lora_pkt_params = { + .preamble_len_in_symb = LORA_PREAMBLE_LENGTH, // Preamble length in symbols + .header_type = LORA_PKT_LEN_MODE, // Header type (implicit or explicit) + .pld_len_in_bytes = PAYLOAD_LENGTH, // Payload length in bytes + .crc = LORA_CRC, // CRC mode + .iq = LORA_IQ, // IQ inversion +}; + +// GFSK modulation parameters +static const lr11xx_radio_mod_params_gfsk_t gfsk_mod_params = { + .br_in_bps = FSK_BITRATE, // Bitrate in bps + .pulse_shape = FSK_PULSE_SHAPE, // Pulse shape + .bw_dsb_param = FSK_BANDWIDTH, // Bandwidth parameter + .fdev_in_hz = FSK_FDEV, // Frequency deviation in Hz +}; + +// GFSK packet parameters +static const lr11xx_radio_pkt_params_gfsk_t gfsk_pkt_params = { + .preamble_len_in_bits = FSK_PREAMBLE_LENGTH, // Preamble length in bits + .preamble_detector = FSK_PREAMBLE_DETECTOR, // Preamble detector type + .sync_word_len_in_bits = FSK_SYNCWORD_LENGTH, // Sync word length in bits + .address_filtering = FSK_ADDRESS_FILTERING, // Address filtering mode + .header_type = FSK_HEADER_TYPE, // Header type + .pld_len_in_bytes = PAYLOAD_LENGTH, // Payload length in bytes + .crc_type = FSK_CRC_TYPE, // CRC type + .dc_free = FSK_DC_FREE, // DC-free encoding mode +}; + +static const lr11xx_radio_mod_params_bpsk_t bpsk_mod_params = { + .br_in_bps = BPSK_BITRATE_IN_BPS, + .pulse_shape = LR11XX_RADIO_DBPSK_PULSE_SHAPE, +}; + +static lr11xx_radio_pkt_params_bpsk_t bpsk_pkt_params = { + .pld_len_in_bytes = 0, // Will be initialized in radio init + .ramp_up_delay = 0, + .ramp_down_delay = 0, + .pld_len_in_bits = 0, // Will be initialized in radio init +}; + +void print_lora_configuration( void ); +void print_gfsk_configuration( void ); + +// Initialize the LR1121 system +void lora_system_init( const void* context ) +{ + lr11xx_system_reset( ( void* ) context ); // Reset the LR1121 system + lr11xx_hal_wakeup( ( void* ) context ); // Wake up the device + + // Enable or disable CRC over SPI +#if defined(USE_LR11XX_CRC_OVER_SPI) + lr11xx_system_enable_spi_crc(( void* ) context, true); +#else + lr11xx_system_enable_spi_crc(( void* ) context, false); +#endif + + // Set the LR1121 to standby mode using the external oscillator + lr11xx_system_set_standby(( void* ) context, LR11XX_SYSTEM_STANDBY_CFG_XOSC); + // Calibrate the image + lr11xx_system_calibrate_image(( void* ) context,0x6B,0x6E); // Calibrate for 430~440MHz + // lr11xx_system_calibrate_image(( void* ) context,0xD7,0xDB); // Calibrate for 863~870MHz + + // Configure the regulator mode + const lr11xx_system_reg_mode_t regulator = smtc_shield_lr11xx_common_get_reg_mode(); + lr11xx_system_set_reg_mode( ( void* ) context, regulator ); + + // Configure the RF switch + const lr11xx_system_rfswitch_cfg_t* rf_switch_setup = smtc_shield_lr11xx_common_get_rf_switch_cfg(); + lr11xx_system_set_dio_as_rf_switch( context, rf_switch_setup ); + + // Enable the TCXO + lr11xx_system_set_tcxo_mode( context, LR11XX_SYSTEM_TCXO_CTRL_3_0V, 300 ); + + // Configure the low-frequency clock source + lr11xx_system_cfg_lfclk( context, LR11XX_SYSTEM_LFCLK_XTAL, true ); + + // Clear all pending error flags + lr11xx_system_clear_errors( context ); + // Calibrate the system + lr11xx_system_calibrate( context, 0x3F ); + + uint16_t errors; + // Retrieve system errors + lr11xx_system_get_errors( context, &errors ); + if(errors & LR11XX_SYSTEM_ERRORS_IMG_CALIB_MASK) + { + printf("Image calibration error\r\n"); + } + // Clear all pending error flags + lr11xx_system_clear_errors( context ); + + // Clear all pending IRQ status bits + lr11xx_system_clear_irq_status( context, LR11XX_SYSTEM_IRQ_ALL_MASK ); +} + +// Initialize the LR1121 radio module +void lora_radio_init( const void* context ) +{ + // Retrieve the PA power configuration for the target frequency and power level + const smtc_shield_lr11xx_pa_pwr_cfg_t* pa_pwr_cfg = + smtc_shield_lr1121mb1gis_get_pa_pwr_cfg( RF_FREQ_IN_HZ, TX_OUTPUT_POWER_DBM ); + + if( pa_pwr_cfg == NULL ) + { + printf( "Invalid target frequency or power level\n" ); + while( true ) + { + } + } + + // Print common configuration parameters + printf( "Common parameters:\n" ); + printf( " Packet type = %s\n", lr11xx_radio_pkt_type_to_str( PACKET_TYPE ) ); + printf( " RF frequency = %u Hz\n", RF_FREQ_IN_HZ ); + printf( " Output power = %i dBm\n", TX_OUTPUT_POWER_DBM ); + printf( " Fallback mode = %s\n", lr11xx_radio_fallback_modes_to_str( FALLBACK_MODE ) ); + printf( ( ENABLE_RX_BOOST_MODE == true ) ? " Rx boost activated\n" : " Rx boost deactivated\n" ); + printf( "\n" ); + + // Set the packet type + lr11xx_radio_set_pkt_type( context, PACKET_TYPE ); + + // Verify the packet type setting + lr11xx_radio_pkt_type_t spi_check; + lr11xx_radio_get_pkt_type(context, &spi_check); + if(spi_check == LR11XX_RADIO_PKT_TYPE_LORA) + { + printf("LoRa modulation\r\n" ); + } + else if(spi_check == LR11XX_RADIO_PKT_TYPE_GFSK) + { + printf("GFSK modulation\r\n" ); + } + else + printf("spi_check_err\r\n" ); + + // Set the RF frequency + lr11xx_radio_set_rf_freq( context, RF_FREQ_IN_HZ ); + + // Set the RSSI calibration table + lr11xx_radio_set_rssi_calibration(context, smtc_shield_lr11xx_get_rssi_calibration_table( RF_FREQ_IN_HZ )); + + // Configure the PA settings + lr11xx_radio_set_pa_cfg( context, &( pa_pwr_cfg->pa_config ) ); + + // Set the TX power and ramp time + lr11xx_radio_set_tx_params( context, pa_pwr_cfg->power, PA_RAMP_TIME ); + + // Set the fallback mode after TX/RX operations + lr11xx_radio_set_rx_tx_fallback_mode( context, FALLBACK_MODE ); + // Configure the RX boost mode + lr11xx_radio_cfg_rx_boosted( context, ENABLE_RX_BOOST_MODE ); + + // Configure LoRa or GFSK parameters based on the packet type + if( PACKET_TYPE == LR11XX_RADIO_PKT_TYPE_LORA ) + { + print_lora_configuration( ); + lora_mod_params.ldro = smtc_shield_lr11xx_common_compute_lora_ldro( LORA_SPREADING_FACTOR, LORA_BANDWIDTH ); + lr11xx_radio_set_lora_mod_params( context, &lora_mod_params ); + lr11xx_radio_set_lora_pkt_params( context, &lora_pkt_params ); + lr11xx_radio_set_lora_sync_word( context, LORA_SYNCWORD ); + } + // Configure the radio for GFSK modulation + else if( PACKET_TYPE == LR11XX_RADIO_PKT_TYPE_GFSK ) + { + // Print the current GFSK configuration + print_gfsk_configuration( ); + + // Set the GFSK modulation parameters + lr11xx_radio_set_gfsk_mod_params( context, &gfsk_mod_params ); + // Set the GFSK packet parameters + lr11xx_radio_set_gfsk_pkt_params( context, &gfsk_pkt_params ); + // Set the GFSK sync word + lr11xx_radio_set_gfsk_sync_word( context, gfsk_sync_word ); + + // If DC-free encoding is enabled, set the whitening seed + if( FSK_DC_FREE != LR11XX_RADIO_GFSK_DC_FREE_OFF ) + { + lr11xx_radio_set_gfsk_whitening_seed( context, FSK_WHITENING_SEED ); + } + + // If CRC is enabled, set the CRC parameters + if( FSK_CRC_TYPE != LR11XX_RADIO_GFSK_CRC_OFF ) + { + lr11xx_radio_set_gfsk_crc_params( context, FSK_CRC_SEED, FSK_CRC_POLYNOMIAL ); + } + + // If address filtering is enabled, set the packet address + if( FSK_ADDRESS_FILTERING != LR11XX_RADIO_GFSK_ADDRESS_FILTERING_DISABLE ) + { + lr11xx_radio_set_pkt_address( context, FSK_NODE_ADDRESS, FSK_BROADCAST_ADDRESS ); + } + } + // Configure the radio for LR-FHSS modulation + else if( PACKET_TYPE == LR11XX_RADIO_PKT_TYPE_LR_FHSS ) + { + // Define the LR-FHSS modulation parameters + const lr11xx_radio_mod_params_lr_fhss_t mod_lr_fhss = { + .br_in_bps = LR11XX_RADIO_LR_FHSS_BITRATE_488_BPS, // Bitrate in bps + .pulse_shape = LR11XX_RADIO_LR_FHSS_PULSE_SHAPE_BT_1, // Pulse shape + }; + + // Set the LR-FHSS modulation parameters + lr11xx_radio_set_lr_fhss_mod_params( context, &mod_lr_fhss ); + } +} + +void lora_radio_dbpsk_init( const void* context, const uint8_t payload_len ) +{ + const smtc_shield_lr11xx_pa_pwr_cfg_t* pa_pwr_cfg = + smtc_shield_lr1121mb1gis_get_pa_pwr_cfg( SIGFOX_UPLINK_RF_FREQ_IN_HZ, SIGFOX_TX_OUTPUT_POWER_DBM ); + + if( pa_pwr_cfg == NULL ) + { + printf( "Invalid target frequency or power level\n" ); + while( true ) + { + } + } + + printf( "Sigfox parameters:\n" ); + printf( " Packet type = %s\n", lr11xx_radio_pkt_type_to_str( LR11XX_RADIO_PKT_TYPE_BPSK ) ); + printf( " RF frequency = %u Hz\n", SIGFOX_UPLINK_RF_FREQ_IN_HZ ); + printf( " Output power = %i dBm\n", SIGFOX_TX_OUTPUT_POWER_DBM ); + + lr11xx_radio_set_pkt_type( context, LR11XX_RADIO_PKT_TYPE_BPSK ); + lr11xx_radio_set_rf_freq( context, SIGFOX_UPLINK_RF_FREQ_IN_HZ ); + lr11xx_radio_set_rssi_calibration( + context, smtc_shield_lr11xx_get_rssi_calibration_table( SIGFOX_UPLINK_RF_FREQ_IN_HZ ) ); + lr11xx_radio_set_pa_cfg( context, &( pa_pwr_cfg->pa_config ) ); + + lr11xx_radio_set_tx_params( context, pa_pwr_cfg->power, PA_RAMP_TIME ) ; + + lr11xx_radio_set_bpsk_mod_params( context, &bpsk_mod_params ); + + bpsk_pkt_params.pld_len_in_bytes = smtc_dbpsk_get_pld_len_in_bytes( payload_len << 3 ); + bpsk_pkt_params.pld_len_in_bits = smtc_dbpsk_get_pld_len_in_bits( payload_len << 3 ); + + if( BPSK_BITRATE_IN_BPS == 100 ) + { + bpsk_pkt_params.ramp_up_delay = LR11XX_RADIO_SIGFOX_DBPSK_RAMP_UP_TIME_100_BPS; + bpsk_pkt_params.ramp_down_delay = LR11XX_RADIO_SIGFOX_DBPSK_RAMP_DOWN_TIME_100_BPS; + } + else if( BPSK_BITRATE_IN_BPS == 600 ) + { + bpsk_pkt_params.ramp_up_delay = LR11XX_RADIO_SIGFOX_DBPSK_RAMP_UP_TIME_600_BPS; + bpsk_pkt_params.ramp_down_delay = LR11XX_RADIO_SIGFOX_DBPSK_RAMP_DOWN_TIME_600_BPS; + } + else + { + bpsk_pkt_params.ramp_up_delay = LR11XX_RADIO_SIGFOX_DBPSK_RAMP_UP_TIME_DEFAULT; + bpsk_pkt_params.ramp_down_delay = LR11XX_RADIO_SIGFOX_DBPSK_RAMP_DOWN_TIME_DEFAULT; + } + + lr11xx_radio_set_bpsk_pkt_params( context, &bpsk_pkt_params ); +} + + +// Print the LoRa configuration parameters +void print_lora_configuration(void) +{ + // Print LoRa modulation parameters + printf( "LoRa modulation parameters:\n" ); + printf( " Spreading factor = %s\n", lr11xx_radio_lora_sf_to_str( LORA_SPREADING_FACTOR ) ); // Spreading factor + printf( " Bandwidth = %s\n", lr11xx_radio_lora_bw_to_str( LORA_BANDWIDTH ) ); // Bandwidth + printf( " Coding rate = %s\n", lr11xx_radio_lora_cr_to_str( LORA_CODING_RATE ) ); // Coding rate + printf( "\n" ); + + // Print LoRa packet parameters + printf( "LoRa packet parameters:\n" ); + printf( " Preamble length = %d symbol(s)\n", LORA_PREAMBLE_LENGTH ); // Preamble length in symbols + printf( " Header mode = %s\n", lr11xx_radio_lora_pkt_len_modes_to_str( LORA_PKT_LEN_MODE ) ); // Header mode + printf( " Payload length = %d byte(s)\n", PAYLOAD_LENGTH ); // Payload length in bytes + printf( " CRC mode = %s\n", lr11xx_radio_lora_crc_to_str( LORA_CRC ) ); // CRC mode + printf( " IQ = %s\n", lr11xx_radio_lora_iq_to_str( LORA_IQ ) ); // IQ inversion + printf( "\n" ); + + // Print LoRa syncword + printf( "LoRa syncword = 0x%02X\n", LORA_SYNCWORD ); + printf( "\n" ); +} + +// Print the GFSK configuration parameters +void print_gfsk_configuration( void ) +{ + // Print GFSK modulation parameters + printf( "GFSK modulation parameters:\n" ); + printf( " Bitrate = %u bps\n", FSK_BITRATE ); // Bitrate in bps + printf( " Pulse shape = %s\n", lr11xx_radio_gfsk_pulse_shape_to_str( FSK_PULSE_SHAPE ) ); // Pulse shape + printf( " Bandwidth = %s\n", lr11xx_radio_gfsk_bw_to_str( FSK_BANDWIDTH ) ); // Bandwidth + printf( " Frequency deviation = %u Hz\n", FSK_FDEV ); // Frequency deviation in Hz + printf( "\n" ); + + // Print GFSK packet parameters + printf( "GFSK packet parameters:\n" ); + printf( " Preamble length = %d bit(s)\n", FSK_PREAMBLE_LENGTH ); // Preamble length in bits + printf( " Preamble detector = %s\n", lr11xx_radio_gfsk_preamble_detector_to_str( FSK_PREAMBLE_DETECTOR ) ); // Preamble detector + printf( " Syncword length = %d bit(s)\n", FSK_SYNCWORD_LENGTH ); // Syncword length in bits + printf( " Address filtering = %s\n", lr11xx_radio_gfsk_address_filtering_to_str( FSK_ADDRESS_FILTERING ) ); // Address filtering mode + if( FSK_ADDRESS_FILTERING != LR11XX_RADIO_GFSK_ADDRESS_FILTERING_DISABLE ) + { + printf( " (Node address = 0x%02X)\n", FSK_NODE_ADDRESS ); // Node address + if( FSK_ADDRESS_FILTERING == LR11XX_RADIO_GFSK_ADDRESS_FILTERING_NODE_AND_BROADCAST_ADDRESSES ) + { + printf( " (Broadcast address = 0x%02X)\n", FSK_BROADCAST_ADDRESS ); // Broadcast address + } + } + printf( " Header mode = %s\n", lr11xx_radio_gfsk_pkt_len_modes_to_str( FSK_HEADER_TYPE ) ); // Header mode + printf( " Payload length = %d byte(s)\n", PAYLOAD_LENGTH ); // Payload length in bytes + printf( " CRC mode = %s\n", lr11xx_radio_gfsk_crc_type_to_str( FSK_CRC_TYPE ) ); // CRC mode + if( FSK_CRC_TYPE != LR11XX_RADIO_GFSK_CRC_OFF ) + { + printf( " (CRC seed = 0x%08X)\n", FSK_CRC_SEED ); // CRC seed + printf( " (CRC polynomial = 0x%08X)\n", FSK_CRC_POLYNOMIAL ); // CRC polynomial + } + printf( " DC free = %s\n", lr11xx_radio_gfsk_dc_free_to_str( FSK_DC_FREE ) ); // DC-free encoding mode + if( FSK_DC_FREE != LR11XX_RADIO_GFSK_DC_FREE_OFF ) + { + printf( " (Whitening seed = 0x%04X)\n", FSK_WHITENING_SEED ); // Whitening seed + } + printf( "\n" ); +} + +// Calculate the time on air for the configured packet +uint32_t get_time_on_air_in_ms( void ) +{ + // Determine the time on air based on the packet type + switch( PACKET_TYPE ) + { + case LR11XX_RADIO_PKT_TYPE_LORA: + { + // Calculate time on air for LoRa + return lr11xx_radio_get_lora_time_on_air_in_ms( &lora_pkt_params, &lora_mod_params ); + } + case LR11XX_RADIO_PKT_TYPE_GFSK: + { + // Calculate time on air for GFSK + return lr11xx_radio_get_gfsk_time_on_air_in_ms( &gfsk_pkt_params, &gfsk_mod_params ); + } + default: + { + // Return 0 if the packet type is not recognized + return 0; + } + } +} \ No newline at end of file diff --git a/components/esp_lora_1121/test_app/main/lr1121_config.h b/components/esp_lora_1121/test_app/main/lr1121_config.h new file mode 100755 index 0000000..465e94a --- /dev/null +++ b/components/esp_lora_1121/test_app/main/lr1121_config.h @@ -0,0 +1,212 @@ +/*! + * @file lr1121_config.h + * + * @brief Common functions shared by the examples + * + * @copyright + * The Clear BSD License + * Copyright Semtech Corporation 2022. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the disclaimer + * below) provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Semtech corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY + * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LR1121_CONFIG_H +#define LR1121_CONFIG_H + +#include "esp_lora_1121.h" + +#define RX_CONTINUOUS 0xFFFFFF + +/*! + * @brief General parameters + */ +#define PACKET_TYPE LR11XX_RADIO_PKT_TYPE_LORA //LR11XX_RADIO_PKT_TYPE_GFSK LR11XX_RADIO_PKT_TYPE_LORA +#define RF_FREQ_IN_HZ 434 * 1000 * 1000 +#define TX_OUTPUT_POWER_DBM 22 //-9~22 +#define PA_RAMP_TIME LR11XX_RADIO_RAMP_48_US +#define FALLBACK_MODE LR11XX_RADIO_FALLBACK_STDBY_RC +#define ENABLE_RX_BOOST_MODE true +#define PAYLOAD_LENGTH 7 + +/*! + * @brief Modulation parameters for LoRa packets + */ +#define LORA_SPREADING_FACTOR LR11XX_RADIO_LORA_SF7 +#define LORA_BANDWIDTH LR11XX_RADIO_LORA_BW_125 +#define LORA_CODING_RATE LR11XX_RADIO_LORA_CR_4_5 + +/*! + * @brief Packet parameters for LoRa packets + */ +#define LORA_PREAMBLE_LENGTH 8 +#define LORA_PKT_LEN_MODE LR11XX_RADIO_LORA_PKT_EXPLICIT +#define LORA_IQ LR11XX_RADIO_LORA_IQ_STANDARD +#define LORA_CRC LR11XX_RADIO_LORA_CRC_OFF + +#define LORA_SYNCWORD 0x12 // 0x12 Private Network, 0x34 Public Network + +/*! + * @brief Modulation parameters for GFSK packets + */ +#ifndef FSK_FDEV +#define FSK_FDEV 25000U // Hz +#endif +#ifndef FSK_BITRATE +#define FSK_BITRATE 50000U // bps +#endif +#ifndef FSK_BANDWIDTH +#define FSK_BANDWIDTH LR11XX_RADIO_GFSK_BW_117300 // Make sure to follow the rule: (2 * FDEV + BITRATE) < BW +#endif +#ifndef FSK_PULSE_SHAPE +#define FSK_PULSE_SHAPE LR11XX_RADIO_GFSK_PULSE_SHAPE_OFF +#endif + +/*! + * @brief Packet parameters for GFSK packets + */ +#ifndef FSK_PREAMBLE_LENGTH +#define FSK_PREAMBLE_LENGTH 32 // bits +#endif +#ifndef FSK_PREAMBLE_DETECTOR +#define FSK_PREAMBLE_DETECTOR LR11XX_RADIO_GFSK_PREAMBLE_DETECTOR_MIN_16BITS +#endif +#ifndef FSK_SYNCWORD_LENGTH +#define FSK_SYNCWORD_LENGTH 40 // bits +#endif +#ifndef FSK_ADDRESS_FILTERING +#define FSK_ADDRESS_FILTERING LR11XX_RADIO_GFSK_ADDRESS_FILTERING_DISABLE +#endif +#ifndef FSK_HEADER_TYPE +#define FSK_HEADER_TYPE LR11XX_RADIO_GFSK_PKT_VAR_LEN +#endif +#ifndef FSK_CRC_TYPE +#define FSK_CRC_TYPE LR11XX_RADIO_GFSK_CRC_1_BYTE_INV +#endif +#ifndef FSK_DC_FREE +#define FSK_DC_FREE LR11XX_RADIO_GFSK_DC_FREE_OFF +#endif + +/*! + * @brief GFSK sync word + */ +static const uint8_t gfsk_sync_word[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; + +/*! + * @brief GFSK whitening seed + */ +#ifndef FSK_WHITENING_SEED +#define FSK_WHITENING_SEED 0x0123 +#endif + +/*! + * @brief GFSK CRC seed + */ +#ifndef FSK_CRC_SEED +#define FSK_CRC_SEED 0x01234567 +#endif + +/*! + * @brief GFSK CRC polynomial + */ +#ifndef FSK_CRC_POLYNOMIAL +#define FSK_CRC_POLYNOMIAL 0x01234567 +#endif + +/*! + * @brief GFSK address filtering - node address + */ +#ifndef FSK_NODE_ADDRESS +#define FSK_NODE_ADDRESS 0x05 +#endif + +/*! + * @brief GFSK address filtering - broadcast address + */ +#ifndef FSK_BROADCAST_ADDRESS +#define FSK_BROADCAST_ADDRESS 0xAB +#endif + +/*! + * @brief Sigfox radio configuration + */ +#ifndef SIGFOX_RC +#define SIGFOX_RC 1 +#endif + +#if( SIGFOX_RC == 1 ) +#define SIGFOX_UPLINK_RF_FREQ_IN_HZ 868130000 +#define BPSK_BITRATE_IN_BPS 100 +#define SIGFOX_TX_OUTPUT_POWER_DBM 14 +#define RAMP_UP_DELAY SIGFOX_DBPSK_RAMP_UP_TIME_100_BPS +#define RAMP_DOWN_DELAY SIGFOX_DBPSK_RAMP_DOWN_TIME_100_BPS +#elif( SIGFOX_RC == 2 ) +#define SIGFOX_UPLINK_RF_FREQ_IN_HZ 902200000 +#define BPSK_BITRATE_IN_BPS 600 +#define SIGFOX_TX_OUTPUT_POWER_DBM 22 +#define RAMP_UP_DELAY SIGFOX_DBPSK_RAMP_UP_TIME_600_BPS +#define RAMP_DOWN_DELAY SIGFOX_DBPSK_RAMP_DOWN_TIME_600_BPS +#elif( SIGFOX_RC == 3 ) +#define SIGFOX_UPLINK_RF_FREQ_IN_HZ 923200000 +#define BPSK_BITRATE_IN_BPS 100 +#define SIGFOX_TX_OUTPUT_POWER_DBM 14 +#define RAMP_UP_DELAY SIGFOX_DBPSK_RAMP_DOWN_TIME_100_BPS +#define RAMP_DOWN_DELAY SIGFOX_DBPSK_RAMP_DOWN_TIME_100_BPS +#elif( SIGFOX_RC == 4 ) +#define SIGFOX_UPLINK_RF_FREQ_IN_HZ 920800000 +#define BPSK_BITRATE_IN_BPS 600 +#define SIGFOX_TX_OUTPUT_POWER_DBM 22 +#define RAMP_UP_DELAY SIGFOX_DBPSK_RAMP_UP_TIME_600_BPS +#define RAMP_DOWN_DELAY SIGFOX_DBPSK_RAMP_DOWN_TIME_600_BPS +#elif( SIGFOX_RC == 5 ) +#define SIGFOX_UPLINK_RF_FREQ_IN_HZ 923300000 +#define BPSK_BITRATE_IN_BPS 100 +#define SIGFOX_TX_OUTPUT_POWER_DBM 12 +#define RAMP_UP_DELAY SIGFOX_DBPSK_RAMP_DOWN_TIME_100_BPS +#define RAMP_DOWN_DELAY SIGFOX_DBPSK_RAMP_DOWN_TIME_100_BPS +#elif( SIGFOX_RC == 6 ) +#define SIGFOX_UPLINK_RF_FREQ_IN_HZ 865200000 +#define BPSK_BITRATE_IN_BPS 100 +#define SIGFOX_TX_OUTPUT_POWER_DBM 14 +#define RAMP_UP_DELAY SIGFOX_DBPSK_RAMP_DOWN_TIME_100_BPS +#define RAMP_DOWN_DELAY SIGFOX_DBPSK_RAMP_DOWN_TIME_100_BPS +#elif( SIGFOX_RC == 7 ) +#define SIGFOX_UPLINK_RF_FREQ_IN_HZ 868800000 +#define BPSK_BITRATE_IN_BPS 100 +#define SIGFOX_TX_OUTPUT_POWER_DBM 14 +#define RAMP_UP_DELAY SIGFOX_DBPSK_RAMP_DOWN_TIME_100_BPS +#define RAMP_DOWN_DELAY SIGFOX_DBPSK_RAMP_DOWN_TIME_100_BPS +#else +#error "Select a valid Radio Configuration" +#endif + +extern lr1121_t lr1121; + +void lora_system_init( const void* context ); +void lora_radio_init( const void* context ); +void lora_radio_dbpsk_init( const void* context, const uint8_t payload_len ); + +uint32_t get_time_on_air_in_ms( void ); +#endif \ No newline at end of file diff --git a/components/esp_lora_1121/test_app/main/test_lr1121.c b/components/esp_lora_1121/test_app/main/test_lr1121.c new file mode 100755 index 0000000..deb1bca --- /dev/null +++ b/components/esp_lora_1121/test_app/main/test_lr1121.c @@ -0,0 +1,330 @@ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/i2c_master.h" +#include "esp_log.h" +#include "esp_lora_1121.h" + +#include "lr1121_config.h" + +static const char *TAG = "lr1121_example"; + +#define TEST_SPI_MOSI (GPIO_NUM_45) +#define TEST_SPI_MISO (GPIO_NUM_46) +#define TEST_SPI_CLK (GPIO_NUM_40) +#define TEST_SPI_CS (GPIO_NUM_42) +#define TEST_SPI_INT (GPIO_NUM_38) +#define TEST_SPI_RESET (GPIO_NUM_39) +#define TEST_SPI_BUSY (GPIO_NUM_41) + +#define TEST_SPI_NUM (SPI3_HOST) +#define TEST_SPI_CLK_SPEED_HZ 8 * 1000 * 1000 + +#define RX_TIMEOUT_VALUE 600 +#define PING_PONG_PREFIX_SIZE 5 +#define SYNC_PACKET_THRESHOLD 64 +#define ITERATION_INDEX ( PING_PONG_PREFIX_SIZE + 1 ) +#define DELAY_BEFORE_TX_MS 20 +#define DELAY_PING_PONG_PACE_MS 200 +#define IRQ_MASK \ + ( LR11XX_SYSTEM_IRQ_TX_DONE | LR11XX_SYSTEM_IRQ_RX_DONE | LR11XX_SYSTEM_IRQ_TIMEOUT | \ + LR11XX_SYSTEM_IRQ_HEADER_ERROR | LR11XX_SYSTEM_IRQ_CRC_ERROR | LR11XX_SYSTEM_IRQ_FSK_LEN_ERROR ) + +#define HAL_DBG_TRACE_ARRAY( msg, array, len ) \ + do \ + { \ + printf( "%s - (%lu bytes):\n", msg, ( uint32_t ) len ); \ + for( uint32_t i = 0; i < ( uint32_t ) len; i++ ) \ + { \ + if( ( ( i % 16 ) == 0 ) && ( i > 0 ) ) \ + { \ + printf( "\n" ); \ + } \ + printf( " %02X", array[i] ); \ + } \ + printf( "\n" ); \ + } while( 0 ); + +static spi_device_handle_t spi_handle = NULL;// SPI Handle + +static uint8_t buffer_tx[PAYLOAD_LENGTH]; +static bool is_master = true; + +static const uint8_t ping_msg[PING_PONG_PREFIX_SIZE] = "PING"; +static const uint8_t pong_msg[PING_PONG_PREFIX_SIZE] = "PONG"; + +static uint8_t iteration = 0; +static uint16_t packets_to_sync = 0; + +bool irq_flag; +/** + * @brief Handle reception failure for ping-pong example + */ +static void ping_pong_reception_failure_handling( void ); + +void lora_irq_process( const void* context, lr11xx_system_irq_mask_t irq_filter_mask ); + +static void IRAM_ATTR isr(void* arg) { + irq_flag = true; // Set the interrupt flag +} + +static esp_err_t spi_bus_init() { + spi_bus_config_t buscfg = { + .mosi_io_num = TEST_SPI_MOSI, + .miso_io_num = TEST_SPI_MISO, + .sclk_io_num = TEST_SPI_CLK, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = 64, + }; + + spi_device_interface_config_t devcfg = { + .clock_speed_hz = TEST_SPI_CLK_SPEED_HZ, // 8 MHz + .mode = 0, // SPI mode 0: CPOL=0, CPHA=0 + .spics_io_num = -1, + .queue_size = 1, + }; + // Initialize SPI bus + ESP_ERROR_CHECK(spi_bus_initialize(TEST_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO)); + ESP_ERROR_CHECK(spi_bus_add_device(TEST_SPI_NUM, &devcfg, &spi_handle)); + + return ESP_OK; +} + +static void lr1121_test_task(void *arg) { + + lora_init_io_context(&lr1121,TEST_SPI_CS,TEST_SPI_RESET,TEST_SPI_BUSY,TEST_SPI_INT); // Initialize the I/O context for the LR1121 + lora_init_io(&lr1121); // Initialize the I/O for the LR1121 + + lora_spi_init(&lr1121, spi_handle); // Initialize the SPI interface for the LR1121 + + printf( "===== LR11xx Ping-Pong example =====\n\n" ); + printf( "LR11XX driver version: %s\n", lr11xx_driver_version_get_version_string( ) ); + + // Initialize the system + lora_system_init(&lr1121); + // Print the version number for verification + lora_print_version(&lr1121); + // Initialize the LoRa radio + lora_radio_init(&lr1121); + + lora_init_irq(&lr1121, isr); // Initialize the interrupt service routine + + + ASSERT_LR11XX_RC( lr11xx_system_set_dio_irq_params( &lr1121, IRQ_MASK, 0 ) ); + ASSERT_LR11XX_RC( lr11xx_system_clear_irq_status( &lr1121, LR11XX_SYSTEM_IRQ_ALL_MASK ) ); + + /* Intializes random number generator */ + srand( 10 ); + + memcpy( buffer_tx, ping_msg, PING_PONG_PREFIX_SIZE ); + buffer_tx[PING_PONG_PREFIX_SIZE] = ( uint8_t ) 0; + buffer_tx[ITERATION_INDEX] = ( uint8_t ) ( iteration ); + for( int i = PING_PONG_PREFIX_SIZE + 1 + 1; i < PAYLOAD_LENGTH; i++ ) + { + buffer_tx[i] = i; + } + + ASSERT_LR11XX_RC( lr11xx_regmem_write_buffer8( &lr1121, buffer_tx, PAYLOAD_LENGTH ) ); + + + ASSERT_LR11XX_RC( lr11xx_radio_set_tx( &lr1121, 0 ) ); + while (1) + { + if(irq_flag) + lora_irq_process( &lr1121, IRQ_MASK ); + vTaskDelay(1 / portTICK_PERIOD_MS); // Short delay to control the loop speed + } +} + +void on_tx_done( void ) +{ + + printf( "Sent message %s, iteration %d\n", buffer_tx, iteration ); + vTaskDelay( DELAY_PING_PONG_PACE_MS / portTICK_PERIOD_MS); + ASSERT_LR11XX_RC( lr11xx_radio_set_rx( + &lr1121, + get_time_on_air_in_ms( ) + RX_TIMEOUT_VALUE + rand( ) % 500 ) ); // Random delay to avoid + // unwanted synchronization +} + +void lora_receive( const void* context, uint8_t* buffer, uint8_t buffer_length, uint8_t* size ) +{ + lr11xx_radio_rx_buffer_status_t rx_buffer_status; + lr11xx_radio_pkt_status_lora_t pkt_status_lora; + lr11xx_radio_pkt_status_gfsk_t pkt_status_gfsk; + + lr11xx_radio_get_rx_buffer_status( context, &rx_buffer_status ); + *size = rx_buffer_status.pld_len_in_bytes; + if( *size > buffer_length ) + { + printf( "Received payload (size: %d) is bigger than the buffer (size: %d)!\n", *size, + buffer_length ); + return; + } + lr11xx_regmem_read_buffer8( context, buffer, rx_buffer_status.buffer_start_pointer, + rx_buffer_status.pld_len_in_bytes ); + + HAL_DBG_TRACE_ARRAY( "Packet content", buffer, *size ); + + printf( "Packet status:\n" ); + if( PACKET_TYPE == LR11XX_RADIO_PKT_TYPE_LORA ) + { + lr11xx_radio_get_lora_pkt_status( context, &pkt_status_lora ); + printf( " - RSSI packet = %i dBm\n", pkt_status_lora.rssi_pkt_in_dbm ); + printf( " - Signal RSSI packet = %i dBm\n", pkt_status_lora.signal_rssi_pkt_in_dbm ); + printf( " - SNR packet = %i dB\n", pkt_status_lora.snr_pkt_in_db ); + } + else if( PACKET_TYPE == LR11XX_RADIO_PKT_TYPE_GFSK ) + { + lr11xx_radio_get_gfsk_pkt_status( context, &pkt_status_gfsk ); + printf( " - RSSI average = %i dBm\n", pkt_status_gfsk.rssi_avg_in_dbm ); + printf( " - RSSI sync = %i dBm\n", pkt_status_gfsk.rssi_sync_in_dbm ); + } +} + +void on_rx_done( void ) +{ + uint8_t buffer_rx[PAYLOAD_LENGTH]; + uint8_t size; + + packets_to_sync = 0; + + + lora_receive( &lr1121, buffer_rx, PAYLOAD_LENGTH, &size ); + iteration = buffer_rx[ITERATION_INDEX]; + + iteration++; + printf( "Received message %s, iteration %d\n", buffer_rx, iteration ); + + if( is_master == true ) + { + if( memcmp( buffer_rx, ping_msg, PING_PONG_PREFIX_SIZE ) == 0 ) + { + is_master = false; + memcpy( buffer_tx, pong_msg, PING_PONG_PREFIX_SIZE ); + } + else if( memcmp( buffer_rx, pong_msg, PING_PONG_PREFIX_SIZE ) != 0 ) + { + printf( "Unexpected message\n" ); + } + } + else + { + if( memcmp( buffer_rx, ping_msg, PING_PONG_PREFIX_SIZE ) != 0 ) + { + printf( "Unexpected message\n" ); + + is_master = true; + memcpy( buffer_tx, ping_msg, PING_PONG_PREFIX_SIZE ); + } + } + + vTaskDelay( (DELAY_PING_PONG_PACE_MS + DELAY_BEFORE_TX_MS) / portTICK_PERIOD_MS); + buffer_tx[ITERATION_INDEX] = iteration; + + ASSERT_LR11XX_RC( lr11xx_regmem_write_buffer8( &lr1121, buffer_tx, PAYLOAD_LENGTH ) ); + + ASSERT_LR11XX_RC( lr11xx_radio_set_tx( &lr1121, 0 ) ); +} + +void on_rx_timeout( void ) +{ + packets_to_sync++; + if( packets_to_sync > SYNC_PACKET_THRESHOLD ) + { + printf( + "It looks like synchronisation is still not done, consider resetting one of the board\n" ); + } + ping_pong_reception_failure_handling( ); +} + +void on_rx_crc_error( void ) +{ + ping_pong_reception_failure_handling( ); +} + +void on_fsk_len_error( void ) +{ + ping_pong_reception_failure_handling( ); +} + +static void ping_pong_reception_failure_handling( void ) +{ + is_master = true; + iteration = 0; + memcpy( buffer_tx, ping_msg, PING_PONG_PREFIX_SIZE ); + buffer_tx[ITERATION_INDEX] = iteration; + + ASSERT_LR11XX_RC( lr11xx_regmem_write_buffer8( &lr1121, buffer_tx, PAYLOAD_LENGTH ) ); + + ASSERT_LR11XX_RC( lr11xx_radio_set_tx( &lr1121, 0 ) ); +} + +void lora_irq_process( const void* context, lr11xx_system_irq_mask_t irq_filter_mask ) +{ + + irq_flag = false; + + lr11xx_system_irq_mask_t irq_regs; + lr11xx_system_get_and_clear_irq_status( context, &irq_regs ); + + printf( "Interrupt flags = 0x%08lX\n", irq_regs ); + + irq_regs &= irq_filter_mask; + + printf( "Interrupt flags (after filtering) = 0x%08lX\n", irq_regs ); + + if( ( irq_regs & LR11XX_SYSTEM_IRQ_TX_DONE ) == LR11XX_SYSTEM_IRQ_TX_DONE ) + { + printf( "Tx done\n" ); + on_tx_done( ); + } + + + if( ( irq_regs & LR11XX_SYSTEM_IRQ_HEADER_ERROR ) == LR11XX_SYSTEM_IRQ_HEADER_ERROR ) + { + printf( "Header error\n" ); + } + + if( ( irq_regs & LR11XX_SYSTEM_IRQ_RX_DONE ) == LR11XX_SYSTEM_IRQ_RX_DONE ) + { + if( ( irq_regs & LR11XX_SYSTEM_IRQ_CRC_ERROR ) == LR11XX_SYSTEM_IRQ_CRC_ERROR ) + { + printf( "CRC error\n" ); + on_rx_crc_error( ); + } + else if( ( irq_regs & LR11XX_SYSTEM_IRQ_FSK_LEN_ERROR ) == LR11XX_SYSTEM_IRQ_FSK_LEN_ERROR ) + { + printf( "FSK length error\n" ); + on_fsk_len_error( ); + } + else + { + printf( "Rx done\n" ); + on_rx_done( ); + } + } + + if( ( irq_regs & LR11XX_SYSTEM_IRQ_TIMEOUT ) == LR11XX_SYSTEM_IRQ_TIMEOUT ) + { + printf( "Rx timeout\n" ); + on_rx_timeout( ); + } + + printf( "\n" ); + +} + +void app_main(void) +{ + ESP_LOGI(TAG, "Initializing SPI..."); + + esp_err_t ret = spi_bus_init(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize SPI (error: %d)", ret); + return; + } + + xTaskCreate(lr1121_test_task, "lr1121_test_task", 4096, NULL, 5, NULL); +} \ No newline at end of file diff --git a/components/input/CMakeLists.txt b/components/input/CMakeLists.txt new file mode 100644 index 0000000..05664a6 --- /dev/null +++ b/components/input/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "input.c" + INCLUDE_DIRS "include" + REQUIRES esp_adc +) diff --git a/components/input/Kconfig b/components/input/Kconfig new file mode 100644 index 0000000..eb518c8 --- /dev/null +++ b/components/input/Kconfig @@ -0,0 +1,32 @@ +menu "Joystick input" + +config JOYSTICK_ADC_CHANNEL + int "ADC1 channel for joystick ladder" + range 0 9 + default 6 + +config JOYSTICK_ADC_LEVEL_LEFT + int "Expected ADC raw value for LEFT" + default 600 + +config JOYSTICK_ADC_LEVEL_UP + int "Expected ADC raw value for UP" + default 1200 + +config JOYSTICK_ADC_LEVEL_PRESS + int "Expected ADC raw value for PRESS" + default 1900 + +config JOYSTICK_ADC_LEVEL_DOWN + int "Expected ADC raw value for DOWN" + default 2600 + +config JOYSTICK_ADC_LEVEL_RIGHT + int "Expected ADC raw value for RIGHT" + default 3300 + +config JOYSTICK_ADC_TOLERANCE + int "Tolerance (+/- counts) when matching ADC reading to a direction" + default 150 + +endmenu diff --git a/components/input/include/input.h b/components/input/include/input.h new file mode 100644 index 0000000..1e43927 --- /dev/null +++ b/components/input/include/input.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esp_err.h" +#include "esp_adc/adc_oneshot.h" + +typedef enum { + INPUT_NONE = 0, + INPUT_UP, + INPUT_DOWN, + INPUT_LEFT, + INPUT_RIGHT, + INPUT_CENTER, +} input_event_t; + +void input_init(void); +input_event_t input_poll(void); +esp_err_t input_read_raw(int *out_raw); +adc_channel_t input_get_channel(void); diff --git a/components/input/input.c b/components/input/input.c new file mode 100644 index 0000000..918177a --- /dev/null +++ b/components/input/input.c @@ -0,0 +1,94 @@ +#include "input.h" +#include +#include "esp_log.h" +#include "sdkconfig.h" +#include "esp_adc/adc_oneshot.h" + +static const char *TAG = "input"; + +static adc_oneshot_unit_handle_t s_adc_handle; +static adc_channel_t s_channel; + +static void log_levels(void) +{ + ESP_LOGI(TAG, "Joystick ladder on ADC1_CH%d, levels L=%d U=%d P=%d D=%d R=%d tol=%d", + CONFIG_JOYSTICK_ADC_CHANNEL, + CONFIG_JOYSTICK_ADC_LEVEL_LEFT, + CONFIG_JOYSTICK_ADC_LEVEL_UP, + CONFIG_JOYSTICK_ADC_LEVEL_PRESS, + CONFIG_JOYSTICK_ADC_LEVEL_DOWN, + CONFIG_JOYSTICK_ADC_LEVEL_RIGHT, + CONFIG_JOYSTICK_ADC_TOLERANCE); +} + +void input_init(void) +{ + adc_oneshot_unit_init_cfg_t unit_cfg = { + .unit_id = ADC_UNIT_1, + .ulp_mode = ADC_ULP_MODE_DISABLE, + }; + ESP_ERROR_CHECK(adc_oneshot_new_unit(&unit_cfg, &s_adc_handle)); + + s_channel = (adc_channel_t)CONFIG_JOYSTICK_ADC_CHANNEL; + adc_oneshot_chan_cfg_t chan_cfg = { + .bitwidth = ADC_BITWIDTH_DEFAULT, + .atten = ADC_ATTEN_DB_11, + }; + ESP_ERROR_CHECK(adc_oneshot_config_channel(s_adc_handle, s_channel, &chan_cfg)); + log_levels(); +} + +adc_channel_t input_get_channel(void) +{ + return s_channel; +} + +esp_err_t input_read_raw(int *out_raw) +{ + int raw = 0; + esp_err_t err = adc_oneshot_read(s_adc_handle, s_channel, &raw); + if (err != ESP_OK) { + return err; + } + if (out_raw) { + *out_raw = raw; + } + return ESP_OK; +} + +input_event_t input_poll(void) +{ + int raw = 0; + if (input_read_raw(&raw) != ESP_OK) { + return INPUT_NONE; + } + struct level_map { + input_event_t evt; + int expected; + } map[] = { + {INPUT_LEFT, CONFIG_JOYSTICK_ADC_LEVEL_LEFT}, + // Вертикаль інвертована (UP читається ближче до рівня DOWN) на цій платі + {INPUT_UP, CONFIG_JOYSTICK_ADC_LEVEL_DOWN}, + {INPUT_CENTER, CONFIG_JOYSTICK_ADC_LEVEL_PRESS}, + {INPUT_DOWN, CONFIG_JOYSTICK_ADC_LEVEL_UP}, + {INPUT_RIGHT, CONFIG_JOYSTICK_ADC_LEVEL_RIGHT}, + }; + + int best_diff = INT_MAX; + input_event_t best_evt = INPUT_NONE; + for (size_t i = 0; i < sizeof(map) / sizeof(map[0]); i++) { + int diff = map[i].expected - raw; + if (diff < 0) { + diff = -diff; + } + if (diff < best_diff) { + best_diff = diff; + best_evt = map[i].evt; + } + } + + if (best_diff <= CONFIG_JOYSTICK_ADC_TOLERANCE) { + return best_evt; + } + return INPUT_NONE; +} diff --git a/components/lora_radio/CMakeLists.txt b/components/lora_radio/CMakeLists.txt new file mode 100644 index 0000000..661623b --- /dev/null +++ b/components/lora_radio/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register( + SRCS "lora_radio.c" + INCLUDE_DIRS "include" + REQUIRES common esp_lora_1121 + PRIV_REQUIRES esp_driver_spi esp_driver_gpio esp_timer driver +) diff --git a/components/lora_radio/Kconfig b/components/lora_radio/Kconfig new file mode 100644 index 0000000..ec33f45 --- /dev/null +++ b/components/lora_radio/Kconfig @@ -0,0 +1,56 @@ +menu "LoRa radio (LR1121)" + +config LORA_SPI_HOST + int "SPI host" + range 0 3 + default 2 + help + Номер SPI host для RA01 (звичайно HSPI = 2, VSPI = 3). + +config LORA_PIN_CS + int "LR1121 CS (NSS) pin" + default 18 + +config LORA_PIN_MOSI + int "LR1121 MOSI pin" + default 27 + +config LORA_PIN_MISO + int "LR1121 MISO pin" + default 19 + +config LORA_PIN_SCK + int "LR1121 SCK pin" + default 5 + +config LORA_PIN_RST + int "LR1121 RESET pin" + default 14 + +config LORA_PIN_BUSY + int "LR1121 BUSY pin" + default 26 + +config LORA_PIN_DIO1 + int "LR1121 DIO1 pin" + default 25 + +config LORA_FREQ_MHZ + int "Частота МГц" + default 868 + +config LORA_BW_KHZ + int "Смуга пропускання кГц" + default 125 + +config LORA_SF + int "Spreading factor" + range 5 12 + default 7 + +config LORA_CR + int "Coding rate (4/x)" + range 5 8 + default 5 + +endmenu diff --git a/components/lora_radio/include/lora_radio.h b/components/lora_radio/include/lora_radio.h new file mode 100644 index 0000000..432dc7b --- /dev/null +++ b/components/lora_radio/include/lora_radio.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include "esp_err.h" + +#ifndef CONFIG_LORA_CR +#define CONFIG_LORA_CR 5 +#endif + +typedef struct { + int freq_centi_mhz; // частота у сотих MHz (433.35 MHz => 43335) + int bw_khz; // смуга пропускання, кГц + int sf; // spreading factor (5..12) + int cr; // coding rate denominator (4/x) + int tx_power_dbm; // вихідна потужність, дБм + uint16_t preamble_syms; // довжина прекомбози, символи + uint8_t payload_len; // очікувана довжина корисного навантаження (0 = довільна для explicit header) + bool crc_on; // включити CRC + bool iq_invert; // інверсія IQ (звичайно false) + bool header_implicit; // implicit header режим +} lora_params_t; + +typedef struct { + int rssi_dbm; + int snr_db; + uint8_t last_status; +} lora_metrics_t; + +bool lora_radio_init(bool is_tx_role); +esp_err_t lora_radio_apply_params(const lora_params_t *params); +esp_err_t lora_radio_set_tx_power_dbm(int dbm); +esp_err_t lora_radio_set_frequency_centi_mhz(int freq_centi_mhz); +void lora_radio_get_params(lora_params_t *out); +void lora_radio_tick_tx(void); +void lora_radio_tick_rx(void); +void lora_radio_get_metrics(lora_metrics_t *out); +void lora_radio_get_last_payload(char *out, size_t out_len); +esp_err_t lora_radio_send(const uint8_t *data, size_t len); diff --git a/components/lora_radio/lora_radio.c b/components/lora_radio/lora_radio.c new file mode 100644 index 0000000..e2f17cb --- /dev/null +++ b/components/lora_radio/lora_radio.c @@ -0,0 +1,476 @@ +#include "lora_radio.h" +#include +#include "esp_check.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "esp_lora_1121.h" +#include "lr1121_common/lr1121_common.h" +#include "lr11xx_driver/lr11xx_hal.h" +#include "lr11xx_driver/lr11xx_radio.h" +#include "lr11xx_driver/lr11xx_regmem.h" +#include "lr11xx_driver/lr11xx_system.h" + +static const char *TAG = "lora_radio"; + +#define LR1121_SPI_FREQ_HZ (8 * 1000 * 1000) +#define LR1121_SPI_MODE (0) +#define LR1121_RX_CONTINUOUS (0xFFFFFF) +#define LR1121_SYNC_WORD (0x12) // Private network +#define LR1121_PA_RAMP (LR11XX_RADIO_RAMP_48_US) + +static lr1121_t s_lr = {0}; +static spi_device_handle_t s_spi = NULL; +static bool s_is_tx = false; +static lora_params_t s_params; +static lora_metrics_t s_metrics = { .rssi_dbm = -120, .snr_db = -20, .last_status = 0 }; +static char s_last_payload[96] = "(no data)"; +static size_t s_last_payload_len = 0; +static int64_t s_last_rx_restart_us = 0; +static bool s_tx_in_progress = false; +static int64_t s_last_tx_us = 0; + +static inline esp_err_t lr_status_to_esp(lr11xx_status_t st) +{ + return (st == LR11XX_STATUS_OK) ? ESP_OK : ESP_FAIL; +} + +static uint32_t cmhz_to_hz(int cmhz) +{ + if (cmhz <= 0) { + return 0; + } + return (uint32_t)cmhz * 10000U; +} + +static lr11xx_radio_lora_sf_t map_sf(int sf) +{ + switch (sf) { + case 5: return LR11XX_RADIO_LORA_SF5; + case 6: return LR11XX_RADIO_LORA_SF6; + case 7: return LR11XX_RADIO_LORA_SF7; + case 8: return LR11XX_RADIO_LORA_SF8; + case 9: return LR11XX_RADIO_LORA_SF9; + case 10: return LR11XX_RADIO_LORA_SF10; + case 11: return LR11XX_RADIO_LORA_SF11; + case 12: return LR11XX_RADIO_LORA_SF12; + default: return LR11XX_RADIO_LORA_SF7; + } +} + +static lr11xx_radio_lora_bw_t map_bw(int bw_khz) +{ + switch (bw_khz) { + case 125: return LR11XX_RADIO_LORA_BW_125; + case 250: return LR11XX_RADIO_LORA_BW_250; + case 500: return LR11XX_RADIO_LORA_BW_500; + default: return LR11XX_RADIO_LORA_BW_125; + } +} + +static lr11xx_radio_lora_cr_t map_cr(int cr) +{ + switch (cr) { + case 5: return LR11XX_RADIO_LORA_CR_4_5; + case 6: return LR11XX_RADIO_LORA_CR_4_6; + case 7: return LR11XX_RADIO_LORA_CR_4_7; + case 8: return LR11XX_RADIO_LORA_CR_4_8; + default: return LR11XX_RADIO_LORA_CR_4_5; + } +} + +static void sanitize_params(lora_params_t *p) +{ + if (!p) { + return; + } + if (p->freq_centi_mhz < 15000) p->freq_centi_mhz = 15000; // 150 MHz мінімум + if (p->freq_centi_mhz > 250000) p->freq_centi_mhz = 250000; // 2.5 GHz максимум + if (p->bw_khz != 125 && p->bw_khz != 250 && p->bw_khz != 500) { + p->bw_khz = 125; + } + if (p->sf < 5) p->sf = 5; + if (p->sf > 12) p->sf = 12; + if (p->cr < 5) p->cr = 5; + if (p->cr > 8) p->cr = 8; + int max_dbm = 22; + // HF port (L/S/2.4 GHz) має нижчу допустиму потужність + if (p->freq_centi_mhz >= 150000) { + max_dbm = 13; + } + if (p->tx_power_dbm < -17) p->tx_power_dbm = -17; + if (p->tx_power_dbm > max_dbm) p->tx_power_dbm = max_dbm; + if (p->preamble_syms == 0) p->preamble_syms = 8; + if (p->payload_len == 0 && p->header_implicit) { + p->payload_len = 1; + } +} + +static void lr1121_calibrate_image_for_freq(uint32_t freq_hz) +{ + // Використовуємо універсальне калібрування за частотою в MHz ±10 MHz + uint16_t mhz = (uint16_t)(freq_hz / 1000000U); + uint16_t f1 = (mhz > 10) ? (uint16_t)(mhz - 10) : mhz; + uint16_t f2 = (uint16_t)(mhz + 10); + lr11xx_system_calibrate_image_in_mhz(&s_lr, f1, f2); +} + +static esp_err_t radio_setup_spi(void) +{ + spi_bus_config_t buscfg = { + .mosi_io_num = CONFIG_LORA_PIN_MOSI, + .miso_io_num = CONFIG_LORA_PIN_MISO, + .sclk_io_num = CONFIG_LORA_PIN_SCK, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + }; + esp_err_t err = spi_bus_initialize(CONFIG_LORA_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO); + if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { + return err; + } + + if (s_spi) { + return ESP_OK; + } + + spi_device_interface_config_t devcfg = { + .clock_speed_hz = LR1121_SPI_FREQ_HZ, + .mode = LR1121_SPI_MODE, + .spics_io_num = -1, // CS керуємо вручну в HAL + .queue_size = 2, + }; + err = spi_bus_add_device(CONFIG_LORA_SPI_HOST, &devcfg, &s_spi); + if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { + return err; + } + return ESP_OK; +} + +static esp_err_t lr1121_hw_init(void) +{ + lora_init_io_context(&s_lr, + CONFIG_LORA_PIN_CS, + CONFIG_LORA_PIN_RST, + CONFIG_LORA_PIN_BUSY, + CONFIG_LORA_PIN_DIO1); + lora_init_io(&s_lr); + lora_spi_init(&s_lr, s_spi); + return ESP_OK; +} + +static esp_err_t lr1121_system_boot(void) +{ + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_reset(&s_lr)), TAG, "reset"); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_hal_wakeup(&s_lr)), TAG, "wakeup"); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_set_standby(&s_lr, LR11XX_SYSTEM_STANDBY_CFG_XOSC)), TAG, "standby"); + const lr11xx_system_reg_mode_t reg_mode = smtc_shield_lr11xx_common_get_reg_mode(); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_set_reg_mode(&s_lr, reg_mode)), TAG, "reg mode"); + + const lr11xx_system_rfswitch_cfg_t *rf_switch = smtc_shield_lr11xx_common_get_rf_switch_cfg(); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_set_dio_as_rf_switch(&s_lr, rf_switch)), TAG, "rf switch"); + + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_set_tcxo_mode(&s_lr, LR11XX_SYSTEM_TCXO_CTRL_3_0V, 300)), TAG, "tcxo"); + + const smtc_shield_lr11xx_lfclk_cfg_t *lf = smtc_shield_lr11xx_common_get_lfclk_cfg(); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_cfg_lfclk(&s_lr, lf->lf_clk_cfg, lf->wait_32k_ready)), TAG, "lfclk"); + + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_clear_errors(&s_lr)), TAG, "clear errors"); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_calibrate(&s_lr, 0x3F)), TAG, "calibrate"); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_clear_irq_status(&s_lr, LR11XX_SYSTEM_IRQ_ALL_MASK)), TAG, "clear irq"); + + lr11xx_system_version_t ver = {0}; + if (lr11xx_system_get_version(&s_lr, &ver) == LR11XX_STATUS_OK) { + ESP_LOGI(TAG, "LR1121 ver hw=0x%02X type=0x%02X fw=0x%04X", + ver.hw, ver.type, ver.fw); + } + return ESP_OK; +} + +static esp_err_t lr1121_apply_lora(const lora_params_t *p) +{ + uint32_t freq_hz = cmhz_to_hz(p->freq_centi_mhz); + lr11xx_radio_lora_bw_t bw = map_bw(p->bw_khz); + lr11xx_radio_lora_sf_t sf = map_sf(p->sf); + lr11xx_radio_lora_cr_t cr = map_cr(p->cr); + + lr11xx_radio_mod_params_lora_t mod = { + .sf = sf, + .bw = bw, + .cr = cr, + .ldro = smtc_shield_lr11xx_common_compute_lora_ldro(sf, bw), + }; + lr11xx_radio_pkt_params_lora_t pkt = { + .preamble_len_in_symb = p->preamble_syms, + .header_type = p->header_implicit ? LR11XX_RADIO_LORA_PKT_IMPLICIT : LR11XX_RADIO_LORA_PKT_EXPLICIT, + .pld_len_in_bytes = p->payload_len, + .crc = p->crc_on ? LR11XX_RADIO_LORA_CRC_ON : LR11XX_RADIO_LORA_CRC_OFF, + .iq = p->iq_invert ? LR11XX_RADIO_LORA_IQ_INVERTED : LR11XX_RADIO_LORA_IQ_STANDARD, + }; + + const lr11xx_radio_rssi_calibration_table_t *rssi_cal = smtc_shield_lr11xx_get_rssi_calibration_table(freq_hz); + const smtc_shield_lr11xx_pa_pwr_cfg_t *pa_cfg = smtc_shield_lr1121mb1gis_get_pa_pwr_cfg(freq_hz, p->tx_power_dbm); + if (pa_cfg == NULL || rssi_cal == NULL) { + return ESP_ERR_INVALID_ARG; + } + + lr1121_calibrate_image_for_freq(freq_hz); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_pkt_type(&s_lr, LR11XX_RADIO_PKT_TYPE_LORA)), TAG, "pkt type"); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_rf_freq(&s_lr, freq_hz)), TAG, "freq"); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_rssi_calibration(&s_lr, rssi_cal)), TAG, "rssi cal"); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_pa_cfg(&s_lr, &pa_cfg->pa_config)), TAG, "pa cfg"); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_tx_params(&s_lr, pa_cfg->power, LR1121_PA_RAMP)), TAG, "tx params"); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_rx_tx_fallback_mode(&s_lr, LR11XX_RADIO_FALLBACK_STDBY_RC)), TAG, "fallback"); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_cfg_rx_boosted(&s_lr, true)), TAG, "rx boost"); + + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_lora_mod_params(&s_lr, &mod)), TAG, "mod params"); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_lora_pkt_params(&s_lr, &pkt)), TAG, "pkt params"); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_lora_sync_word(&s_lr, LR1121_SYNC_WORD)), TAG, "sync word"); + + lr11xx_system_irq_mask_t irq_mask = LR11XX_SYSTEM_IRQ_RX_DONE | LR11XX_SYSTEM_IRQ_TIMEOUT | LR11XX_SYSTEM_IRQ_HEADER_ERROR | LR11XX_SYSTEM_IRQ_CRC_ERROR; + if (s_is_tx) { + irq_mask |= LR11XX_SYSTEM_IRQ_TX_DONE; + } + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_set_dio_irq_params(&s_lr, irq_mask, 0)), TAG, "irq mask"); + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_system_clear_irq_status(&s_lr, LR11XX_SYSTEM_IRQ_ALL_MASK)), TAG, "clr irq"); + + return ESP_OK; +} + +static esp_err_t lr1121_enter_rx_continuous(void) +{ + s_last_rx_restart_us = esp_timer_get_time(); + return lr_status_to_esp(lr11xx_radio_set_rx_with_timeout_in_rtc_step(&s_lr, LR1121_RX_CONTINUOUS)); +} + +bool lora_radio_init(bool is_tx_role) +{ + s_is_tx = is_tx_role; + s_params.freq_centi_mhz = CONFIG_LORA_FREQ_MHZ * 100; + s_params.bw_khz = CONFIG_LORA_BW_KHZ; + s_params.sf = CONFIG_LORA_SF; + s_params.cr = CONFIG_LORA_CR; + s_params.tx_power_dbm = 14; + s_params.preamble_syms = 8; + s_params.payload_len = 0; + s_params.crc_on = true; + s_params.iq_invert = false; + s_params.header_implicit = false; + sanitize_params(&s_params); + + ESP_LOGI(TAG, "Init LR1121 over SPI host %d (MOSI=%d, MISO=%d, SCK=%d, CS=%d, RST=%d, BUSY=%d, DIO1=%d)", + CONFIG_LORA_SPI_HOST, + CONFIG_LORA_PIN_MOSI, + CONFIG_LORA_PIN_MISO, + CONFIG_LORA_PIN_SCK, + CONFIG_LORA_PIN_CS, + CONFIG_LORA_PIN_RST, + CONFIG_LORA_PIN_BUSY, + CONFIG_LORA_PIN_DIO1); + if (CONFIG_LORA_PIN_BUSY < 0) { + ESP_LOGE(TAG, "BUSY pin обов'язковий для LR1121, вкажіть коректний GPIO"); + return false; + } + + if (radio_setup_spi() != ESP_OK) { + ESP_LOGE(TAG, "SPI init failed"); + return false; + } + if (lr1121_hw_init() != ESP_OK) { + ESP_LOGE(TAG, "IO init failed"); + return false; + } + if (lr1121_system_boot() != ESP_OK) { + ESP_LOGE(TAG, "System boot failed"); + return false; + } + if (lr1121_apply_lora(&s_params) != ESP_OK) { + ESP_LOGE(TAG, "Initial params failed"); + return false; + } + + if (!s_is_tx) { + lr1121_enter_rx_continuous(); + } + + ESP_LOGI(TAG, "Radio ready: %.2f MHz BW %d kHz SF %d CR 4/%d TX %d dBm", + s_params.freq_centi_mhz / 100.0f, + s_params.bw_khz, + s_params.sf, + s_params.cr, + s_params.tx_power_dbm); + return true; +} + +esp_err_t lora_radio_apply_params(const lora_params_t *params) +{ + if (!params) { + return ESP_ERR_INVALID_ARG; + } + lora_params_t clean = *params; + sanitize_params(&clean); + esp_err_t err = lr1121_apply_lora(&clean); + if (err == ESP_OK) { + s_params = clean; + if (!s_is_tx) { + lr1121_enter_rx_continuous(); + } + ESP_LOGI(TAG, "Apply LoRa params: %.2f MHz, BW %d kHz, SF %d, CR 4/%d, TX %d dBm", + s_params.freq_centi_mhz / 100.0f, + s_params.bw_khz, + s_params.sf, + s_params.cr, + s_params.tx_power_dbm); + } + return err; +} + +esp_err_t lora_radio_set_tx_power_dbm(int dbm) +{ + lora_params_t next = s_params; + next.tx_power_dbm = dbm; + return lora_radio_apply_params(&next); +} + +esp_err_t lora_radio_set_frequency_centi_mhz(int freq_centi_mhz) +{ + lora_params_t next = s_params; + next.freq_centi_mhz = freq_centi_mhz; + return lora_radio_apply_params(&next); +} + +void lora_radio_get_params(lora_params_t *out) +{ + if (!out) { + return; + } + *out = s_params; +} + +void lora_radio_tick_tx(void) +{ + if (!s_is_tx) { + return; + } + lr11xx_system_irq_mask_t irq_status = 0; + if (lr11xx_system_get_and_clear_irq_status(&s_lr, &irq_status) != LR11XX_STATUS_OK) { + return; + } + if (irq_status == 0) { + return; + } + s_metrics.last_status = (uint8_t)(irq_status & 0xFF); + if (irq_status & LR11XX_SYSTEM_IRQ_TX_DONE) { + s_tx_in_progress = false; + ESP_LOGI(TAG, "TX done"); + } + if (irq_status & LR11XX_SYSTEM_IRQ_TIMEOUT) { + s_tx_in_progress = false; + ESP_LOGW(TAG, "TX timeout"); + } +} + +void lora_radio_tick_rx(void) +{ + if (s_is_tx) { + return; + } + + lr11xx_system_irq_mask_t irq_status = 0; + if (lr11xx_system_get_and_clear_irq_status(&s_lr, &irq_status) != LR11XX_STATUS_OK) { + return; + } + if (irq_status == 0) { + int64_t now = esp_timer_get_time(); + if (now - s_last_rx_restart_us > 1000000) { + lr1121_enter_rx_continuous(); + } + return; + } + s_metrics.last_status = (uint8_t)(irq_status & 0xFF); + + if (irq_status & LR11XX_SYSTEM_IRQ_RX_DONE) { + lr11xx_radio_rx_buffer_status_t rx_status = {0}; + if (lr11xx_radio_get_rx_buffer_status(&s_lr, &rx_status) == LR11XX_STATUS_OK && rx_status.pld_len_in_bytes > 0) { + uint8_t buf[sizeof(s_last_payload)]; + uint8_t copy_len = rx_status.pld_len_in_bytes; + if (copy_len >= sizeof(buf)) { + copy_len = sizeof(buf) - 1; + } + if (lr11xx_regmem_read_buffer8(&s_lr, buf, rx_status.buffer_start_pointer, copy_len) == LR11XX_STATUS_OK) { + memcpy(s_last_payload, buf, copy_len); + s_last_payload[copy_len] = '\0'; + s_last_payload_len = copy_len; + + lr11xx_radio_pkt_status_lora_t pkt = {0}; + if (lr11xx_radio_get_lora_pkt_status(&s_lr, &pkt) == LR11XX_STATUS_OK) { + s_metrics.rssi_dbm = pkt.rssi_pkt_in_dbm; + s_metrics.snr_db = pkt.snr_pkt_in_db; + } + ESP_LOGI(TAG, "RX %u bytes RSSI=%ddBm SNR=%ddB \"%s\"", + (unsigned)copy_len, s_metrics.rssi_dbm, s_metrics.snr_db, s_last_payload); + } + } + lr1121_enter_rx_continuous(); + } + + if (irq_status & (LR11XX_SYSTEM_IRQ_TIMEOUT | LR11XX_SYSTEM_IRQ_HEADER_ERROR | LR11XX_SYSTEM_IRQ_CRC_ERROR)) { + ESP_LOGW(TAG, "RX irq=0x%04X (timeout/header/crc)", irq_status); + lr1121_enter_rx_continuous(); + } +} + +void lora_radio_get_metrics(lora_metrics_t *out) +{ + if (!out) { + return; + } + *out = s_metrics; +} + +void lora_radio_get_last_payload(char *out, size_t out_len) +{ + if (!out || out_len == 0) { + return; + } + if (s_last_payload_len == 0) { + strlcpy(out, "(no data)", out_len); + return; + } + strlcpy(out, s_last_payload, out_len); +} + +esp_err_t lora_radio_send(const uint8_t *data, size_t len) +{ + if (!s_is_tx) { + return ESP_ERR_INVALID_STATE; + } + if (!data || len == 0) { + return ESP_ERR_INVALID_ARG; + } + if (len > 255) { + len = 255; // обмеження довжини пакета + } + + // Оновити pkt params під фактичну довжину + lr11xx_radio_pkt_params_lora_t pkt = { + .preamble_len_in_symb = s_params.preamble_syms, + .header_type = s_params.header_implicit ? LR11XX_RADIO_LORA_PKT_IMPLICIT : LR11XX_RADIO_LORA_PKT_EXPLICIT, + .pld_len_in_bytes = (uint8_t)len, + .crc = s_params.crc_on ? LR11XX_RADIO_LORA_CRC_ON : LR11XX_RADIO_LORA_CRC_OFF, + .iq = s_params.iq_invert ? LR11XX_RADIO_LORA_IQ_INVERTED : LR11XX_RADIO_LORA_IQ_STANDARD, + }; + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_lora_pkt_params(&s_lr, &pkt)), TAG, "pkt params tx"); + + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_regmem_write_buffer8(&s_lr, data, len)), TAG, "write buf"); + + // timeout 0 => без тайм-ауту, чекаємо TX_DONE + ESP_RETURN_ON_ERROR(lr_status_to_esp(lr11xx_radio_set_tx(&s_lr, 0)), TAG, "set tx"); + s_tx_in_progress = true; + s_last_tx_us = esp_timer_get_time(); + s_params.payload_len = (uint8_t)len; + return ESP_OK; +} diff --git a/components/ui/CMakeLists.txt b/components/ui/CMakeLists.txt new file mode 100644 index 0000000..527b822 --- /dev/null +++ b/components/ui/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register( + SRCS "ui.c" + INCLUDE_DIRS "include" + REQUIRES common + PRIV_REQUIRES esp_driver_i2c esp_driver_gpio driver +) diff --git a/components/ui/Kconfig b/components/ui/Kconfig new file mode 100644 index 0000000..418f7eb --- /dev/null +++ b/components/ui/Kconfig @@ -0,0 +1,38 @@ +menu "Display" + +config DISPLAY_DRIVER + string "Display driver" + default "SSD1306 I2C OLED" + +config DISPLAY_I2C_PORT + int "Display I2C port" + range 0 1 + default 0 + +config DISPLAY_I2C_ADDR + int "Display I2C address" + default 60 + help + Decimal value of OLED I2C address (default 0x3C = 60). + +config DISPLAY_PIN_SDA + int "Display SDA pin" + default 21 + +config DISPLAY_PIN_RST + int "Display RESET pin (-1 if not used)" + default -1 + +config DISPLAY_PIN_SCL + int "Display SCL pin" + default 22 + +config DISPLAY_WIDTH + int "Display width (px)" + default 128 + +config DISPLAY_HEIGHT + int "Display height (px)" + default 64 + +endmenu diff --git a/components/ui/include/ui.h b/components/ui/include/ui.h new file mode 100644 index 0000000..2568691 --- /dev/null +++ b/components/ui/include/ui.h @@ -0,0 +1,5 @@ +#pragma once + +void ui_init(void); +void ui_show_role(const char *role); +void ui_show_status(const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6); diff --git a/components/ui/ui.c b/components/ui/ui.c new file mode 100644 index 0000000..6a2fbfc --- /dev/null +++ b/components/ui/ui.c @@ -0,0 +1,386 @@ +#include "ui.h" +#include +#include +#include +#include +#include +#include "esp_log.h" +#include "sdkconfig.h" +#include "driver/i2c.h" +#include "driver/gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#define TAG "ui" + +#define OLED_CONTROL_CMD 0x00 +#define OLED_CONTROL_DATA 0x40 +#define I2C_TIMEOUT_MS 50 + +static i2c_port_t s_port = CONFIG_DISPLAY_I2C_PORT; +static uint8_t s_addr = CONFIG_DISPLAY_I2C_ADDR; +static int s_reset_pin = CONFIG_DISPLAY_PIN_RST; +static uint8_t s_framebuf[CONFIG_DISPLAY_WIDTH * CONFIG_DISPLAY_HEIGHT / 8]; +static char s_role[32] = "LoRa"; +static bool s_i2c_ok = false; +static bool s_dirty = true; +static char s_prev_lines[6][48] = {{0}}; +static char s_prev_role[sizeof(s_role)] = {0}; + +// 5x7 font (ASCII 32..127), taken from a minimal public-domain font +static const uint8_t font5x7[] = { + 0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x5F,0x00,0x00, 0x00,0x07,0x00,0x07,0x00, 0x14,0x7F,0x14,0x7F,0x14, + 0x24,0x2A,0x7F,0x2A,0x12, 0x23,0x13,0x08,0x64,0x62, 0x36,0x49,0x55,0x22,0x50, 0x00,0x05,0x03,0x00,0x00, + 0x00,0x1C,0x22,0x41,0x00, 0x00,0x41,0x22,0x1C,0x00, 0x14,0x08,0x3E,0x08,0x14, 0x08,0x08,0x3E,0x08,0x08, + 0x00,0x50,0x30,0x00,0x00, 0x08,0x08,0x08,0x08,0x08, 0x00,0x60,0x60,0x00,0x00, 0x20,0x10,0x08,0x04,0x02, + 0x3E,0x51,0x49,0x45,0x3E, 0x00,0x42,0x7F,0x40,0x00, 0x72,0x49,0x49,0x49,0x46, 0x21,0x41,0x45,0x4B,0x31, + 0x18,0x14,0x12,0x7F,0x10, 0x27,0x45,0x45,0x45,0x39, 0x3C,0x4A,0x49,0x49,0x30, 0x01,0x71,0x09,0x05,0x03, + 0x36,0x49,0x49,0x49,0x36, 0x06,0x49,0x49,0x29,0x1E, 0x00,0x36,0x36,0x00,0x00, 0x00,0x56,0x36,0x00,0x00, + 0x08,0x14,0x22,0x41,0x00, 0x14,0x14,0x14,0x14,0x14, 0x00,0x41,0x22,0x14,0x08, 0x02,0x01,0x59,0x09,0x06, + 0x3E,0x41,0x5D,0x59,0x4E, 0x7E,0x11,0x11,0x11,0x7E, 0x7F,0x49,0x49,0x49,0x36, 0x3E,0x41,0x41,0x41,0x22, + 0x7F,0x41,0x41,0x22,0x1C, 0x7F,0x49,0x49,0x49,0x41, 0x7F,0x09,0x09,0x09,0x01, 0x3E,0x41,0x49,0x49,0x7A, + 0x7F,0x08,0x08,0x08,0x7F, 0x00,0x41,0x7F,0x41,0x00, 0x20,0x40,0x41,0x3F,0x01, 0x7F,0x08,0x14,0x22,0x41, + 0x7F,0x40,0x40,0x40,0x40, 0x7F,0x02,0x04,0x02,0x7F, 0x7F,0x04,0x08,0x10,0x7F, 0x3E,0x41,0x41,0x41,0x3E, + 0x7F,0x09,0x09,0x09,0x06, 0x3E,0x41,0x51,0x21,0x5E, 0x7F,0x09,0x19,0x29,0x46, 0x46,0x49,0x49,0x49,0x31, + 0x01,0x01,0x7F,0x01,0x01, 0x3F,0x40,0x40,0x40,0x3F, 0x1F,0x20,0x40,0x20,0x1F, 0x7F,0x20,0x18,0x20,0x7F, + 0x63,0x14,0x08,0x14,0x63, 0x03,0x04,0x78,0x04,0x03, 0x61,0x51,0x49,0x45,0x43, 0x00,0x7F,0x41,0x41,0x00, + 0x02,0x04,0x08,0x10,0x20, 0x00,0x41,0x41,0x7F,0x00, 0x04,0x02,0x01,0x02,0x04, 0x40,0x40,0x40,0x40,0x40, + 0x00,0x01,0x02,0x04,0x00, 0x20,0x54,0x54,0x54,0x78, 0x7F,0x48,0x44,0x44,0x38, 0x38,0x44,0x44,0x44,0x28, + 0x38,0x44,0x44,0x48,0x7F, 0x38,0x54,0x54,0x54,0x18, 0x08,0x7E,0x09,0x01,0x02, 0x0C,0x52,0x52,0x52,0x3E, + 0x7F,0x08,0x04,0x04,0x78, 0x00,0x44,0x7D,0x40,0x00, 0x20,0x40,0x40,0x3D,0x00, 0x7F,0x10,0x28,0x44,0x00, + 0x00,0x41,0x7F,0x40,0x00, 0x7C,0x04,0x18,0x04,0x78, 0x7C,0x08,0x04,0x04,0x78, 0x38,0x44,0x44,0x44,0x38, + 0x7C,0x14,0x14,0x14,0x08, 0x08,0x14,0x14,0x18,0x7C, 0x7C,0x08,0x04,0x04,0x08, 0x48,0x54,0x54,0x54,0x24, + 0x04,0x3F,0x44,0x40,0x20, 0x3C,0x40,0x40,0x20,0x7C, 0x1C,0x20,0x40,0x20,0x1C, 0x3C,0x40,0x30,0x40,0x3C, + 0x44,0x28,0x10,0x28,0x44, 0x0C,0x50,0x50,0x50,0x3C, 0x44,0x64,0x54,0x4C,0x44, 0x00,0x08,0x36,0x41,0x00, + 0x00,0x00,0x7F,0x00,0x00, 0x00,0x41,0x36,0x08,0x00, 0x10,0x08,0x08,0x10,0x08, 0x78,0x46,0x41,0x46,0x78 +}; + +static esp_err_t ssd1306_write(uint8_t control, const uint8_t *data, size_t len) +{ + if (!s_i2c_ok) { + return ESP_ERR_INVALID_STATE; + } + if (!data || len == 0) { + return ESP_OK; + } + static uint8_t buf[CONFIG_DISPLAY_WIDTH + 2]; + if (len + 1 > sizeof(buf)) { + return ESP_ERR_INVALID_SIZE; + } + buf[0] = control; + memcpy(&buf[1], data, len); + return i2c_master_write_to_device(s_port, s_addr, buf, len + 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)); +} + +static inline esp_err_t ssd1306_send_cmd(uint8_t cmd) +{ + if (!s_i2c_ok) { + return ESP_ERR_INVALID_STATE; + } + return ssd1306_write(OLED_CONTROL_CMD, &cmd, 1); +} + +static inline esp_err_t ssd1306_send_cmd2(uint8_t cmd, uint8_t val) +{ + if (!s_i2c_ok) { + return ESP_ERR_INVALID_STATE; + } + uint8_t data[2] = {cmd, val}; + return ssd1306_write(OLED_CONTROL_CMD, data, sizeof(data)); +} + +static inline esp_err_t ssd1306_send_data(const uint8_t *data, size_t len) +{ + if (!s_i2c_ok) { + return ESP_ERR_INVALID_STATE; + } + return ssd1306_write(OLED_CONTROL_DATA, data, len); +} + +static void i2c_init(void) +{ + s_i2c_ok = false; + if (s_reset_pin == CONFIG_DISPLAY_PIN_SCL || s_reset_pin == CONFIG_DISPLAY_PIN_SDA) { + ESP_LOGW(TAG, "RST pin matches I2C pin (RST=%d, SDA=%d, SCL=%d) — ensure wiring is correct", s_reset_pin, CONFIG_DISPLAY_PIN_SDA, CONFIG_DISPLAY_PIN_SCL); + } + + // Базова валідація пінів, щоб не падати через ESP_ERR_INVALID_ARG + if (CONFIG_DISPLAY_PIN_SDA < 0 || CONFIG_DISPLAY_PIN_SCL < 0 || + CONFIG_DISPLAY_PIN_SDA > 48 || CONFIG_DISPLAY_PIN_SCL > 48 || + CONFIG_DISPLAY_PIN_SDA == CONFIG_DISPLAY_PIN_SCL || + CONFIG_DISPLAY_PIN_SDA == 45 || CONFIG_DISPLAY_PIN_SDA == 46 || + CONFIG_DISPLAY_PIN_SCL == 45 || CONFIG_DISPLAY_PIN_SCL == 46) { + ESP_LOGE(TAG, "Invalid I2C pins: SDA=%d SCL=%d (port %d)", + CONFIG_DISPLAY_PIN_SDA, CONFIG_DISPLAY_PIN_SCL, s_port); + return; + } + + i2c_config_t conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = CONFIG_DISPLAY_PIN_SDA, + .scl_io_num = CONFIG_DISPLAY_PIN_SCL, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + }; + conf.master.clk_speed = 400000; + esp_err_t err = i2c_param_config(s_port, &conf); + if (err != ESP_OK) { + ESP_LOGE(TAG, "i2c_param_config failed: SDA=%d SCL=%d port=%d err=%s", + CONFIG_DISPLAY_PIN_SDA, CONFIG_DISPLAY_PIN_SCL, s_port, esp_err_to_name(err)); + return; + } + err = i2c_driver_install(s_port, conf.mode, 0, 0, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "i2c_driver_install failed: port=%d err=%s", s_port, esp_err_to_name(err)); + return; + } + s_i2c_ok = true; +} + +static void oled_reset(void) +{ + if (s_reset_pin < 0) { + return; + } + gpio_config_t io_conf = { + .pin_bit_mask = 1ULL << s_reset_pin, + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + gpio_config(&io_conf); + gpio_set_level(s_reset_pin, 1); + vTaskDelay(pdMS_TO_TICKS(1)); + gpio_set_level(s_reset_pin, 0); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(s_reset_pin, 1); + vTaskDelay(pdMS_TO_TICKS(10)); +} + +static void ssd1306_init_sequence(void) +{ + ESP_LOGI(TAG, "Init OLED %s %dx%d at I2C%d addr 0x%02X (SDA=%d, SCL=%d, RST=%d)", + CONFIG_DISPLAY_DRIVER, + CONFIG_DISPLAY_WIDTH, + CONFIG_DISPLAY_HEIGHT, + s_port, + s_addr, + CONFIG_DISPLAY_PIN_SDA, + CONFIG_DISPLAY_PIN_SCL, + CONFIG_DISPLAY_PIN_RST); + + uint8_t com_pins = (CONFIG_DISPLAY_HEIGHT == 32) ? 0x02 : 0x12; + uint8_t multiplex = CONFIG_DISPLAY_HEIGHT - 1; + + ESP_ERROR_CHECK(ssd1306_send_cmd(0xAE)); // display off + ESP_ERROR_CHECK(ssd1306_send_cmd2(0xD5, 0x80)); // clock div + ESP_ERROR_CHECK(ssd1306_send_cmd2(0xA8, multiplex)); // multiplex + ESP_ERROR_CHECK(ssd1306_send_cmd2(0xD3, 0x00)); // display offset + ESP_ERROR_CHECK(ssd1306_send_cmd(0x40)); // start line = 0 + ESP_ERROR_CHECK(ssd1306_send_cmd2(0x8D, 0x14)); // charge pump on + ESP_ERROR_CHECK(ssd1306_send_cmd2(0x20, 0x00)); // horizontal addressing + ESP_ERROR_CHECK(ssd1306_send_cmd(0xA1)); // segment remap + ESP_ERROR_CHECK(ssd1306_send_cmd(0xC8)); // COM scan dec + ESP_ERROR_CHECK(ssd1306_send_cmd2(0xDA, com_pins)); // COM pins config + ESP_ERROR_CHECK(ssd1306_send_cmd2(0x81, 0x7F)); // contrast + ESP_ERROR_CHECK(ssd1306_send_cmd2(0xD9, 0xF1)); // pre-charge + ESP_ERROR_CHECK(ssd1306_send_cmd2(0xDB, 0x40)); // VCOM detect + ESP_ERROR_CHECK(ssd1306_send_cmd(0xA4)); // resume RAM + ESP_ERROR_CHECK(ssd1306_send_cmd(0xA6)); // normal display + ESP_ERROR_CHECK(ssd1306_send_cmd(0x2E)); // deactivate scroll + ESP_ERROR_CHECK(ssd1306_send_cmd(0xAF)); // display on + + // set column/page range to avoid leftover data on wider panels + ESP_ERROR_CHECK(ssd1306_send_cmd(0x21)); + ESP_ERROR_CHECK(ssd1306_send_cmd(0x00)); + ESP_ERROR_CHECK(ssd1306_send_cmd(CONFIG_DISPLAY_WIDTH - 1)); + ESP_ERROR_CHECK(ssd1306_send_cmd(0x22)); + ESP_ERROR_CHECK(ssd1306_send_cmd(0x00)); + ESP_ERROR_CHECK(ssd1306_send_cmd((CONFIG_DISPLAY_HEIGHT / 8) - 1)); +} + +static void clear_buffer(void) +{ + memset(s_framebuf, 0x00, sizeof(s_framebuf)); +} + +static void draw_pixel(int x, int y, bool on) +{ + if (x < 0 || x >= CONFIG_DISPLAY_WIDTH || y < 0 || y >= CONFIG_DISPLAY_HEIGHT) { + return; + } + int page = y / 8; + int bit = y % 8; + size_t idx = page * CONFIG_DISPLAY_WIDTH + x; + if (on) { + s_framebuf[idx] |= (1U << bit); + } else { + s_framebuf[idx] &= ~(1U << bit); + } +} + +static void draw_char_scaled(int x, int y, char c, int scale) +{ + if (c < 32 || c > 126) { + c = '?'; + } + if (scale < 1) { + scale = 1; + } + const uint8_t *glyph = &font5x7[(c - 32) * 5]; + for (int col = 0; col < 5; col++) { + uint8_t line = glyph[col]; + for (int row = 0; row < 7; row++) { + bool pixel_on = line & (1 << row); + for (int dx = 0; dx < scale; dx++) { + for (int dy = 0; dy < scale; dy++) { + int px = x + col * scale + dx; + int py = y + row * scale + dy; + draw_pixel(px, py, pixel_on); + } + } + } + } + // spacing column + for (int dx = 0; dx < scale; dx++) { + int px = x + 5 * scale + dx; + for (int row = 0; row < 7 * scale; row++) { + draw_pixel(px, y + row, false); + } + } +} + +static void draw_text_scaled(int x, int y, const char *text, int scale) +{ + int cursor_x = x; + while (text && *text) { + draw_char_scaled(cursor_x, y, *text, scale); + cursor_x += 6 * scale; + text++; + } +} + +static void sanitize_ascii(const char *in, char *out, size_t out_len) +{ + if (!out || out_len == 0) { + return; + } + if (!in) { + out[0] = '\0'; + return; + } + size_t out_idx = 0; + for (size_t i = 0; in[i] != '\0' && out_idx + 1 < out_len; i++) { + unsigned char c = (unsigned char)in[i]; + if (c >= 32 && c <= 126) { + out[out_idx++] = (char)c; + } else { + out[out_idx++] = ' '; + } + } + out[out_idx] = '\0'; +} + +static void sanitize_role(const char *role) +{ + if (!role || role[0] == '\0') { + strlcpy(s_role, "LoRa", sizeof(s_role)); + return; + } + if (strstr(role, "ередавач")) { + strlcpy(s_role, "TX", sizeof(s_role)); + return; + } + if (strstr(role, "риймач")) { + strlcpy(s_role, "RX", sizeof(s_role)); + return; + } + sanitize_ascii(role, s_role, sizeof(s_role)); + if (s_role[0] == '\0') { + strlcpy(s_role, "LoRa", sizeof(s_role)); + } +} + +static void ssd1306_flush(void) +{ + const int pages = CONFIG_DISPLAY_HEIGHT / 8; + for (int page = 0; page < pages; page++) { + ESP_ERROR_CHECK(ssd1306_send_cmd(0xB0 | page)); + ESP_ERROR_CHECK(ssd1306_send_cmd(0x00)); + ESP_ERROR_CHECK(ssd1306_send_cmd(0x10)); + const uint8_t *line = &s_framebuf[page * CONFIG_DISPLAY_WIDTH]; + ESP_ERROR_CHECK(ssd1306_send_data(line, CONFIG_DISPLAY_WIDTH)); + } +} + +void ui_init(void) +{ + i2c_init(); + if (!s_i2c_ok) { + ESP_LOGE(TAG, "UI disabled: I2C init failed"); + return; + } + oled_reset(); + ssd1306_init_sequence(); + clear_buffer(); + ssd1306_flush(); + s_dirty = true; +} + +void ui_show_role(const char *role) +{ + if (!s_i2c_ok) { + return; + } + ESP_LOGI(TAG, "UI: role %s", role); + sanitize_role(role); + s_dirty = true; +} + +void ui_show_status(const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6) +{ + if (!s_i2c_ok) { + return; + } + const char *lines[6] = {line1, line2, line3, line4, line5, line6}; + char safe[6][48] = {{0}}; + bool changed = s_dirty; + + if (strncmp(s_prev_role, s_role, sizeof(s_prev_role)) != 0) { + changed = true; + strlcpy(s_prev_role, s_role, sizeof(s_prev_role)); + } + + for (int i = 0; i < 6; i++) { + sanitize_ascii(lines[i], safe[i], sizeof(safe[i])); + if (strncmp(s_prev_lines[i], safe[i], sizeof(s_prev_lines[i])) != 0) { + changed = true; + strlcpy(s_prev_lines[i], safe[i], sizeof(s_prev_lines[i])); + } + } + + if (!changed) { + return; + } + + clear_buffer(); + + // Header + draw_text_scaled(0, 0, s_role, 2); + + // Рядки починаються нижче заголовка, щоб не накладались + int y = 16; + const int row_height = 8; + for (int i = 0; i < 6; i++) { + if (safe[i][0] != '\0') { + draw_text_scaled(0, y, safe[i], 1); + } + y += row_height + 2; + } + + ssd1306_flush(); + s_dirty = false; + ESP_LOGI(TAG, "UI: %s | %s | %s | %s | %s | %s", + safe[0], safe[1], safe[2], safe[3], safe[4], safe[5]); +} diff --git a/components/usb_api/CMakeLists.txt b/components/usb_api/CMakeLists.txt new file mode 100644 index 0000000..bb3e4bb --- /dev/null +++ b/components/usb_api/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "usb_api.c" + INCLUDE_DIRS "include" + REQUIRES driver +) diff --git a/components/usb_api/include/usb_api.h b/components/usb_api/include/usb_api.h new file mode 100644 index 0000000..d15ad44 --- /dev/null +++ b/components/usb_api/include/usb_api.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +typedef void (*usb_api_line_handler_t)(const char *line); + +// Initialize USB Serial/JTAG line reader; pass handler for complete lines. +void usb_api_init(usb_api_line_handler_t handler); + +// Non-blocking poll; call from main loop/task. +void usb_api_tick(void); + +// Send a line to host (no automatic newline added). +void usb_api_send_line(const char *line); + +// Return true when host is connected over USB Serial/JTAG. +bool usb_api_is_connected(void); diff --git a/components/usb_api/usb_api.c b/components/usb_api/usb_api.c new file mode 100644 index 0000000..6258961 --- /dev/null +++ b/components/usb_api/usb_api.c @@ -0,0 +1,75 @@ +#include "usb_api.h" +#include +#include "esp_err.h" +#include "esp_log.h" +#include "driver/usb_serial_jtag.h" + +static const char *TAG = "usb_api"; +static usb_api_line_handler_t s_handler = NULL; +static char s_line_buf[256]; +static size_t s_line_len = 0; + +void usb_api_init(usb_api_line_handler_t handler) +{ + s_handler = handler; + s_line_len = 0; + usb_serial_jtag_driver_config_t cfg = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(); + esp_err_t err = usb_serial_jtag_driver_install(&cfg); + if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { + ESP_LOGE(TAG, "usb driver install failed: %s", esp_err_to_name(err)); + } +} + +bool usb_api_is_connected(void) +{ + return usb_serial_jtag_is_connected(); +} + +void usb_api_send_line(const char *line) +{ + if (!line) { + return; + } + size_t len = strlen(line); + if (len == 0) { + return; + } + int written = usb_serial_jtag_write_bytes((const uint8_t *)line, len, 0); + if (written < 0) { + ESP_LOGW(TAG, "usb write failed"); + } +} + +static void handle_complete_line(void) +{ + if (s_line_len == 0) { + return; + } + s_line_buf[s_line_len] = '\0'; + if (s_handler) { + s_handler(s_line_buf); + } + s_line_len = 0; +} + +void usb_api_tick(void) +{ + uint8_t buf[64]; + int n = usb_serial_jtag_read_bytes(buf, sizeof(buf), 0); + if (n <= 0) { + return; + } + for (int i = 0; i < n; i++) { + char c = (char)buf[i]; + if (c == '\r' || c == '\n') { + handle_complete_line(); + continue; + } + if (s_line_len + 1 >= sizeof(s_line_buf)) { + // drop line on overflow + s_line_len = 0; + continue; + } + s_line_buf[s_line_len++] = c; + } +} diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 0000000..f0c20ca --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,18 @@ +## IDF Component Manager Manifest File +dependencies: + ## Required IDF version + idf: + version: '>=4.1.0' + # # Put list of dependencies here + # # For components maintained by Espressif: + # component: "~1.0.0" + # # For 3rd party components: + # username/component: ">=1.0.0,<2.0.0" + # username2/component2: + # version: "~1.0.0" + # # For transient dependencies `public` flag can be set. + # # `public` flag doesn't have an effect dependencies of the `main` component. + # # All dependencies of `main` are public by default. + # public: true + jgromes/radiolib: ^7.1.2 + waveshare/esp_lora_1121: ^1.0.0 diff --git a/py_app/README.md b/py_app/README.md new file mode 100644 index 0000000..5d5b7e5 --- /dev/null +++ b/py_app/README.md @@ -0,0 +1,25 @@ +# Py Qt configurator for RX/TX firmware + +Desktop GUI (PyQt6) that talks to the device over USB CDC (virtual COM). It sends line-delimited JSON commands and applies changes immediately when you edit controls. + +## Setup +``` +cd py_app +python3 -m venv .venv +source .venv/bin/activate # or .venv\Scripts\activate on Windows +pip install -r requirements.txt +python main.py +``` + +## Protocol expected on the device side +The firmware should read UTF-8 lines from the USB CDC serial port and handle JSON commands: +- `{"cmd": "set_params", "params": {freq_mhz, band, bw_khz, sf, cr, tx_power_dbm, period_ms, tx_enabled, payload}}` — apply and persist as needed. +- `{"cmd": "get_status"}` — respond with JSON status (e.g. metrics, current params). +- `{"cmd": "reboot_bootloader"}` — reboot into ROM bootloader for flashing. + +The GUI logs any text received, so returning JSON or human-readable lines works. + +## Notes +- Bands offered: 430/868/915/L/S/2.4G; frequency control is in MHz (150–2500). +- Payload length is limited to 31 chars to match the firmware UI constraint. +- Changes are debounced by 150 ms; dragging a spinbox will send the last value when you stop. diff --git a/py_app/main.py b/py_app/main.py new file mode 100644 index 0000000..df1cc15 --- /dev/null +++ b/py_app/main.py @@ -0,0 +1,426 @@ +#!/usr/bin/env python3 +""" +PyQt6 desktop tool for configuring the RX/TX firmware over USB CDC. +Protocol: line-delimited JSON. Expected commands (device-side to implement): + {"cmd": "set_params", "params": {freq_mhz, bw_khz, sf, cr, tx_power_dbm, payload, period_ms, tx_enabled, band}} + {"cmd": "get_status"} + {"cmd": "reboot_bootloader"} +The GUI sends updates automatically when fields change. +""" +from __future__ import annotations + +import json +import sys +import threading +from dataclasses import dataclass +from typing import Dict, Optional + +from PyQt6.QtCore import QTimer, pyqtSignal, QObject +from PyQt6.QtWidgets import ( + QApplication, + QCheckBox, + QComboBox, + QGridLayout, + QGroupBox, + QHBoxLayout, + QLabel, + QLineEdit, + QMainWindow, + QPushButton, + QSpinBox, + QTextEdit, + QVBoxLayout, + QWidget, +) + +import serial +from serial.tools import list_ports + + +BANDS = [ + ("430", 430), + ("868", 868), + ("915", 915), + ("L" , 1550), # MHz midpoint + ("S" , 2000), + ("2.4G", 2442), +] + + +@dataclass +class DeviceParams: + freq_mhz: int = 433 + band: str = "430" + bw_khz: int = 125 + sf: int = 7 + cr: int = 5 + tx_power_dbm: int = 14 + period_ms: int = 1000 + tx_enabled: bool = True + payload: str = "PING 1" + + def to_dict(self) -> Dict[str, object]: + return { + "freq_mhz": self.freq_mhz, + "band": self.band, + "bw_khz": self.bw_khz, + "sf": self.sf, + "cr": self.cr, + "tx_power_dbm": self.tx_power_dbm, + "period_ms": self.period_ms, + "tx_enabled": self.tx_enabled, + "payload": self.payload, + } + + +class DeviceConnection(QObject): + status_changed = pyqtSignal(str) + data_received = pyqtSignal(str) + connected_changed = pyqtSignal(bool) + + def __init__(self) -> None: + super().__init__() + self._ser: Optional[serial.Serial] = None + self._rx_thread: Optional[threading.Thread] = None + self._stop = threading.Event() + + @staticmethod + def available_ports(): + return list_ports.comports() + + def connect(self, port: str, baud: int = 115200) -> None: + self.disconnect() + try: + self._ser = serial.Serial(port=port, baudrate=baud, timeout=0.1) + except serial.SerialException as exc: + self.status_changed.emit(f"Connect failed: {exc}") + self._ser = None + return + self._stop.clear() + self._rx_thread = threading.Thread(target=self._rx_loop, daemon=True) + self._rx_thread.start() + self.status_changed.emit(f"Connected to {port} @ {baud}") + self.connected_changed.emit(True) + + def disconnect(self) -> None: + self._stop.set() + if self._rx_thread and self._rx_thread.is_alive(): + self._rx_thread.join(timeout=0.5) + self._rx_thread = None + if self._ser: + try: + self._ser.close() + except Exception: + pass + was_connected = self._ser is not None + self._ser = None + if was_connected: + self.connected_changed.emit(False) + self.status_changed.emit("Disconnected") + + def is_connected(self) -> bool: + return self._ser is not None and self._ser.is_open + + def send_json(self, obj: Dict[str, object]) -> None: + if not self.is_connected(): + self.status_changed.emit("Not connected") + return + try: + line = json.dumps(obj) + "\n" + self._ser.write(line.encode("utf-8")) + except Exception as exc: + self.status_changed.emit(f"Send failed: {exc}") + + def _rx_loop(self) -> None: + assert self._ser is not None + while not self._stop.is_set(): + try: + raw = self._ser.readline() + except Exception as exc: + self.status_changed.emit(f"Read error: {exc}") + break + if not raw: + continue + try: + text = raw.decode("utf-8", errors="replace").strip() + except Exception: + continue + if text: + self.data_received.emit(text) + self.connected_changed.emit(False) + + +class MainWindow(QMainWindow): + def __init__(self) -> None: + super().__init__() + self.setWindowTitle("LoRa RX/TX Config") + self.conn = DeviceConnection() + self.params = DeviceParams() + self._tx_widgets = [] + self._apply_timer = QTimer(self) + self._apply_timer.setSingleShot(True) + self._apply_timer.timeout.connect(self.send_params) + self._build_ui() + self._wire() + + def _build_ui(self) -> None: + container = QWidget() + main_layout = QVBoxLayout(container) + + # Connection controls + conn_row = QHBoxLayout() + self.port_combo = QComboBox() + self.refresh_ports() + self.btn_refresh = QPushButton("Refresh") + self.btn_connect = QPushButton("Connect") + conn_row.addWidget(QLabel("Port:")) + conn_row.addWidget(self.port_combo, stretch=1) + conn_row.addWidget(self.btn_refresh) + conn_row.addWidget(self.btn_connect) + main_layout.addLayout(conn_row) + + # Parameters + params_group = QGroupBox("Radio parameters") + grid = QGridLayout(params_group) + + self.spin_freq = QSpinBox() + self.spin_freq.setRange(150, 2500) + self.spin_freq.setSuffix(" MHz") + self.spin_freq.setValue(self.params.freq_mhz) + + self.combo_band = QComboBox() + for name, _ in BANDS: + self.combo_band.addItem(name) + self.combo_band.setCurrentText(self.params.band) + + self.combo_bw = QComboBox() + for bw in (125, 250, 500): + self.combo_bw.addItem(f"{bw} kHz", bw) + self.combo_bw.setCurrentIndex(0) + + self.combo_sf = QComboBox() + for sf in range(5, 13): + self.combo_sf.addItem(f"SF{sf}", sf) + self.combo_sf.setCurrentText("SF7") + + self.combo_cr = QComboBox() + for cr in range(5, 9): + self.combo_cr.addItem(f"4/{cr}", cr) + self.combo_cr.setCurrentIndex(0) + + self.spin_power = QSpinBox() + self.spin_power.setRange(-17, 22) + self.spin_power.setSuffix(" dBm") + self.spin_power.setValue(self.params.tx_power_dbm) + + self.spin_period = QSpinBox() + self.spin_period.setRange(100, 60000) + self.spin_period.setSingleStep(100) + self.spin_period.setSuffix(" ms") + self.spin_period.setValue(self.params.period_ms) + + self.chk_tx_enabled = QCheckBox("TX enabled") + self.chk_tx_enabled.setChecked(self.params.tx_enabled) + + self.edit_payload = QLineEdit(self.params.payload) + self.edit_payload.setMaxLength(31) + + grid.addWidget(QLabel("Band"), 0, 0) + grid.addWidget(self.combo_band, 0, 1) + grid.addWidget(QLabel("Frequency"), 1, 0) + grid.addWidget(self.spin_freq, 1, 1) + grid.addWidget(QLabel("Bandwidth"), 2, 0) + grid.addWidget(self.combo_bw, 2, 1) + grid.addWidget(QLabel("Spreading factor"), 3, 0) + grid.addWidget(self.combo_sf, 3, 1) + grid.addWidget(QLabel("Coding rate"), 4, 0) + grid.addWidget(self.combo_cr, 4, 1) + self.lbl_tx_power = QLabel("TX power") + grid.addWidget(self.lbl_tx_power, 5, 0) + grid.addWidget(self.spin_power, 5, 1) + self.lbl_payload = QLabel("Payload") + grid.addWidget(self.lbl_payload, 6, 0) + grid.addWidget(self.edit_payload, 6, 1) + self.lbl_period = QLabel("Period") + grid.addWidget(self.lbl_period, 7, 0) + grid.addWidget(self.spin_period, 7, 1) + grid.addWidget(self.chk_tx_enabled, 8, 1) + + main_layout.addWidget(params_group) + + # Actions + actions = QHBoxLayout() + self.btn_boot = QPushButton("Bootloader") + actions.addWidget(self.btn_boot) + main_layout.addLayout(actions) + + # Status widgets for live metrics + metrics_row = QHBoxLayout() + self.lbl_role = QLabel("role: ?") + self.lbl_snr = QLabel("SNR: -") + self.lbl_rssi = QLabel("RSSI: -") + self.lbl_status = QLabel("IRQ: -") + metrics_row.addWidget(self.lbl_role) + metrics_row.addWidget(self.lbl_snr) + metrics_row.addWidget(self.lbl_rssi) + metrics_row.addWidget(self.lbl_status) + metrics_row.addStretch() + main_layout.addLayout(metrics_row) + + self.lbl_rx_payload = QLabel("Payload: -") + self.lbl_rx_payload.setWordWrap(True) + main_layout.addWidget(self.lbl_rx_payload) + + self.log_view = QTextEdit() + self.log_view.setReadOnly(True) + self.log_view.setPlaceholderText("Incoming lines...") + main_layout.addWidget(self.log_view) + + self.status_label = QLabel("Idle") + main_layout.addWidget(self.status_label) + + self.setCentralWidget(container) + self._tx_widgets = [ + self.lbl_tx_power, + self.spin_power, + self.lbl_payload, + self.edit_payload, + self.lbl_period, + self.spin_period, + self.chk_tx_enabled, + ] + self._set_tx_controls_visible(True) + + def _wire(self) -> None: + self.btn_refresh.clicked.connect(self.refresh_ports) + self.btn_connect.clicked.connect(self.toggle_connect) + self.btn_boot.clicked.connect(self.request_bootloader) + + for widget in ( + self.combo_band, + self.combo_bw, + self.combo_sf, + self.combo_cr, + ): + widget.currentIndexChanged.connect(self.schedule_apply) + + for widget in ( + self.spin_freq, + self.spin_power, + self.spin_period, + ): + widget.valueChanged.connect(self.schedule_apply) + + self.chk_tx_enabled.stateChanged.connect(self.schedule_apply) + self.edit_payload.textChanged.connect(self.schedule_apply) + + self.conn.status_changed.connect(self.append_status) + self.conn.data_received.connect(self.append_rx) + self.conn.connected_changed.connect(self.on_connected_changed) + + self.poll_timer = QTimer(self) + self.poll_timer.timeout.connect(self.request_status) + + def refresh_ports(self) -> None: + current = self.port_combo.currentText() + self.port_combo.clear() + for p in self.conn.available_ports(): + self.port_combo.addItem(p.device) + if current: + idx = self.port_combo.findText(current) + if idx >= 0: + self.port_combo.setCurrentIndex(idx) + + def toggle_connect(self) -> None: + if self.conn.is_connected(): + self.conn.disconnect() + return + port = self.port_combo.currentText() + if not port: + self.append_status("Select a port first") + return + self.conn.connect(port) + + def on_connected_changed(self, connected: bool) -> None: + self.btn_connect.setText("Disconnect" if connected else "Connect") + if connected: + self.send_params() + self.poll_timer.start(1000) + else: + self.poll_timer.stop() + self._set_tx_controls_visible(True) + self.lbl_rx_payload.setText("Payload: -") + + def schedule_apply(self) -> None: + # debounce rapid changes (spinner drags) + self._apply_timer.start(150) + + def gather_params(self) -> DeviceParams: + p = DeviceParams() + p.band = self.combo_band.currentText() + p.freq_mhz = self.spin_freq.value() + p.bw_khz = int(self.combo_bw.currentData()) + p.sf = int(self.combo_sf.currentData()) + p.cr = int(self.combo_cr.currentData()) + p.tx_power_dbm = self.spin_power.value() + p.period_ms = self.spin_period.value() + p.tx_enabled = self.chk_tx_enabled.isChecked() + p.payload = self.edit_payload.text() + return p + + def send_params(self) -> None: + self.params = self.gather_params() + self.conn.send_json({"cmd": "set_params", "params": self.params.to_dict()}) + + def request_status(self) -> None: + self.conn.send_json({"cmd": "get_status"}) + + def request_bootloader(self) -> None: + self.conn.send_json({"cmd": "reboot_bootloader"}) + + def append_status(self, text: str) -> None: + self.status_label.setText(text) + + def append_rx(self, text: str) -> None: + # Try to parse JSON status responses to populate labels + try: + obj = json.loads(text) + except Exception: + self.log_view.append(f"[rx] {text}") + return + + if obj.get("resp") == "status": + role = obj.get("role", "?") + snr = obj.get("snr_db", None) + rssi = obj.get("rssi_dbm", None) + irq = obj.get("last_status", None) + payload = obj.get("payload", None) + self.lbl_role.setText(f"role: {role}") + self.lbl_snr.setText(f"SNR: {snr} dB" if snr is not None else "SNR: -") + self.lbl_rssi.setText(f"RSSI: {rssi} dBm" if rssi is not None else "RSSI: -") + self.lbl_status.setText(f"IRQ: 0x{irq:02X}" if irq is not None else "IRQ: -") + if payload is not None: + self.lbl_rx_payload.setText(f"Payload: {payload}") + else: + self.lbl_rx_payload.setText("Payload: -") + role_text = str(role).lower() + is_rx = role_text.startswith("rx") or role_text == "receiver" + self._set_tx_controls_visible(not is_rx) + else: + self.status_label.setText(text) + + def _set_tx_controls_visible(self, visible: bool) -> None: + for widget in self._tx_widgets: + widget.setVisible(visible) + + +def main() -> int: + app = QApplication(sys.argv) + win = MainWindow() + win.setFixedSize(520, 520) + win.show() + return app.exec() + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/py_app/requirements.txt b/py_app/requirements.txt new file mode 100644 index 0000000..876e7e3 --- /dev/null +++ b/py_app/requirements.txt @@ -0,0 +1,2 @@ +PyQt6==6.6.1 +pyserial==3.5 diff --git a/sdkconfig b/sdkconfig new file mode 100644 index 0000000..c245f55 --- /dev/null +++ b/sdkconfig @@ -0,0 +1,1265 @@ +# +# Automatically generated file. DO NOT EDIT. +# Espressif IoT Development Framework (ESP-IDF) 5.5.1 Project Configuration +# +CONFIG_SOC_CAPS_ECO_VER_MAX=301 +CONFIG_SOC_ADC_SUPPORTED=y +CONFIG_SOC_DAC_SUPPORTED=y +CONFIG_SOC_UART_SUPPORTED=y +CONFIG_SOC_MCPWM_SUPPORTED=y +CONFIG_SOC_GPTIMER_SUPPORTED=y +CONFIG_SOC_SDMMC_HOST_SUPPORTED=y +CONFIG_SOC_BT_SUPPORTED=y +CONFIG_SOC_PCNT_SUPPORTED=y +CONFIG_SOC_PHY_SUPPORTED=y +CONFIG_SOC_WIFI_SUPPORTED=y +CONFIG_SOC_SDIO_SLAVE_SUPPORTED=y +CONFIG_SOC_TWAI_SUPPORTED=y +CONFIG_SOC_EFUSE_SUPPORTED=y +CONFIG_SOC_EMAC_SUPPORTED=y +CONFIG_SOC_ULP_SUPPORTED=y +CONFIG_SOC_CCOMP_TIMER_SUPPORTED=y +CONFIG_SOC_RTC_FAST_MEM_SUPPORTED=y +CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED=y +CONFIG_SOC_RTC_MEM_SUPPORTED=y +CONFIG_SOC_I2S_SUPPORTED=y +CONFIG_SOC_RMT_SUPPORTED=y +CONFIG_SOC_SDM_SUPPORTED=y +CONFIG_SOC_GPSPI_SUPPORTED=y +CONFIG_SOC_LEDC_SUPPORTED=y +CONFIG_SOC_I2C_SUPPORTED=y +CONFIG_SOC_SUPPORT_COEXISTENCE=y +CONFIG_SOC_AES_SUPPORTED=y +CONFIG_SOC_MPI_SUPPORTED=y +CONFIG_SOC_SHA_SUPPORTED=y +CONFIG_SOC_FLASH_ENC_SUPPORTED=y +CONFIG_SOC_SECURE_BOOT_SUPPORTED=y +CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y +CONFIG_SOC_BOD_SUPPORTED=y +CONFIG_SOC_ULP_FSM_SUPPORTED=y +CONFIG_SOC_CLK_TREE_SUPPORTED=y +CONFIG_SOC_MPU_SUPPORTED=y +CONFIG_SOC_WDT_SUPPORTED=y +CONFIG_SOC_SPI_FLASH_SUPPORTED=y +CONFIG_SOC_RNG_SUPPORTED=y +CONFIG_SOC_LIGHT_SLEEP_SUPPORTED=y +CONFIG_SOC_DEEP_SLEEP_SUPPORTED=y +CONFIG_SOC_LP_PERIPH_SHARE_INTERRUPT=y +CONFIG_SOC_PM_SUPPORTED=y +CONFIG_SOC_DPORT_WORKAROUND_DIS_INTERRUPT_LVL=5 +CONFIG_SOC_XTAL_SUPPORT_26M=y +CONFIG_SOC_XTAL_SUPPORT_40M=y +CONFIG_SOC_XTAL_SUPPORT_AUTO_DETECT=y +CONFIG_SOC_ADC_RTC_CTRL_SUPPORTED=y +CONFIG_SOC_ADC_DIG_CTRL_SUPPORTED=y +CONFIG_SOC_ADC_DMA_SUPPORTED=y +CONFIG_SOC_ADC_PERIPH_NUM=2 +CONFIG_SOC_ADC_MAX_CHANNEL_NUM=10 +CONFIG_SOC_ADC_ATTEN_NUM=4 +CONFIG_SOC_ADC_DIGI_CONTROLLER_NUM=2 +CONFIG_SOC_ADC_PATT_LEN_MAX=16 +CONFIG_SOC_ADC_DIGI_MIN_BITWIDTH=9 +CONFIG_SOC_ADC_DIGI_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_DIGI_RESULT_BYTES=2 +CONFIG_SOC_ADC_DIGI_DATA_BYTES_PER_CONV=4 +CONFIG_SOC_ADC_DIGI_MONITOR_NUM=0 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH=2 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW=20 +CONFIG_SOC_ADC_RTC_MIN_BITWIDTH=9 +CONFIG_SOC_ADC_RTC_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_SHARED_POWER=y +CONFIG_SOC_BROWNOUT_RESET_SUPPORTED=y +CONFIG_SOC_SHARED_IDCACHE_SUPPORTED=y +CONFIG_SOC_IDCACHE_PER_CORE=y +CONFIG_SOC_CPU_CORES_NUM=2 +CONFIG_SOC_CPU_INTR_NUM=32 +CONFIG_SOC_CPU_HAS_FPU=y +CONFIG_SOC_HP_CPU_HAS_MULTIPLE_CORES=y +CONFIG_SOC_CPU_BREAKPOINTS_NUM=2 +CONFIG_SOC_CPU_WATCHPOINTS_NUM=2 +CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=0x40 +CONFIG_SOC_DAC_CHAN_NUM=2 +CONFIG_SOC_DAC_RESOLUTION=8 +CONFIG_SOC_DAC_DMA_16BIT_ALIGN=y +CONFIG_SOC_GPIO_PORT=1 +CONFIG_SOC_GPIO_PIN_COUNT=40 +CONFIG_SOC_GPIO_VALID_GPIO_MASK=0xFFFFFFFFFF +CONFIG_SOC_GPIO_IN_RANGE_MAX=39 +CONFIG_SOC_GPIO_OUT_RANGE_MAX=33 +CONFIG_SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK=0xEF0FEA +CONFIG_SOC_GPIO_CLOCKOUT_BY_IO_MUX=y +CONFIG_SOC_GPIO_CLOCKOUT_CHANNEL_NUM=3 +CONFIG_SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP=y +CONFIG_SOC_I2C_NUM=2 +CONFIG_SOC_HP_I2C_NUM=2 +CONFIG_SOC_I2C_FIFO_LEN=32 +CONFIG_SOC_I2C_CMD_REG_NUM=16 +CONFIG_SOC_I2C_SUPPORT_SLAVE=y +CONFIG_SOC_I2C_SUPPORT_APB=y +CONFIG_SOC_I2C_SUPPORT_10BIT_ADDR=y +CONFIG_SOC_I2C_STOP_INDEPENDENT=y +CONFIG_SOC_I2S_NUM=2 +CONFIG_SOC_I2S_HW_VERSION_1=y +CONFIG_SOC_I2S_SUPPORTS_APLL=y +CONFIG_SOC_I2S_SUPPORTS_PLL_F160M=y +CONFIG_SOC_I2S_SUPPORTS_PDM=y +CONFIG_SOC_I2S_SUPPORTS_PDM_TX=y +CONFIG_SOC_I2S_SUPPORTS_PCM2PDM=y +CONFIG_SOC_I2S_SUPPORTS_PDM_RX=y +CONFIG_SOC_I2S_SUPPORTS_PDM2PCM=y +CONFIG_SOC_I2S_PDM_MAX_TX_LINES=1 +CONFIG_SOC_I2S_PDM_MAX_RX_LINES=1 +CONFIG_SOC_I2S_SUPPORTS_ADC_DAC=y +CONFIG_SOC_I2S_SUPPORTS_ADC=y +CONFIG_SOC_I2S_SUPPORTS_DAC=y +CONFIG_SOC_I2S_SUPPORTS_LCD_CAMERA=y +CONFIG_SOC_I2S_MAX_DATA_WIDTH=24 +CONFIG_SOC_I2S_TRANS_SIZE_ALIGN_WORD=y +CONFIG_SOC_I2S_LCD_I80_VARIANT=y +CONFIG_SOC_LCD_I80_SUPPORTED=y +CONFIG_SOC_LCD_I80_BUSES=2 +CONFIG_SOC_LCD_I80_BUS_WIDTH=24 +CONFIG_SOC_LEDC_HAS_TIMER_SPECIFIC_MUX=y +CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y +CONFIG_SOC_LEDC_SUPPORT_REF_TICK=y +CONFIG_SOC_LEDC_SUPPORT_HS_MODE=y +CONFIG_SOC_LEDC_TIMER_NUM=4 +CONFIG_SOC_LEDC_CHANNEL_NUM=8 +CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=20 +CONFIG_SOC_MCPWM_GROUPS=2 +CONFIG_SOC_MCPWM_TIMERS_PER_GROUP=3 +CONFIG_SOC_MCPWM_OPERATORS_PER_GROUP=3 +CONFIG_SOC_MCPWM_COMPARATORS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_GENERATORS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_TRIGGERS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_GPIO_FAULTS_PER_GROUP=3 +CONFIG_SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP=y +CONFIG_SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER=3 +CONFIG_SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP=3 +CONFIG_SOC_MMU_PERIPH_NUM=2 +CONFIG_SOC_MMU_LINEAR_ADDRESS_REGION_NUM=3 +CONFIG_SOC_MPU_MIN_REGION_SIZE=0x20000000 +CONFIG_SOC_MPU_REGIONS_MAX_NUM=8 +CONFIG_SOC_PCNT_GROUPS=1 +CONFIG_SOC_PCNT_UNITS_PER_GROUP=8 +CONFIG_SOC_PCNT_CHANNELS_PER_UNIT=2 +CONFIG_SOC_PCNT_THRES_POINT_PER_UNIT=2 +CONFIG_SOC_RMT_GROUPS=1 +CONFIG_SOC_RMT_TX_CANDIDATES_PER_GROUP=8 +CONFIG_SOC_RMT_RX_CANDIDATES_PER_GROUP=8 +CONFIG_SOC_RMT_CHANNELS_PER_GROUP=8 +CONFIG_SOC_RMT_MEM_WORDS_PER_CHANNEL=64 +CONFIG_SOC_RMT_SUPPORT_REF_TICK=y +CONFIG_SOC_RMT_SUPPORT_APB=y +CONFIG_SOC_RMT_CHANNEL_CLK_INDEPENDENT=y +CONFIG_SOC_RTCIO_PIN_COUNT=18 +CONFIG_SOC_RTCIO_INPUT_OUTPUT_SUPPORTED=y +CONFIG_SOC_RTCIO_HOLD_SUPPORTED=y +CONFIG_SOC_RTCIO_WAKE_SUPPORTED=y +CONFIG_SOC_SDM_GROUPS=1 +CONFIG_SOC_SDM_CHANNELS_PER_GROUP=8 +CONFIG_SOC_SDM_CLK_SUPPORT_APB=y +CONFIG_SOC_SPI_HD_BOTH_INOUT_SUPPORTED=y +CONFIG_SOC_SPI_AS_CS_SUPPORTED=y +CONFIG_SOC_SPI_PERIPH_NUM=3 +CONFIG_SOC_SPI_DMA_CHAN_NUM=2 +CONFIG_SOC_SPI_MAX_CS_NUM=3 +CONFIG_SOC_SPI_SUPPORT_CLK_APB=y +CONFIG_SOC_SPI_MAXIMUM_BUFFER_SIZE=64 +CONFIG_SOC_SPI_MAX_PRE_DIVIDER=8192 +CONFIG_SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_26M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_20M_SUPPORTED=y +CONFIG_SOC_TIMER_GROUPS=2 +CONFIG_SOC_TIMER_GROUP_TIMERS_PER_GROUP=2 +CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=64 +CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=4 +CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y +CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32 +CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16 +CONFIG_SOC_TOUCH_SENSOR_VERSION=1 +CONFIG_SOC_TOUCH_SENSOR_NUM=10 +CONFIG_SOC_TOUCH_MIN_CHAN_ID=0 +CONFIG_SOC_TOUCH_MAX_CHAN_ID=9 +CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y +CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1 +CONFIG_SOC_TWAI_CONTROLLER_NUM=1 +CONFIG_SOC_TWAI_MASK_FILTER_NUM=1 +CONFIG_SOC_TWAI_BRP_MIN=2 +CONFIG_SOC_TWAI_CLK_SUPPORT_APB=y +CONFIG_SOC_TWAI_SUPPORT_MULTI_ADDRESS_LAYOUT=y +CONFIG_SOC_UART_NUM=3 +CONFIG_SOC_UART_HP_NUM=3 +CONFIG_SOC_UART_SUPPORT_APB_CLK=y +CONFIG_SOC_UART_SUPPORT_REF_TICK=y +CONFIG_SOC_UART_FIFO_LEN=128 +CONFIG_SOC_UART_BITRATE_MAX=5000000 +CONFIG_SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE=y +CONFIG_SOC_SPIRAM_SUPPORTED=y +CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y +CONFIG_SOC_SHA_SUPPORT_PARALLEL_ENG=y +CONFIG_SOC_SHA_ENDIANNESS_BE=y +CONFIG_SOC_SHA_SUPPORT_SHA1=y +CONFIG_SOC_SHA_SUPPORT_SHA256=y +CONFIG_SOC_SHA_SUPPORT_SHA384=y +CONFIG_SOC_SHA_SUPPORT_SHA512=y +CONFIG_SOC_MPI_MEM_BLOCKS_NUM=4 +CONFIG_SOC_MPI_OPERATIONS_NUM=1 +CONFIG_SOC_RSA_MAX_BIT_LEN=4096 +CONFIG_SOC_AES_SUPPORT_AES_128=y +CONFIG_SOC_AES_SUPPORT_AES_192=y +CONFIG_SOC_AES_SUPPORT_AES_256=y +CONFIG_SOC_SECURE_BOOT_V1=y +CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS=1 +CONFIG_SOC_FLASH_ENCRYPTED_XTS_AES_BLOCK_MAX=32 +CONFIG_SOC_PHY_DIG_REGS_MEM_SIZE=21 +CONFIG_SOC_PM_SUPPORT_EXT0_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_EXT1_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_EXT_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_RTC_PERIPH_PD=y +CONFIG_SOC_PM_SUPPORT_RTC_FAST_MEM_PD=y +CONFIG_SOC_PM_SUPPORT_RTC_SLOW_MEM_PD=y +CONFIG_SOC_PM_SUPPORT_RC_FAST_PD=y +CONFIG_SOC_PM_SUPPORT_VDDSDIO_PD=y +CONFIG_SOC_PM_SUPPORT_MODEM_PD=y +CONFIG_SOC_CONFIGURABLE_VDDSDIO_SUPPORTED=y +CONFIG_SOC_PM_MODEM_PD_BY_SW=y +CONFIG_SOC_CLK_APLL_SUPPORTED=y +CONFIG_SOC_CLK_RC_FAST_D256_SUPPORTED=y +CONFIG_SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256=y +CONFIG_SOC_CLK_RC_FAST_SUPPORT_CALIBRATION=y +CONFIG_SOC_CLK_XTAL32K_SUPPORTED=y +CONFIG_SOC_CLK_LP_FAST_SUPPORT_XTAL_D4=y +CONFIG_SOC_SDMMC_USE_IOMUX=y +CONFIG_SOC_SDMMC_NUM_SLOTS=2 +CONFIG_SOC_WIFI_WAPI_SUPPORT=y +CONFIG_SOC_WIFI_CSI_SUPPORT=y +CONFIG_SOC_WIFI_MESH_SUPPORT=y +CONFIG_SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW=y +CONFIG_SOC_WIFI_NAN_SUPPORT=y +CONFIG_SOC_BLE_SUPPORTED=y +CONFIG_SOC_BLE_MESH_SUPPORTED=y +CONFIG_SOC_BT_CLASSIC_SUPPORTED=y +CONFIG_SOC_BLUFI_SUPPORTED=y +CONFIG_SOC_BT_H2C_ENC_KEY_CTRL_ENH_VSC_SUPPORTED=y +CONFIG_SOC_BLE_MULTI_CONN_OPTIMIZATION=y +CONFIG_SOC_ULP_HAS_ADC=y +CONFIG_SOC_PHY_COMBO_MODULE=y +CONFIG_SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK=y +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TOOLCHAIN="gcc" +CONFIG_IDF_TOOLCHAIN_GCC=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET_ARCH="xtensa" +CONFIG_IDF_TARGET="esp32" +CONFIG_IDF_INIT_VERSION="5.5.1" +CONFIG_IDF_TARGET_ESP32=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 + +# +# Build type +# +CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y +# CONFIG_APP_BUILD_TYPE_RAM is not set +CONFIG_APP_BUILD_GENERATE_BINARIES=y +CONFIG_APP_BUILD_BOOTLOADER=y +CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y +# CONFIG_APP_REPRODUCIBLE_BUILD is not set +# CONFIG_APP_NO_BLOBS is not set +# CONFIG_APP_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set +# CONFIG_APP_COMPATIBLE_PRE_V3_1_BOOTLOADERS is not set +# end of Build type + +# +# Bootloader config +# + +# +# Bootloader manager +# +CONFIG_BOOTLOADER_COMPILE_TIME_DATE=y +CONFIG_BOOTLOADER_PROJECT_VER=1 +# end of Bootloader manager + +# +# Application Rollback +# +# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set +# end of Application Rollback + +# +# Recovery Bootloader and Rollback +# +# end of Recovery Bootloader and Rollback + +CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x1000 +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set + +# +# Log +# +CONFIG_BOOTLOADER_LOG_VERSION_1=y +CONFIG_BOOTLOADER_LOG_VERSION=1 +# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set +CONFIG_BOOTLOADER_LOG_LEVEL=3 + +# +# Format +# +# CONFIG_BOOTLOADER_LOG_COLORS is not set +CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y +# end of Format + +# +# Settings +# +CONFIG_BOOTLOADER_LOG_MODE_TEXT_EN=y +CONFIG_BOOTLOADER_LOG_MODE_TEXT=y +# end of Settings +# end of Log + +# +# Serial Flash Configurations +# +# CONFIG_BOOTLOADER_FLASH_DC_AWARE is not set +CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=y +# end of Serial Flash Configurations + +# CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_8V is not set +CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y +# CONFIG_BOOTLOADER_FACTORY_RESET is not set +# CONFIG_BOOTLOADER_APP_TEST is not set +CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y +CONFIG_BOOTLOADER_WDT_ENABLE=y +# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set +CONFIG_BOOTLOADER_WDT_TIME_MS=9000 +# CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set +CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0 +# CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set +# end of Bootloader config + +# +# Security features +# +CONFIG_SECURE_BOOT_V1_SUPPORTED=y +# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set +# CONFIG_SECURE_BOOT is not set +# CONFIG_SECURE_FLASH_ENC_ENABLED is not set +# end of Security features + +# +# Application manager +# +CONFIG_APP_COMPILE_TIME_DATE=y +# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set +# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set +# CONFIG_APP_PROJECT_VER_FROM_CONFIG is not set +CONFIG_APP_RETRIEVE_LEN_ELF_SHA=9 +# end of Application manager + +CONFIG_ESP_ROM_HAS_CRC_LE=y +CONFIG_ESP_ROM_HAS_CRC_BE=y +CONFIG_ESP_ROM_HAS_MZ_CRC32=y +CONFIG_ESP_ROM_HAS_JPEG_DECODE=y +CONFIG_ESP_ROM_HAS_UART_BUF_SWITCH=y +CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y +CONFIG_ESP_ROM_HAS_NEWLIB=y +CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y +CONFIG_ESP_ROM_HAS_NEWLIB_32BIT_TIME=y +CONFIG_ESP_ROM_HAS_SW_FLOAT=y +CONFIG_ESP_ROM_USB_OTG_NUM=-1 +CONFIG_ESP_ROM_USB_SERIAL_DEVICE_NUM=-1 +CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y +CONFIG_ESP_ROM_HAS_OUTPUT_PUTC_FUNC=y + +# +# Serial flasher config +# +# CONFIG_ESPTOOLPY_NO_STUB is not set +# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set +# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE="dio" +# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set +CONFIG_ESPTOOLPY_FLASHFREQ_40M=y +# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set +# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set +CONFIG_ESPTOOLPY_FLASHFREQ="40m" +# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE="2MB" +# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set +CONFIG_ESPTOOLPY_BEFORE_RESET=y +# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +# CONFIG_ESPTOOLPY_AFTER_NORESET is not set +CONFIG_ESPTOOLPY_AFTER="hard_reset" +CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 +# end of Serial flasher config + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_SINGLE_APP=y +# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set +# CONFIG_PARTITION_TABLE_TWO_OTA is not set +# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set +# CONFIG_PARTITION_TABLE_CUSTOM is not set +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# Compiler options +# +CONFIG_COMPILER_OPTIMIZATION_DEBUG=y +# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set +# CONFIG_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_COMPILER_OPTIMIZATION_NONE is not set +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set +CONFIG_COMPILER_ASSERT_NDEBUG_EVALUATE=y +CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set +CONFIG_COMPILER_HIDE_PATHS_MACROS=y +# CONFIG_COMPILER_CXX_EXCEPTIONS is not set +# CONFIG_COMPILER_CXX_RTTI is not set +CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y +# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set +# CONFIG_COMPILER_NO_MERGE_CONSTANTS is not set +# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set +CONFIG_COMPILER_DISABLE_DEFAULT_ERRORS=y +# CONFIG_COMPILER_DISABLE_GCC12_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC13_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC14_WARNINGS is not set +# CONFIG_COMPILER_DUMP_RTL_FILES is not set +CONFIG_COMPILER_RT_LIB_GCCLIB=y +CONFIG_COMPILER_RT_LIB_NAME="gcc" +CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=y +# CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE is not set +# CONFIG_COMPILER_STATIC_ANALYZER is not set +# end of Compiler options + +# +# Component config +# + +# +# !!! MINIMAL_BUILD is enabled !!! +# + +# +# Only common components and those transitively required by the main component are listed +# + +# +# If a component configuration is missing, please add it to the main component's requirements +# + +# +# eFuse Bit Manager +# +# CONFIG_EFUSE_CUSTOM_TABLE is not set +# CONFIG_EFUSE_VIRTUAL is not set +# CONFIG_EFUSE_CODE_SCHEME_COMPAT_NONE is not set +CONFIG_EFUSE_CODE_SCHEME_COMPAT_3_4=y +# CONFIG_EFUSE_CODE_SCHEME_COMPAT_REPEAT is not set +CONFIG_EFUSE_MAX_BLK_LEN=192 +# end of eFuse Bit Manager + +# +# Common ESP-related +# +CONFIG_ESP_ERR_TO_NAME_LOOKUP=y +# end of Common ESP-related + +# +# ESP-Driver:GPIO Configurations +# +# CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL is not set +# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:GPIO Configurations + +# +# Hardware Settings +# + +# +# Chip revision +# +CONFIG_ESP32_REV_MIN_0=y +# CONFIG_ESP32_REV_MIN_1 is not set +# CONFIG_ESP32_REV_MIN_1_1 is not set +# CONFIG_ESP32_REV_MIN_2 is not set +# CONFIG_ESP32_REV_MIN_3 is not set +# CONFIG_ESP32_REV_MIN_3_1 is not set +CONFIG_ESP32_REV_MIN=0 +CONFIG_ESP32_REV_MIN_FULL=0 +CONFIG_ESP_REV_MIN_FULL=0 + +# +# Maximum Supported ESP32 Revision (Rev v3.99) +# +CONFIG_ESP32_REV_MAX_FULL=399 +CONFIG_ESP_REV_MAX_FULL=399 +CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL=0 +CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL=99 + +# +# Maximum Supported ESP32 eFuse Block Revision (eFuse Block Rev v0.99) +# +# end of Chip revision + +# +# 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 is not set +# CONFIG_ESP_MAC_USE_CUSTOM_MAC_AS_BASE_MAC is not set +# end of MAC Config + +# +# Sleep Config +# +# CONFIG_ESP_SLEEP_POWER_DOWN_FLASH is not set +CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND=y +# CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU is not set +CONFIG_ESP_SLEEP_RTC_BUS_ISO_WORKAROUND=y +# CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND is not set +CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY=2000 +# CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION is not set +# CONFIG_ESP_SLEEP_DEBUG is not set +CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS=y +# end of Sleep Config + +# +# RTC Clock Config +# +CONFIG_RTC_CLK_SRC_INT_RC=y +# CONFIG_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_RTC_CLK_SRC_INT_8MD256 is not set +CONFIG_RTC_CLK_CAL_CYCLES=1024 +# end of RTC Clock Config + +# +# Peripheral Control +# +CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=y +CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=y +# end of Peripheral Control + +# +# Main XTAL Config +# +# CONFIG_XTAL_FREQ_26 is not set +# CONFIG_XTAL_FREQ_32 is not set +CONFIG_XTAL_FREQ_40=y +# CONFIG_XTAL_FREQ_AUTO is not set +CONFIG_XTAL_FREQ=40 +# end of Main XTAL Config + +# +# Power Supplier +# + +# +# Brownout Detector +# +CONFIG_ESP_BROWNOUT_DET=y +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_0=y +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7 is not set +CONFIG_ESP_BROWNOUT_DET_LVL=0 +CONFIG_ESP_BROWNOUT_USE_INTR=y +# end of Brownout Detector +# end of Power Supplier + +CONFIG_ESP_INTR_IN_IRAM=y +# end of Hardware Settings + +# +# ESP-MM: Memory Management Configurations +# +# end of ESP-MM: Memory Management Configurations + +# +# Partition API Configuration +# +# end of Partition API Configuration + +# +# Power Management +# +CONFIG_PM_SLEEP_FUNC_IN_IRAM=y +# CONFIG_PM_ENABLE is not set +CONFIG_PM_SLP_IRAM_OPT=y +# end of Power Management + +# +# ESP-ROM +# +CONFIG_ESP_ROM_PRINT_IN_IRAM=y +# end of ESP-ROM + +# +# ESP Security Specific +# +# end of ESP Security Specific + +# +# ESP System Settings +# +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160 + +# +# Memory +# +# CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE is not set + +# +# Non-backward compatible options +# +# CONFIG_ESP_SYSTEM_ESP32_SRAM1_REGION_AS_IRAM is not set +# end of Non-backward compatible options +# end of Memory + +# +# Trace memory +# +# CONFIG_ESP32_TRAX is not set +CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x0 +# end of Trace memory + +CONFIG_ESP_SYSTEM_IN_IRAM=y +# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set +CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y +# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set +CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS=0 + +# +# Memory protection +# +# end of Memory protection + +CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584 +CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y +# CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set +# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set +CONFIG_ESP_MAIN_TASK_AFFINITY=0x0 +CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 +CONFIG_ESP_CONSOLE_UART_DEFAULT=y +# CONFIG_ESP_CONSOLE_UART_CUSTOM is not set +# CONFIG_ESP_CONSOLE_NONE is not set +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=0 +CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 +CONFIG_ESP_INT_WDT=y +CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 +CONFIG_ESP_INT_WDT_CHECK_CPU1=y +CONFIG_ESP_TASK_WDT_EN=y +CONFIG_ESP_TASK_WDT_INIT=y +# CONFIG_ESP_TASK_WDT_PANIC is not set +CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP_PANIC_HANDLER_IRAM is not set +# CONFIG_ESP_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP_DEBUG_OCDAWARE=y +# CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 is not set +CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4=y +# CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE is not set +# end of ESP System Settings + +# +# IPC (Inter-Processor Call) +# +CONFIG_ESP_IPC_ENABLE=y +CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 +CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y +CONFIG_ESP_IPC_ISR_ENABLE=y +# end of IPC (Inter-Processor Call) + +# +# ESP Timer (High Resolution Timer) +# +CONFIG_ESP_TIMER_IN_IRAM=y +# CONFIG_ESP_TIMER_PROFILING is not set +CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y +CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y +CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 +CONFIG_ESP_TIMER_INTERRUPT_LEVEL=1 +# CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL is not set +CONFIG_ESP_TIMER_TASK_AFFINITY=0x0 +CONFIG_ESP_TIMER_TASK_AFFINITY_CPU0=y +CONFIG_ESP_TIMER_ISR_AFFINITY_CPU0=y +# CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD is not set +CONFIG_ESP_TIMER_IMPL_TG0_LAC=y +# end of ESP Timer (High Resolution Timer) + +# +# FreeRTOS +# + +# +# Kernel +# +# CONFIG_FREERTOS_SMP is not set +# CONFIG_FREERTOS_UNICORE is not set +CONFIG_FREERTOS_HZ=100 +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y +CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 +# CONFIG_FREERTOS_USE_IDLE_HOOK is not set +# CONFIG_FREERTOS_USE_TICK_HOOK is not set +CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 +# CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY is not set +CONFIG_FREERTOS_USE_TIMERS=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_NAME="Tmr Svc" +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0 is not set +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU1 is not set +CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_CORE_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 +CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 +CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 +# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set +# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set +# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set +# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set +# end of Kernel + +# +# Port +# +CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y +# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set +CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y +# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set +# CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set +CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y +CONFIG_FREERTOS_ISR_STACKSIZE=1536 +CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y +# CONFIG_FREERTOS_FPU_IN_ISR is not set +CONFIG_FREERTOS_TICK_SUPPORT_CORETIMER=y +CONFIG_FREERTOS_CORETIMER_0=y +# CONFIG_FREERTOS_CORETIMER_1 is not set +CONFIG_FREERTOS_SYSTICK_USES_CCOUNT=y +# CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set +# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set +# end of Port + +# +# Extra +# +# end of Extra + +CONFIG_FREERTOS_PORT=y +CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y +CONFIG_FREERTOS_DEBUG_OCDAWARE=y +CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y +CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y +CONFIG_FREERTOS_NUMBER_OF_CORES=2 +CONFIG_FREERTOS_IN_IRAM=y +# end of FreeRTOS + +# +# Hardware Abstraction Layer (HAL) and Low Level (LL) +# +CONFIG_HAL_ASSERTION_EQUALS_SYSTEM=y +# CONFIG_HAL_ASSERTION_DISABLE is not set +# CONFIG_HAL_ASSERTION_SILENT is not set +# CONFIG_HAL_ASSERTION_ENABLE is not set +CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2 +# end of Hardware Abstraction Layer (HAL) and Low Level (LL) + +# +# Heap memory debugging +# +CONFIG_HEAP_POISONING_DISABLED=y +# CONFIG_HEAP_POISONING_LIGHT is not set +# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set +CONFIG_HEAP_TRACING_OFF=y +# CONFIG_HEAP_TRACING_STANDALONE is not set +# CONFIG_HEAP_TRACING_TOHOST is not set +# CONFIG_HEAP_USE_HOOKS is not set +# CONFIG_HEAP_TASK_TRACKING is not set +# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set +# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set +# end of Heap memory debugging + +# +# Log +# +CONFIG_LOG_VERSION_1=y +# CONFIG_LOG_VERSION_2 is not set +CONFIG_LOG_VERSION=1 + +# +# Log Level +# +# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set +# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set +# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set +# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y +# CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set +# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set +CONFIG_LOG_MAXIMUM_LEVEL=3 + +# +# Level Settings +# +# CONFIG_LOG_MASTER_LEVEL is not set +CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y +# CONFIG_LOG_TAG_LEVEL_IMPL_NONE is not set +# CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST is not set +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST=y +# CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY is not set +CONFIG_LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP=y +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31 +# end of Level Settings +# end of Log Level + +# +# Format +# +# CONFIG_LOG_COLORS is not set +CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y +# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set +# end of Format + +# +# Settings +# +CONFIG_LOG_MODE_TEXT_EN=y +CONFIG_LOG_MODE_TEXT=y +# end of Settings + +CONFIG_LOG_IN_IRAM=y +# end of Log + +# +# mbedTLS +# +CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y +# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set +# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set +CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +# CONFIG_MBEDTLS_DYNAMIC_BUFFER is not set +# CONFIG_MBEDTLS_DEBUG is not set + +# +# mbedTLS v3.x related +# +# CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 is not set +# CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH is not set +# CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set +# CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set +CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y +# CONFIG_MBEDTLS_SSL_KEYING_MATERIAL_EXPORT is not set +CONFIG_MBEDTLS_PKCS7_C=y +# end of mbedTLS v3.x related + +# +# Certificate Bundle +# +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set +# CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEPRECATED_LIST is not set +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS=200 +# end of Certificate Bundle + +# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set +# CONFIG_MBEDTLS_CMAC_C is not set +CONFIG_MBEDTLS_HARDWARE_AES=y +CONFIG_MBEDTLS_GCM_SUPPORT_NON_AES_CIPHER=y +CONFIG_MBEDTLS_HARDWARE_MPI=y +# CONFIG_MBEDTLS_LARGE_KEY_SOFTWARE_MPI is not set +CONFIG_MBEDTLS_HARDWARE_SHA=y +CONFIG_MBEDTLS_ROM_MD5=y +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY is not set +CONFIG_MBEDTLS_HAVE_TIME=y +# CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set +# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set +CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y +CONFIG_MBEDTLS_SHA1_C=y +CONFIG_MBEDTLS_SHA512_C=y +# CONFIG_MBEDTLS_SHA3_C is not set +CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y +# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set +# CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set +# CONFIG_MBEDTLS_TLS_DISABLED is not set +CONFIG_MBEDTLS_TLS_SERVER=y +CONFIG_MBEDTLS_TLS_CLIENT=y +CONFIG_MBEDTLS_TLS_ENABLED=y + +# +# TLS Key Exchange Methods +# +# CONFIG_MBEDTLS_PSK_MODES is not set +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y +# end of TLS Key Exchange Methods + +CONFIG_MBEDTLS_SSL_RENEGOTIATION=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +# CONFIG_MBEDTLS_SSL_PROTO_GMTSSL1_1 is not set +# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set +CONFIG_MBEDTLS_SSL_ALPN=y +CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y +CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y + +# +# Symmetric Ciphers +# +CONFIG_MBEDTLS_AES_C=y +# CONFIG_MBEDTLS_CAMELLIA_C is not set +# CONFIG_MBEDTLS_DES_C is not set +# CONFIG_MBEDTLS_BLOWFISH_C is not set +# CONFIG_MBEDTLS_XTEA_C is not set +CONFIG_MBEDTLS_CCM_C=y +CONFIG_MBEDTLS_GCM_C=y +# CONFIG_MBEDTLS_NIST_KW_C is not set +# end of Symmetric Ciphers + +# CONFIG_MBEDTLS_RIPEMD160_C is not set + +# +# Certificates +# +CONFIG_MBEDTLS_PEM_PARSE_C=y +CONFIG_MBEDTLS_PEM_WRITE_C=y +CONFIG_MBEDTLS_X509_CRL_PARSE_C=y +CONFIG_MBEDTLS_X509_CSR_PARSE_C=y +# end of Certificates + +CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_PK_PARSE_EC_EXTENDED=y +CONFIG_MBEDTLS_PK_PARSE_EC_COMPRESSED=y +# CONFIG_MBEDTLS_DHM_C is not set +CONFIG_MBEDTLS_ECDH_C=y +CONFIG_MBEDTLS_ECDSA_C=y +# CONFIG_MBEDTLS_ECJPAKE_C is not set +CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y +CONFIG_MBEDTLS_ECP_NIST_OPTIM=y +# CONFIG_MBEDTLS_ECP_FIXED_POINT_OPTIM is not set +# CONFIG_MBEDTLS_POLY1305_C is not set +# CONFIG_MBEDTLS_CHACHA20_C is not set +# CONFIG_MBEDTLS_HKDF_C is not set +# CONFIG_MBEDTLS_THREADING_C is not set +CONFIG_MBEDTLS_ERROR_STRINGS=y +# CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION is not set +# end of mbedTLS + +# +# LibC +# +CONFIG_LIBC_NEWLIB=y +CONFIG_LIBC_MISC_IN_IRAM=y +CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=y +# CONFIG_LIBC_NEWLIB_NANO_FORMAT is not set +CONFIG_LIBC_TIME_SYSCALL_USE_RTC_HRT=y +# CONFIG_LIBC_TIME_SYSCALL_USE_RTC is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_HRT is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_NONE is not set +# end of LibC + +# +# PThreads +# +CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_PTHREAD_STACK_MIN=768 +CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY=y +# CONFIG_PTHREAD_DEFAULT_CORE_0 is not set +# CONFIG_PTHREAD_DEFAULT_CORE_1 is not set +CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" +# end of PThreads + +# +# MMU Config +# +CONFIG_MMU_PAGE_SIZE_64KB=y +CONFIG_MMU_PAGE_MODE="64KB" +CONFIG_MMU_PAGE_SIZE=0x10000 +# end of MMU Config + +# +# Main Flash configuration +# + +# +# SPI Flash behavior when brownout +# +CONFIG_SPI_FLASH_BROWNOUT_RESET_XMC=y +CONFIG_SPI_FLASH_BROWNOUT_RESET=y +# end of SPI Flash behavior when brownout + +# +# Optional and Experimental Features (READ DOCS FIRST) +# + +# +# Features here require specific hardware (READ DOCS FIRST!) +# +CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50 +# CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set +# CONFIG_SPI_FLASH_FORCE_ENABLE_C6_H2_SUSPEND is not set +CONFIG_SPI_FLASH_PLACE_FUNCTIONS_IN_IRAM=y +# end of Optional and Experimental Features (READ DOCS FIRST) +# end of Main Flash configuration + +# +# SPI Flash driver +# +# CONFIG_SPI_FLASH_VERIFY_WRITE is not set +# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set +# CONFIG_SPI_FLASH_SHARE_SPI1_BUS is not set +# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set +CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y +CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 +CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 +CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192 +# CONFIG_SPI_FLASH_SIZE_OVERRIDE is not set +# CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED is not set +# CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST is not set + +# +# Auto-detect flash chips +# +CONFIG_SPI_FLASH_VENDOR_XMC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_GD_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_ISSI_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_MXIC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_WINBOND_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP=y +# CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP is not set +# CONFIG_SPI_FLASH_SUPPORT_TH_CHIP is not set +# end of Auto-detect flash chips + +CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=y +# end of SPI Flash driver +# end of Component config + +# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set + +# Deprecated options for backward compatibility +# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set +# CONFIG_NO_BLOBS is not set +# CONFIG_ESP32_NO_BLOBS is not set +# CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set +# CONFIG_ESP32_COMPATIBLE_PRE_V3_1_BOOTLOADERS is not set +# CONFIG_APP_ROLLBACK_ENABLE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set +CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y +# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set +CONFIG_LOG_BOOTLOADER_LEVEL=3 +# CONFIG_FLASH_ENCRYPTION_ENABLED is not set +# CONFIG_FLASHMODE_QIO is not set +# CONFIG_FLASHMODE_QOUT is not set +CONFIG_FLASHMODE_DIO=y +# CONFIG_FLASHMODE_DOUT is not set +CONFIG_MONITOR_BAUD=115200 +CONFIG_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y +# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set +# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set +CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y +# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set +CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_CXX_EXCEPTIONS is not set +CONFIG_STACK_CHECK_NONE=y +# CONFIG_STACK_CHECK_NORM is not set +# CONFIG_STACK_CHECK_STRONG is not set +# CONFIG_STACK_CHECK_ALL is not set +# CONFIG_WARN_WRITE_STRINGS is not set +# CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set +CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y +CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 +# CONFIG_ESP_SYSTEM_PD_FLASH is not set +CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y +CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y +# CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL is not set +# CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC is not set +# CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256 is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set +CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y +# CONFIG_ESP32_XTAL_FREQ_26 is not set +CONFIG_ESP32_XTAL_FREQ_40=y +# CONFIG_ESP32_XTAL_FREQ_AUTO is not set +CONFIG_ESP32_XTAL_FREQ=40 +CONFIG_BROWNOUT_DET=y +CONFIG_ESP32_BROWNOUT_DET=y +CONFIG_BROWNOUT_DET_LVL_SEL_0=y +CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_0=y +# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set +# CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7 is not set +CONFIG_BROWNOUT_DET_LVL=0 +CONFIG_ESP32_BROWNOUT_DET_LVL=0 +CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y +# CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set +CONFIG_ESP32_DEFAULT_CPU_FREQ_160=y +# CONFIG_ESP32_DEFAULT_CPU_FREQ_240 is not set +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=160 +CONFIG_TRACEMEM_RESERVE_DRAM=0x0 +# CONFIG_ESP32_PANIC_PRINT_HALT is not set +CONFIG_ESP32_PANIC_PRINT_REBOOT=y +# CONFIG_ESP32_PANIC_SILENT_REBOOT is not set +CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_MAIN_TASK_STACK_SIZE=3584 +CONFIG_CONSOLE_UART_DEFAULT=y +# CONFIG_CONSOLE_UART_CUSTOM is not set +# CONFIG_CONSOLE_UART_NONE is not set +# CONFIG_ESP_CONSOLE_UART_NONE is not set +CONFIG_CONSOLE_UART=y +CONFIG_CONSOLE_UART_NUM=0 +CONFIG_CONSOLE_UART_BAUDRATE=115200 +CONFIG_INT_WDT=y +CONFIG_INT_WDT_TIMEOUT_MS=300 +CONFIG_INT_WDT_CHECK_CPU1=y +CONFIG_TASK_WDT=y +CONFIG_ESP_TASK_WDT=y +# CONFIG_TASK_WDT_PANIC is not set +CONFIG_TASK_WDT_TIMEOUT_S=5 +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP32_DEBUG_OCDAWARE=y +# CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set +CONFIG_IPC_TASK_STACK_SIZE=1024 +CONFIG_TIMER_TASK_STACK_SIZE=3584 +CONFIG_TIMER_TASK_PRIORITY=1 +CONFIG_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_TIMER_QUEUE_LENGTH=10 +# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set +# CONFIG_HAL_ASSERTION_SILIENT is not set +# CONFIG_NEWLIB_NANO_FORMAT is not set +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y +CONFIG_ESP32_TIME_SYSCALL_USE_RTC_HRT=y +CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y +# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_HRT is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set +# CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set +CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_ESP32_PTHREAD_STACK_MIN=768 +CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set +CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set +# End of deprecated options diff --git a/sdkconfig.ci b/sdkconfig.ci new file mode 100755 index 0000000..e69de29