From 25ebe440e277123e7a9235e8bd240f9988e0e50a Mon Sep 17 00:00:00 2001 From: Thomas Jentzsch <76921504+yellobyte@users.noreply.github.com> Date: Mon, 16 Dec 2024 23:36:33 +0100 Subject: [PATCH 01/60] add board yb_esp32s3_eth --- boards.txt | 219 +++++++++++++++++++++++++ variants/yb_esp32s3_eth/pins_arduino.h | 63 +++++++ 2 files changed, 282 insertions(+) create mode 100644 variants/yb_esp32s3_eth/pins_arduino.h diff --git a/boards.txt b/boards.txt index 791396da338..a740bde11d8 100644 --- a/boards.txt +++ b/boards.txt @@ -47922,3 +47922,222 @@ yb_esp32s3_amp_v3.menu.EraseFlash.all=Enabled yb_esp32s3_amp_v3.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## + +yb_esp32s3_eth.name=YelloByte YB-ESP32-S3-ETH + +yb_esp32s3_eth.bootloader.tool=esptool_py +yb_esp32s3_eth.bootloader.tool.default=esptool_py + +yb_esp32s3_eth.upload.tool=esptool_py +yb_esp32s3_eth.upload.tool.default=esptool_py +yb_esp32s3_eth.upload.tool.network=esp_ota + +yb_esp32s3_eth.upload.maximum_size=1310720 +yb_esp32s3_eth.upload.maximum_data_size=327680 +yb_esp32s3_eth.upload.flags= +yb_esp32s3_eth.upload.extra_flags= +yb_esp32s3_eth.upload.use_1200bps_touch=false +yb_esp32s3_eth.upload.wait_for_upload_port=false + +yb_esp32s3_eth.serial.disableDTR=false +yb_esp32s3_eth.serial.disableRTS=false + +yb_esp32s3_eth.build.tarch=xtensa +yb_esp32s3_eth.build.bootloader_addr=0x0 +yb_esp32s3_eth.build.target=esp32s3 +yb_esp32s3_eth.build.mcu=esp32s3 +yb_esp32s3_eth.build.core=esp32 +yb_esp32s3_eth.build.variant=yb_esp32s3_eth +yb_esp32s3_eth.build.board=YB_ESP32S3_ETH + +yb_esp32s3_eth.build.usb_mode=1 +yb_esp32s3_eth.build.cdc_on_boot=0 +yb_esp32s3_eth.build.msc_on_boot=0 +yb_esp32s3_eth.build.dfu_on_boot=0 +yb_esp32s3_eth.build.f_cpu=240000000L +yb_esp32s3_eth.build.flash_size=4MB +yb_esp32s3_eth.build.flash_freq=80m +yb_esp32s3_eth.build.flash_mode=dio +yb_esp32s3_eth.build.boot=qio +yb_esp32s3_eth.build.boot_freq=80m +yb_esp32s3_eth.build.partitions=default +yb_esp32s3_eth.build.defines= +yb_esp32s3_eth.build.loop_core= +yb_esp32s3_eth.build.event_core= +yb_esp32s3_eth.build.psram_type=qspi +yb_esp32s3_eth.build.memory_type={build.boot}_{build.psram_type} + +yb_esp32s3_eth.menu.JTAGAdapter.default=Disabled +yb_esp32s3_eth.menu.JTAGAdapter.default.build.copy_jtag_files=0 +yb_esp32s3_eth.menu.JTAGAdapter.builtin=Integrated USB JTAG +yb_esp32s3_eth.menu.JTAGAdapter.builtin.build.openocdscript=esp32s3-builtin.cfg +yb_esp32s3_eth.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +yb_esp32s3_eth.menu.JTAGAdapter.external=FTDI Adapter +yb_esp32s3_eth.menu.JTAGAdapter.external.build.openocdscript=esp32s3-ftdi.cfg +yb_esp32s3_eth.menu.JTAGAdapter.external.build.copy_jtag_files=1 +yb_esp32s3_eth.menu.JTAGAdapter.bridge=ESP USB Bridge +yb_esp32s3_eth.menu.JTAGAdapter.bridge.build.openocdscript=esp32s3-bridge.cfg +yb_esp32s3_eth.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +yb_esp32s3_eth.menu.LoopCore.1=Core 1 +yb_esp32s3_eth.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +yb_esp32s3_eth.menu.LoopCore.0=Core 0 +yb_esp32s3_eth.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +yb_esp32s3_eth.menu.EventsCore.1=Core 1 +yb_esp32s3_eth.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +yb_esp32s3_eth.menu.EventsCore.0=Core 0 +yb_esp32s3_eth.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +yb_esp32s3_eth.menu.USBMode.hwcdc=Hardware CDC and JTAG +yb_esp32s3_eth.menu.USBMode.hwcdc.build.usb_mode=1 +yb_esp32s3_eth.menu.USBMode.default=USB-OTG (TinyUSB) +yb_esp32s3_eth.menu.USBMode.default.build.usb_mode=0 + +yb_esp32s3_eth.menu.CDCOnBoot.default=Disabled +yb_esp32s3_eth.menu.CDCOnBoot.default.build.cdc_on_boot=0 +yb_esp32s3_eth.menu.CDCOnBoot.cdc=Enabled +yb_esp32s3_eth.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 + +yb_esp32s3_eth.menu.MSCOnBoot.default=Disabled +yb_esp32s3_eth.menu.MSCOnBoot.default.build.msc_on_boot=0 +yb_esp32s3_eth.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +yb_esp32s3_eth.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +yb_esp32s3_eth.menu.DFUOnBoot.default=Disabled +yb_esp32s3_eth.menu.DFUOnBoot.default.build.dfu_on_boot=0 +yb_esp32s3_eth.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +yb_esp32s3_eth.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +yb_esp32s3_eth.menu.UploadMode.default=UART0 / Hardware CDC +yb_esp32s3_eth.menu.UploadMode.default.upload.use_1200bps_touch=false +yb_esp32s3_eth.menu.UploadMode.default.upload.wait_for_upload_port=false +yb_esp32s3_eth.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +yb_esp32s3_eth.menu.UploadMode.cdc.upload.use_1200bps_touch=true +yb_esp32s3_eth.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +yb_esp32s3_eth.menu.PSRAM.disabled=Disabled +yb_esp32s3_eth.menu.PSRAM.disabled.build.defines= +yb_esp32s3_eth.menu.PSRAM.disabled.build.psram_type=qspi +yb_esp32s3_eth.menu.PSRAM.enabled=QSPI PSRAM +yb_esp32s3_eth.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +yb_esp32s3_eth.menu.PSRAM.enabled.build.psram_type=qspi +yb_esp32s3_eth.menu.PSRAM.opi=OPI PSRAM +yb_esp32s3_eth.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM +yb_esp32s3_eth.menu.PSRAM.opi.build.psram_type=opi + +yb_esp32s3_eth.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +yb_esp32s3_eth.menu.PartitionScheme.default.build.partitions=default +yb_esp32s3_eth.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) +yb_esp32s3_eth.menu.PartitionScheme.default_8MB.build.partitions=default_8MB +yb_esp32s3_eth.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 +yb_esp32s3_eth.menu.PartitionScheme.default_16MB=16M with spiffs (6.25MB APP/3.43MB SPIFFS) +yb_esp32s3_eth.menu.PartitionScheme.default_16MB.build.partitions=default_16MB +yb_esp32s3_eth.menu.PartitionScheme.default_16MB.upload.maximum_size=6553600 +yb_esp32s3_eth.menu.PartitionScheme.max_app_8MB=Maximum APP (7.9MB APP No OTA/No FS) +yb_esp32s3_eth.menu.PartitionScheme.max_app_8MB.build.partitions=max_app_8MB +yb_esp32s3_eth.menu.PartitionScheme.max_app_8MB.upload.maximum_size=8257536 +yb_esp32s3_eth.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) +yb_esp32s3_eth.menu.PartitionScheme.fatflash.build.partitions=ffat +yb_esp32s3_eth.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 +yb_esp32s3_eth.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +yb_esp32s3_eth.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +yb_esp32s3_eth.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +yb_esp32s3_eth.menu.PartitionScheme.minimal.build.partitions=minimal +yb_esp32s3_eth.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +yb_esp32s3_eth.menu.PartitionScheme.no_ota.build.partitions=no_ota +yb_esp32s3_eth.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +yb_esp32s3_eth.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +yb_esp32s3_eth.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +yb_esp32s3_eth.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +yb_esp32s3_eth.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +yb_esp32s3_eth.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +yb_esp32s3_eth.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +yb_esp32s3_eth.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +yb_esp32s3_eth.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +yb_esp32s3_eth.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +yb_esp32s3_eth.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +yb_esp32s3_eth.menu.PartitionScheme.huge_app.build.partitions=huge_app +yb_esp32s3_eth.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +yb_esp32s3_eth.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +yb_esp32s3_eth.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +yb_esp32s3_eth.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +yb_esp32s3_eth.menu.PartitionScheme.custom=Custom +yb_esp32s3_eth.menu.PartitionScheme.custom.build.partitions= +yb_esp32s3_eth.menu.PartitionScheme.custom.upload.maximum_size=16777216 + +yb_esp32s3_eth.menu.CPUFreq.240=240MHz (WiFi) +yb_esp32s3_eth.menu.CPUFreq.240.build.f_cpu=240000000L +yb_esp32s3_eth.menu.CPUFreq.160=160MHz (WiFi) +yb_esp32s3_eth.menu.CPUFreq.160.build.f_cpu=160000000L +yb_esp32s3_eth.menu.CPUFreq.80=80MHz (WiFi) +yb_esp32s3_eth.menu.CPUFreq.80.build.f_cpu=80000000L +yb_esp32s3_eth.menu.CPUFreq.40=40MHz +yb_esp32s3_eth.menu.CPUFreq.40.build.f_cpu=40000000L +yb_esp32s3_eth.menu.CPUFreq.20=20MHz +yb_esp32s3_eth.menu.CPUFreq.20.build.f_cpu=20000000L +yb_esp32s3_eth.menu.CPUFreq.10=10MHz +yb_esp32s3_eth.menu.CPUFreq.10.build.f_cpu=10000000L + +yb_esp32s3_eth.menu.FlashMode.qio=QIO 80MHz +yb_esp32s3_eth.menu.FlashMode.qio.build.flash_mode=dio +yb_esp32s3_eth.menu.FlashMode.qio.build.boot=qio +yb_esp32s3_eth.menu.FlashMode.qio.build.boot_freq=80m +yb_esp32s3_eth.menu.FlashMode.qio.build.flash_freq=80m +yb_esp32s3_eth.menu.FlashMode.qio120=QIO 120MHz +yb_esp32s3_eth.menu.FlashMode.qio120.build.flash_mode=dio +yb_esp32s3_eth.menu.FlashMode.qio120.build.boot=qio +yb_esp32s3_eth.menu.FlashMode.qio120.build.boot_freq=120m +yb_esp32s3_eth.menu.FlashMode.qio120.build.flash_freq=80m +yb_esp32s3_eth.menu.FlashMode.dio=DIO 80MHz +yb_esp32s3_eth.menu.FlashMode.dio.build.flash_mode=dio +yb_esp32s3_eth.menu.FlashMode.dio.build.boot=dio +yb_esp32s3_eth.menu.FlashMode.dio.build.boot_freq=80m +yb_esp32s3_eth.menu.FlashMode.dio.build.flash_freq=80m +yb_esp32s3_eth.menu.FlashMode.opi=OPI 80MHz +yb_esp32s3_eth.menu.FlashMode.opi.build.flash_mode=dout +yb_esp32s3_eth.menu.FlashMode.opi.build.boot=opi +yb_esp32s3_eth.menu.FlashMode.opi.build.boot_freq=80m +yb_esp32s3_eth.menu.FlashMode.opi.build.flash_freq=80m + +yb_esp32s3_eth.menu.FlashSize.4M=4MB (32Mb) +yb_esp32s3_eth.menu.FlashSize.4M.build.flash_size=4MB +yb_esp32s3_eth.menu.FlashSize.8M=8MB (64Mb) +yb_esp32s3_eth.menu.FlashSize.8M.build.flash_size=8MB +yb_esp32s3_eth.menu.FlashSize.16M=16MB (128Mb) +yb_esp32s3_eth.menu.FlashSize.16M.build.flash_size=16MB + +yb_esp32s3_eth.menu.UploadSpeed.921600=921600 +yb_esp32s3_eth.menu.UploadSpeed.921600.upload.speed=921600 +yb_esp32s3_eth.menu.UploadSpeed.115200=115200 +yb_esp32s3_eth.menu.UploadSpeed.115200.upload.speed=115200 +yb_esp32s3_eth.menu.UploadSpeed.256000.windows=256000 +yb_esp32s3_eth.menu.UploadSpeed.256000.upload.speed=256000 +yb_esp32s3_eth.menu.UploadSpeed.230400.windows.upload.speed=256000 +yb_esp32s3_eth.menu.UploadSpeed.230400=230400 +yb_esp32s3_eth.menu.UploadSpeed.230400.upload.speed=230400 +yb_esp32s3_eth.menu.UploadSpeed.460800.linux=460800 +yb_esp32s3_eth.menu.UploadSpeed.460800.macosx=460800 +yb_esp32s3_eth.menu.UploadSpeed.460800.upload.speed=460800 +yb_esp32s3_eth.menu.UploadSpeed.512000.windows=512000 +yb_esp32s3_eth.menu.UploadSpeed.512000.upload.speed=512000 + +yb_esp32s3_eth.menu.DebugLevel.none=None +yb_esp32s3_eth.menu.DebugLevel.none.build.code_debug=0 +yb_esp32s3_eth.menu.DebugLevel.error=Error +yb_esp32s3_eth.menu.DebugLevel.error.build.code_debug=1 +yb_esp32s3_eth.menu.DebugLevel.warn=Warn +yb_esp32s3_eth.menu.DebugLevel.warn.build.code_debug=2 +yb_esp32s3_eth.menu.DebugLevel.info=Info +yb_esp32s3_eth.menu.DebugLevel.info.build.code_debug=3 +yb_esp32s3_eth.menu.DebugLevel.debug=Debug +yb_esp32s3_eth.menu.DebugLevel.debug.build.code_debug=4 +yb_esp32s3_eth.menu.DebugLevel.verbose=Verbose +yb_esp32s3_eth.menu.DebugLevel.verbose.build.code_debug=5 + +yb_esp32s3_eth.menu.EraseFlash.none=Disabled +yb_esp32s3_eth.menu.EraseFlash.none.upload.erase_cmd= +yb_esp32s3_eth.menu.EraseFlash.all=Enabled +yb_esp32s3_eth.menu.EraseFlash.all.upload.erase_cmd=-e + +############################################################## diff --git a/variants/yb_esp32s3_eth/pins_arduino.h b/variants/yb_esp32s3_eth/pins_arduino.h new file mode 100644 index 00000000000..d7ac750489a --- /dev/null +++ b/variants/yb_esp32s3_eth/pins_arduino.h @@ -0,0 +1,63 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x303A +#define USB_PID 0x1001 + +static const uint8_t LED_BUILTIN = 47; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t SDA = 8; +static const uint8_t SCL = 9; + +static const uint8_t SS = 10; +static const uint8_t MOSI = 11; +static const uint8_t MISO = 13; +static const uint8_t SCK = 12; + +// Definitions for onboard WIZnet W5500 ethernet controller chip +static const uint8_t W5500_SS = 14; // W5500 chip select +static const uint8_t W5500_INT = 18; // available only if solder bridge "INT" is closed (default open) +static const uint8_t W5500_RST = 21; // set GPIO21 to INPUT (high impedance) if RST signal is unused + +static const uint8_t A0 = 1; +static const uint8_t A1 = 2; +static const uint8_t A2 = 3; +static const uint8_t A3 = 4; +static const uint8_t A4 = 5; +static const uint8_t A5 = 6; +static const uint8_t A6 = 7; +static const uint8_t A7 = 8; +static const uint8_t A8 = 9; +static const uint8_t A9 = 10; +static const uint8_t A10 = 11; +static const uint8_t A11 = 12; +static const uint8_t A12 = 13; +static const uint8_t A13 = 14; +static const uint8_t A14 = 15; +static const uint8_t A15 = 16; +static const uint8_t A16 = 17; +static const uint8_t A17 = 18; + +static const uint8_t T1 = 1; +static const uint8_t T2 = 2; +static const uint8_t T3 = 3; +static const uint8_t T4 = 4; +static const uint8_t T5 = 5; +static const uint8_t T6 = 6; +static const uint8_t T7 = 7; +static const uint8_t T8 = 8; +static const uint8_t T9 = 9; +static const uint8_t T10 = 10; +static const uint8_t T11 = 11; +static const uint8_t T12 = 12; +static const uint8_t T13 = 13; +static const uint8_t T14 = 14; + +#endif /* Pins_Arduino_h */ From 4d0ae18aee4168b72d1d7848a6c264883be5344c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 06:37:40 +0000 Subject: [PATCH 02/60] ci(pre-commit): Apply automatic fixes --- variants/yb_esp32s3_eth/pins_arduino.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/yb_esp32s3_eth/pins_arduino.h b/variants/yb_esp32s3_eth/pins_arduino.h index d7ac750489a..77fcf6923fc 100644 --- a/variants/yb_esp32s3_eth/pins_arduino.h +++ b/variants/yb_esp32s3_eth/pins_arduino.h @@ -22,7 +22,7 @@ static const uint8_t MISO = 13; static const uint8_t SCK = 12; // Definitions for onboard WIZnet W5500 ethernet controller chip -static const uint8_t W5500_SS = 14; // W5500 chip select +static const uint8_t W5500_SS = 14; // W5500 chip select static const uint8_t W5500_INT = 18; // available only if solder bridge "INT" is closed (default open) static const uint8_t W5500_RST = 21; // set GPIO21 to INPUT (high impedance) if RST signal is unused From 2fc679151c34f301fe42f3b87b0283c2e38e2ad1 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:57:22 -0300 Subject: [PATCH 03/60] ci(debug): Compile once a week with verbose log level --- .github/scripts/on-push.sh | 24 +++++++++++++----------- .github/scripts/sketch_utils.sh | 5 +++++ .github/workflows/push.yml | 30 ++++++++++++++++++++++++++++-- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/.github/scripts/on-push.sh b/.github/scripts/on-push.sh index aafc9de0596..6095f88e727 100755 --- a/.github/scripts/on-push.sh +++ b/.github/scripts/on-push.sh @@ -9,8 +9,9 @@ function build { local chunk_index=$2 local chunks_cnt=$3 local build_log=$4 - local sketches_file=$5 - shift 5 + local log_level=${5:-none} + local sketches_file=$6 + shift 6 local sketches=("$@") local BUILD_SKETCH="${SCRIPTS_DIR}/sketch_utils.sh build" @@ -19,7 +20,7 @@ function build { local args=("-ai" "$ARDUINO_IDE_PATH" "-au" "$ARDUINO_USR_PATH" "-t" "$target") if [ "$OS_IS_LINUX" == "1" ]; then - args+=("-p" "$ARDUINO_ESP32_PATH/libraries" "-i" "$chunk_index" "-m" "$chunks_cnt") + args+=("-p" "$ARDUINO_ESP32_PATH/libraries" "-i" "$chunk_index" "-m" "$chunks_cnt" "-d" "$log_level") if [ -n "$sketches_file" ]; then args+=("-f" "$sketches_file") fi @@ -54,7 +55,8 @@ fi CHUNK_INDEX=$1 CHUNKS_CNT=$2 BUILD_LOG=$3 -SKETCHES_FILE=$4 +LOG_LEVEL=$4 +SKETCHES_FILE=$5 if [ "$#" -lt 2 ] || [ "$CHUNKS_CNT" -le 0 ]; then CHUNK_INDEX=0 CHUNKS_CNT=1 @@ -88,13 +90,13 @@ if [ "$BUILD_LOG" -eq 1 ]; then fi #build sketches for different targets -build "esp32p4" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" -build "esp32s3" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" -build "esp32s2" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" -build "esp32c3" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" -build "esp32c6" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" -build "esp32h2" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" -build "esp32" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" +build "esp32p4" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" +build "esp32s3" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" +build "esp32s2" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" +build "esp32c3" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" +build "esp32c6" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" +build "esp32h2" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" +build "esp32" "$CHUNK_INDEX" "$CHUNKS_CNT" "$BUILD_LOG" "$LOG_LEVEL" "$SKETCHES_FILE" "${SKETCHES_ESP32[@]}" if [ "$BUILD_LOG" -eq 1 ]; then #remove last comma from the last JSON object diff --git a/.github/scripts/sketch_utils.sh b/.github/scripts/sketch_utils.sh index 3e6ceb675e9..00d7d1bc232 100755 --- a/.github/scripts/sketch_utils.sh +++ b/.github/scripts/sketch_utils.sh @@ -443,6 +443,11 @@ function build_sketches { # build_sketches > $GITHUB_ENV + elif [ "${{ github.event_name }}" == "schedule" ]; then + echo "LOG_LEVEL=verbose" >> $GITHUB_ENV + else + echo "LOG_LEVEL=none" >> $GITHUB_ENV + fi + - name: Build all sketches if: ${{ needs.gen-chunks.outputs.build_all == 'true' }} - run: bash ./.github/scripts/on-push.sh ${{ matrix.chunk }} ${{ env.MAX_CHUNKS }} 1 + run: bash ./.github/scripts/on-push.sh ${{ matrix.chunk }} ${{ env.MAX_CHUNKS }} 1 ${{ env.LOG_LEVEL }} - name: Download sketches found if: ${{ needs.gen-chunks.outputs.build_all == 'false' && needs.gen-chunks.outputs.build_libraries == 'true' }} @@ -175,7 +201,7 @@ jobs: - name: Build selected sketches if: ${{ needs.gen-chunks.outputs.build_all == 'false' && needs.gen-chunks.outputs.build_libraries == 'true' }} - run: bash ./.github/scripts/on-push.sh ${{ matrix.chunk }} ${{ needs.gen-chunks.outputs.chunk_count }} 1 sketches_found.txt + run: bash ./.github/scripts/on-push.sh ${{ matrix.chunk }} ${{ needs.gen-chunks.outputs.chunk_count }} 1 ${{ env.LOG_LEVEL }} sketches_found.txt #Upload cli compile json as artifact - name: Upload cli compile json From 238c384e4ed97cb0b836793e9554fcaae943e5b5 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 17 Dec 2024 19:02:24 -0300 Subject: [PATCH 04/60] fix(readme): Update readme and fix badges --- .github/workflows/tests_results.yml | 3 ++- README.md | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests_results.yml b/.github/workflows/tests_results.yml index 4ef338a9e16..947de64ece5 100644 --- a/.github/workflows/tests_results.yml +++ b/.github/workflows/tests_results.yml @@ -149,9 +149,10 @@ jobs: uses: jaywcjlove/generated-badges@v1.0.13 with: label: Runtime Tests - status: ${{ job.status }} + status: ${{ job.status == 'success' && 'passing' || 'failing' }} output: runtime-tests-results/badge.svg color: ${{ job.status == 'success' && 'green' || 'red' }} + style: flat - name: Push badge if: ${{ !cancelled() && (env.original_event == 'schedule' || env.original_event == 'workflow_dispatch') }} # codespell:ignore cancelled diff --git a/README.md b/README.md index 5b1b1865efa..e610229fc60 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # Arduino core for the ESP32, ESP32-P4, ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6 and ESP32-H2 -[![Build Status](https://github.com/espressif/arduino-esp32/actions/workflows/push.yml/badge.svg?branch=master&event=push)](https://github.com/espressif/arduino-esp32/actions/workflows/push.yml) [![External Libraries Test](https://github.com/espressif/arduino-esp32/actions/workflows/lib.yml/badge.svg?branch=master&event=schedule)](https://github.com/espressif/arduino-esp32/blob/gh-pages/LIBRARIES_TEST.md) [![Hardware Tests](https://github.com/espressif/arduino-esp32/blob/gh-pages/runtime-tests-results/badge.svg)](https://github.com/espressif/arduino-esp32/actions/workflows/tests_results.yml) +[![Build Status](https://img.shields.io/github/actions/workflow/status/espressif/arduino-esp32/push.yml?branch=master&event=push&label=Compilation%20Tests)](https://github.com/espressif/arduino-esp32/actions/workflows/push.yml?query=branch%3Amaster+event%3Apush) +[![Verbose Build Status](https://img.shields.io/github/actions/workflow/status/espressif/arduino-esp32/push.yml?branch=master&event=schedule&label=Compilation%20Tests%20(Verbose))](https://github.com/espressif/arduino-esp32/actions/workflows/push.yml?query=branch%3Amaster+event%3Aschedule) +[![External Libraries Test](https://img.shields.io/github/actions/workflow/status/espressif/arduino-esp32/lib.yml?branch=master&event=schedule&label=External%20Libraries%20Test)](https://github.com/espressif/arduino-esp32/blob/gh-pages/LIBRARIES_TEST.md) +[![Runtime Tests](https://github.com/espressif/arduino-esp32/blob/gh-pages/runtime-tests-results/badge.svg)](https://github.com/espressif/arduino-esp32/actions/workflows/tests_results.yml) ### Need help or have a question? Join the chat at [Gitter](https://gitter.im/espressif/arduino-esp32) or [open a new Discussion](https://github.com/espressif/arduino-esp32/discussions) @@ -16,9 +19,17 @@ ### Development Status -Latest Stable Release [![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) [![Release Date](https://img.shields.io/github/release-date/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) [![Downloads](https://img.shields.io/github/downloads/espressif/arduino-esp32/latest/total.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/) +#### Latest Stable Release -Latest Development Release [![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32/all.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/) [![Release Date](https://img.shields.io/github/release-date-pre/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/) [![Downloads](https://img.shields.io/github/downloads-pre/espressif/arduino-esp32/latest/total.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/) +[![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32.svg)](https://github.com/espressif/arduino-esp32/releases/latest/) +[![Release Date](https://img.shields.io/github/release-date/espressif/arduino-esp32.svg)](https://github.com/espressif/arduino-esp32/releases/latest/) +[![Downloads](https://img.shields.io/github/downloads/espressif/arduino-esp32/latest/total.svg)](https://github.com/espressif/arduino-esp32/releases/latest/) + +#### Latest Development Release + +[![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32/all.svg)](https://github.com/espressif/arduino-esp32/releases/) +[![Release Date](https://img.shields.io/github/release-date-pre/espressif/arduino-esp32.svg)](https://github.com/espressif/arduino-esp32/releases/) +[![Downloads](https://img.shields.io/github/downloads-pre/espressif/arduino-esp32/latest/total.svg)](https://github.com/espressif/arduino-esp32/releases/) ### Development Planning @@ -59,7 +70,7 @@ Here are the ESP32 series supported by the Arduino-ESP32 project: | ESP32-S3 | Yes | Yes | [ESP32-S3](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf) | | ESP32-C6 | Yes | Yes | [ESP32-C6](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf) | | ESP32-H2 | Yes | Yes | [ESP32-H2](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf) | -| ESP32-P4 | No | Yes | [ESP32-P4](https://www.espressif.com/sites/default/files/documentation/esp32-p4_datasheet_en.pdf) | +| ESP32-P4 | Yes | Yes | [ESP32-P4](https://www.espressif.com/sites/default/files/documentation/esp32-p4_datasheet_en.pdf) | > [!NOTE] > ESP32-C2 is also supported by Arduino-ESP32 but requires rebuilding the static libraries. This is not trivial and requires a good understanding of the ESP-IDF From 4884c964631b6fd097a1f4e3504a9524b3b9f0ec Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:35:59 -0300 Subject: [PATCH 05/60] fix(component): Checkout proper branch for uploading component --- .github/workflows/upload-idf-component.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/upload-idf-component.yml b/.github/workflows/upload-idf-component.yml index c716e8144c2..bc544e02114 100644 --- a/.github/workflows/upload-idf-component.yml +++ b/.github/workflows/upload-idf-component.yml @@ -1,6 +1,11 @@ name: Push components to https://components.espressif.com on: + workflow_dispatch: + inputs: + tag: + description: 'Tag to push to the component registry' + required: true workflow_run: workflows: ["ESP32 Arduino Release"] types: @@ -15,7 +20,7 @@ jobs: steps: - name: Get the release tag env: - head_branch: ${{ github.event.workflow_run.head_branch }} + head_branch: ${{ github.event.inputs.tag || github.event.workflow_run.head_branch }} run: | if [ "${{ github.event.workflow_run.conclusion }}" != "success" ]; then echo "Release workflow failed. Exiting..." @@ -39,6 +44,7 @@ jobs: - uses: actions/checkout@v4 with: + ref: ${{ env.RELEASE_TAG }} submodules: "recursive" - name: Upload components to the component registry From 352705e6077de4f0575324efa234814e823b9fb8 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:55:26 -0300 Subject: [PATCH 06/60] fix(component): Add check for workflow_run (#10753) --- .github/workflows/upload-idf-component.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/upload-idf-component.yml b/.github/workflows/upload-idf-component.yml index bc544e02114..8faddef7e5f 100644 --- a/.github/workflows/upload-idf-component.yml +++ b/.github/workflows/upload-idf-component.yml @@ -20,9 +20,9 @@ jobs: steps: - name: Get the release tag env: - head_branch: ${{ github.event.inputs.tag || github.event.workflow_run.head_branch }} + head_branch: ${{ inputs.tag || github.event.workflow_run.head_branch }} run: | - if [ "${{ github.event.workflow_run.conclusion }}" != "success" ]; then + if [ "${{ github.event.workflow_run.conclusion }}" != "success" ] && [ "${{ github.event_name }}" == "workflow_run" ]; then echo "Release workflow failed. Exiting..." exit 1 fi From 9eb7dc6f7bddf98600f6e8de3ccc19cc8ba5281b Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Thu, 19 Dec 2024 12:54:56 +0200 Subject: [PATCH 07/60] Update Issue-report.yml with the latest version numbers --- .github/ISSUE_TEMPLATE/Issue-report.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/Issue-report.yml b/.github/ISSUE_TEMPLATE/Issue-report.yml index 436c8a57b7e..e3a4e92926e 100644 --- a/.github/ISSUE_TEMPLATE/Issue-report.yml +++ b/.github/ISSUE_TEMPLATE/Issue-report.yml @@ -41,6 +41,8 @@ body: options: - latest master (checkout manually) - latest development Release Candidate (RC-X) + - v3.1.0 + - v3.0.7 - v3.0.6 - v3.0.5 - v3.0.4 From 5c41881858d3f387f80bbdea53be39b10e5608df Mon Sep 17 00:00:00 2001 From: Ramprasad G Date: Tue, 7 Jan 2025 01:59:52 -0800 Subject: [PATCH 08/60] improving logging message while waiting for WiFi (#10739) Co-authored-by: Me No Dev --- .../ESP32/examples/Camera/CameraWebServer/CameraWebServer.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/ESP32/examples/Camera/CameraWebServer/CameraWebServer.ino b/libraries/ESP32/examples/Camera/CameraWebServer/CameraWebServer.ino index 5c544c934bf..d483e11b1df 100644 --- a/libraries/ESP32/examples/Camera/CameraWebServer/CameraWebServer.ino +++ b/libraries/ESP32/examples/Camera/CameraWebServer/CameraWebServer.ino @@ -136,6 +136,7 @@ void setup() { WiFi.begin(ssid, password); WiFi.setSleep(false); + Serial.print("WiFi connecting"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); From 089cbabf1716aef8b0350891c201dcf1e1feb14d Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 7 Jan 2025 07:00:21 -0300 Subject: [PATCH 09/60] docs(3rd-party): Add section for third party tools (#10749) * docs(3rd-party): Add section for third party tools * fix(docs): Fix dependencies in Python 3.13 * fix(typo): Fix docs typo * docs(3rd-party): Re-write some phrases * ci(pre-commit): Apply automatic fixes * fix(naming): Rename files * fix(spelling): Fix spelling mistake --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- docs/en/index.rst | 1 + docs/en/third_party/pioarduino.rst | 14 ++++++++++ docs/en/third_party/wokwi.rst | 42 ++++++++++++++++++++++++++++++ docs/en/third_party_tools.rst | 16 ++++++++++++ docs/requirements.txt | 1 + 5 files changed, 74 insertions(+) create mode 100644 docs/en/third_party/pioarduino.rst create mode 100644 docs/en/third_party/wokwi.rst create mode 100644 docs/en/third_party_tools.rst diff --git a/docs/en/index.rst b/docs/en/index.rst index 40b3251c912..1314a8fc78d 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -16,6 +16,7 @@ Here you will find all the relevant information about the project. Guides Tutorials Advanced Utilities + Third Party Tools Migration Guides FAQ Troubleshooting diff --git a/docs/en/third_party/pioarduino.rst b/docs/en/third_party/pioarduino.rst new file mode 100644 index 00000000000..49af583befa --- /dev/null +++ b/docs/en/third_party/pioarduino.rst @@ -0,0 +1,14 @@ +####################################################### +pioarduino - (p)eople (i)nitiated (o)ptimized (arduino) +####################################################### + +.. warning:: + This tool is **not maintained by the ESP32 Arduino Core team**, so we cannot provide support or guarantee that it will work as expected. + +.. note:: + This is a work in progress documentation and we will appreciate your help! We are looking for contributors! + +About +----- + +For more information, please refer to the `official documentation `_. diff --git a/docs/en/third_party/wokwi.rst b/docs/en/third_party/wokwi.rst new file mode 100644 index 00000000000..0a6eb9d0992 --- /dev/null +++ b/docs/en/third_party/wokwi.rst @@ -0,0 +1,42 @@ +##### +Wokwi +##### + +.. warning:: + This tool is **not maintained by the ESP32 Arduino Core team**, so we cannot provide support or guarantee that it will work as expected. + +.. note:: + This is a work in progress documentation and we will appreciate your help! We are looking for contributors! + +About +----- + +Wokwi is an online Electronics simulator. You can use it to simulate Arduino, ESP32, and many other popular boards, parts and sensors. + +The advantages of using Wokwi include: + +- Immediate start: No need to wait for components or download large software. Everything required is available in your browser, enabling you to begin coding your IoT project within seconds. +- Safe experimentation: Virtual hardware cannot be damaged, allowing users to experiment freely without the risk of destroying components. Mistakes can be easily undone. +- Easy collaboration: Sharing a link to your Wokwi project facilitates obtaining help and feedback from others. +- Code reliability: Helps in distinguishing between hardware and software issues, thereby increasing confidence in your code. +- Unlimited resources: Access to an unlimited number of parts without concerns about cost or availability. +- Supportive community: A maker-friendly environment where users can share projects, seek assistance, and find inspiration. + +Unique features provided by Wokwi: + +- Wi-Fi simulation - Connect your simulated project to the internet. You can use MQTT, HTTP, NTP, and many other network protocols. +- Virtual Logic Analyzer - Capture digital signals in your simulation (e.g. UART, I2C, SPI) and analyze them on your computer. +- Advanced debugging with GDB - Powerful Arduino debugger for advanced users. +- SD card simulation - Store and retrieve files and directories from your code. Paying users can also upload binary files (such as images) +- Chips API - Create your own custom chips and parts, and share them with the community. +- Visual Studio Code integration - Simulate your embedded projects directly from VS Code. + +Pricing +------- + +Wokwi is free for personal use. For commercial users and professionals, please check out the paid plans in the `pricing page `_. + +Learn more +---------- + +For more information, please refer to the `official Wokwi website `_ and the `Wokwi documentation `_. diff --git a/docs/en/third_party_tools.rst b/docs/en/third_party_tools.rst new file mode 100644 index 00000000000..fd8fd92ae0c --- /dev/null +++ b/docs/en/third_party_tools.rst @@ -0,0 +1,16 @@ +################# +Third Party Tools +################# + +Here you will find documentation pages for third party tools that can be used with the ESP32 Arduino Core. + +.. warning:: + These tools are **not maintained by the ESP32 Arduino Core team**, so we cannot provide support or guarantee that they will work as expected. + Each tool documentation should be provided and maintained by the community. + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + pioarduino + Wokwi diff --git a/docs/requirements.txt b/docs/requirements.txt index 505feb2e764..d3017fb5adc 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,3 +2,4 @@ esp-docs>=1.4.0 sphinx-copybutton==0.5.0 sphinx-tabs==3.2.0 numpydoc==1.5.0 +standard-imghdr==3.13.0 From b07eb175d8cc7e64c321f9a5673783d9149968a7 Mon Sep 17 00:00:00 2001 From: Mathieu Carbou Date: Tue, 7 Jan 2025 11:00:50 +0100 Subject: [PATCH 10/60] feat(webserver): Middleware with default middleware for cors, authc, curl-like logging (#10750) * feat(webserver): Middleware with default middleware for cors, authc, curl-like logging * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: Rodrigo Garcia Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- CMakeLists.txt | 6 +- .../examples/Middleware/Middleware.ino | 186 ++++++++++++++ .../WebServer/examples/Middleware/ci.json | 5 + libraries/WebServer/src/Middlewares.h | 66 +++++ libraries/WebServer/src/Parsing.cpp | 39 ++- libraries/WebServer/src/WebServer.cpp | 242 +++++++++++++----- libraries/WebServer/src/WebServer.h | 79 +++--- .../WebServer/src/detail/RequestHandler.h | 10 +- .../src/detail/RequestHandlersImpl.h | 33 +++ .../middleware/AuthenticationMiddleware.cpp | 82 ++++++ .../src/middleware/CorsMiddleware.cpp | 47 ++++ .../src/middleware/LoggingMiddleware.cpp | 74 ++++++ .../WebServer/src/middleware/Middleware.h | 54 ++++ .../src/middleware/MiddlewareChain.cpp | 73 ++++++ 14 files changed, 895 insertions(+), 101 deletions(-) create mode 100644 libraries/WebServer/examples/Middleware/Middleware.ino create mode 100644 libraries/WebServer/examples/Middleware/ci.json create mode 100644 libraries/WebServer/src/Middlewares.h create mode 100644 libraries/WebServer/src/middleware/AuthenticationMiddleware.cpp create mode 100644 libraries/WebServer/src/middleware/CorsMiddleware.cpp create mode 100644 libraries/WebServer/src/middleware/LoggingMiddleware.cpp create mode 100644 libraries/WebServer/src/middleware/Middleware.h create mode 100644 libraries/WebServer/src/middleware/MiddlewareChain.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index dd15e06dac8..3718b965386 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -242,7 +242,11 @@ set(ARDUINO_LIBRARY_USB_SRCS set(ARDUINO_LIBRARY_WebServer_SRCS libraries/WebServer/src/WebServer.cpp libraries/WebServer/src/Parsing.cpp - libraries/WebServer/src/detail/mimetable.cpp) + libraries/WebServer/src/detail/mimetable.cpp + libraries/WebServer/src/middleware/MiddlewareChain.cpp + libraries/WebServer/src/middleware/AuthenticationMiddleware.cpp + libraries/WebServer/src/middleware/CorsMiddleware.cpp + libraries/WebServer/src/middleware/LoggingMiddleware.cpp) set(ARDUINO_LIBRARY_NetworkClientSecure_SRCS libraries/NetworkClientSecure/src/ssl_client.cpp diff --git a/libraries/WebServer/examples/Middleware/Middleware.ino b/libraries/WebServer/examples/Middleware/Middleware.ino new file mode 100644 index 00000000000..9d957341c2d --- /dev/null +++ b/libraries/WebServer/examples/Middleware/Middleware.ino @@ -0,0 +1,186 @@ +/** + * Basic example of using Middlewares with WebServer + * + * Middleware are common request/response processing functions that can be applied globally to all incoming requests or to specific handlers. + * They allow for a common processing thus saving memory and space to avoid duplicating code or states on multiple handlers. + * + * Once the example is flashed (with the correct WiFi credentials), you can test the following scenarios with the listed curl commands: + * - CORS Middleware: answers to OPTIONS requests with the specified CORS headers and also add CORS headers to the response when the request has the Origin header + * - Logging Middleware: logs the request and response to an output in a curl-like format + * - Authentication Middleware: test the authentication with Digest Auth + * + * You can also add your own Middleware by extending the Middleware class and implementing the run method. + * When implementing a Middleware, you can decide when to call the next Middleware in the chain by calling next(). + * + * Middleware are execute in order of addition, the ones attached to the server will be executed first. + */ +#include +#include +#include + +// Your AP WiFi Credentials +// ( This is the AP your ESP will broadcast ) +const char *ap_ssid = "ESP32_Demo"; +const char *ap_password = ""; + +WebServer server(80); + +LoggingMiddleware logger; +CorsMiddleware cors; +AuthenticationMiddleware auth; + +void setup(void) { + Serial.begin(115200); + WiFi.softAP(ap_ssid, ap_password); + + Serial.print("IP address: "); + Serial.println(WiFi.AP.localIP()); + + // curl-like output example: + // + // > curl -v -X OPTIONS -H "origin: http://192.168.4.1" http://192.168.4.1/ + // + // Connection from 192.168.4.2:51683 + // > OPTIONS / HTTP/1.1 + // > Host: 192.168.4.1 + // > User-Agent: curl/8.10.0 + // > Accept: */* + // > origin: http://192.168.4.1 + // > + // * Processed in 5 ms + // < HTTP/1.HTTP/1.1 200 OK + // < Content-Type: text/html + // < Access-Control-Allow-Origin: http://192.168.4.1 + // < Access-Control-Allow-Methods: POST,GET,OPTIONS,DELETE + // < Access-Control-Allow-Headers: X-Custom-Header + // < Access-Control-Allow-Credentials: false + // < Access-Control-Max-Age: 600 + // < Content-Length: 0 + // < Connection: close + // < + logger.setOutput(Serial); + + cors.setOrigin("http://192.168.4.1"); + cors.setMethods("POST,GET,OPTIONS,DELETE"); + cors.setHeaders("X-Custom-Header"); + cors.setAllowCredentials(false); + cors.setMaxAge(600); + + auth.setUsername("admin"); + auth.setPassword("admin"); + auth.setRealm("My Super App"); + auth.setAuthMethod(DIGEST_AUTH); + auth.setAuthFailureMessage("Authentication Failed"); + + server.addMiddleware(&logger); + server.addMiddleware(&cors); + + // Not authenticated + // + // Test CORS preflight request with: + // > curl -v -X OPTIONS -H "origin: http://192.168.4.1" http://192.168.4.1/ + // + // Test cross-domain request with: + // > curl -v -X GET -H "origin: http://192.168.4.1" http://192.168.4.1/ + // + server.on("/", []() { + server.send(200, "text/plain", "Home"); + }); + + // Authenticated + // + // > curl -v -X GET -H "origin: http://192.168.4.1" http://192.168.4.1/protected + // + // Outputs: + // + // * Connection from 192.168.4.2:51750 + // > GET /protected HTTP/1.1 + // > Host: 192.168.4.1 + // > User-Agent: curl/8.10.0 + // > Accept: */* + // > origin: http://192.168.4.1 + // > + // * Processed in 7 ms + // < HTTP/1.HTTP/1.1 401 Unauthorized + // < Content-Type: text/html + // < Access-Control-Allow-Origin: http://192.168.4.1 + // < Access-Control-Allow-Methods: POST,GET,OPTIONS,DELETE + // < Access-Control-Allow-Headers: X-Custom-Header + // < Access-Control-Allow-Credentials: false + // < Access-Control-Max-Age: 600 + // < WWW-Authenticate: Digest realm="My Super App", qop="auth", nonce="ac388a64184e3e102aae6fff1c9e8d76", opaque="e7d158f2b54d25328142d118ff0f932d" + // < Content-Length: 21 + // < Connection: close + // < + // + // > curl -v -X GET -H "origin: http://192.168.4.1" --digest -u admin:admin http://192.168.4.1/protected + // + // Outputs: + // + // * Connection from 192.168.4.2:53662 + // > GET /protected HTTP/1.1 + // > Authorization: Digest username="admin", realm="My Super App", nonce="db9e6824eb2a13bc7b2bf8f3c43db896", uri="/protected", cnonce="NTliZDZiNTcwODM2MzAyY2JjMDBmZGJmNzFiY2ZmNzk=", nc=00000001, qop=auth, response="6ebd145ba0d3496a4a73f5ae79ff5264", opaque="23d739c22810282ff820538cba98bda4" + // > Host: 192.168.4.1 + // > User-Agent: curl/8.10.0 + // > Accept: */* + // > origin: http://192.168.4.1 + // > + // Request handling... + // * Processed in 7 ms + // < HTTP/1.HTTP/1.1 200 OK + // < Content-Type: text/plain + // < Access-Control-Allow-Origin: http://192.168.4.1 + // < Access-Control-Allow-Methods: POST,GET,OPTIONS,DELETE + // < Access-Control-Allow-Headers: X-Custom-Header + // < Access-Control-Allow-Credentials: false + // < Access-Control-Max-Age: 600 + // < Content-Length: 9 + // < Connection: close + // < + server + .on( + "/protected", + []() { + Serial.println("Request handling..."); + server.send(200, "text/plain", "Protected"); + } + ) + .addMiddleware(&auth); + + // Not found is also handled by global middleware + // + // curl -v -X GET -H "origin: http://192.168.4.1" http://192.168.4.1/inexsting + // + // Outputs: + // + // * Connection from 192.168.4.2:53683 + // > GET /inexsting HTTP/1.1 + // > Host: 192.168.4.1 + // > User-Agent: curl/8.10.0 + // > Accept: */* + // > origin: http://192.168.4.1 + // > + // * Processed in 16 ms + // < HTTP/1.HTTP/1.1 404 Not Found + // < Content-Type: text/plain + // < Access-Control-Allow-Origin: http://192.168.4.1 + // < Access-Control-Allow-Methods: POST,GET,OPTIONS,DELETE + // < Access-Control-Allow-Headers: X-Custom-Header + // < Access-Control-Allow-Credentials: false + // < Access-Control-Max-Age: 600 + // < Content-Length: 14 + // < Connection: close + // < + server.onNotFound([]() { + server.send(404, "text/plain", "Page not found"); + }); + + server.collectAllHeaders(); + server.begin(); + Serial.println("HTTP server started"); +} + +void loop(void) { + server.handleClient(); + delay(2); //allow the cpu to switch to other tasks +} diff --git a/libraries/WebServer/examples/Middleware/ci.json b/libraries/WebServer/examples/Middleware/ci.json new file mode 100644 index 00000000000..36babb82730 --- /dev/null +++ b/libraries/WebServer/examples/Middleware/ci.json @@ -0,0 +1,5 @@ +{ + "requires": [ + "CONFIG_SOC_WIFI_SUPPORTED=y" + ] +} diff --git a/libraries/WebServer/src/Middlewares.h b/libraries/WebServer/src/Middlewares.h new file mode 100644 index 00000000000..04fab52790b --- /dev/null +++ b/libraries/WebServer/src/Middlewares.h @@ -0,0 +1,66 @@ +#ifndef MIDDLEWARES_H +#define MIDDLEWARES_H + +#include +#include + +#include + +// curl-like logging middleware +class LoggingMiddleware : public Middleware { +public: + void setOutput(Print &output); + + bool run(WebServer &server, Middleware::Callback next) override; + +private: + Print *_out = nullptr; +}; + +class CorsMiddleware : public Middleware { +public: + CorsMiddleware &setOrigin(const char *origin); + CorsMiddleware &setMethods(const char *methods); + CorsMiddleware &setHeaders(const char *headers); + CorsMiddleware &setAllowCredentials(bool credentials); + CorsMiddleware &setMaxAge(uint32_t seconds); + + void addCORSHeaders(WebServer &server); + + bool run(WebServer &server, Middleware::Callback next) override; + +private: + String _origin = F("*"); + String _methods = F("*"); + String _headers = F("*"); + bool _credentials = true; + uint32_t _maxAge = 86400; +}; + +class AuthenticationMiddleware : public Middleware { +public: + AuthenticationMiddleware &setUsername(const char *username); + AuthenticationMiddleware &setPassword(const char *password); + AuthenticationMiddleware &setPasswordHash(const char *sha1AsBase64orHex); + AuthenticationMiddleware &setCallback(WebServer::THandlerFunctionAuthCheck fn); + + AuthenticationMiddleware &setRealm(const char *realm); + AuthenticationMiddleware &setAuthMethod(HTTPAuthMethod method); + AuthenticationMiddleware &setAuthFailureMessage(const char *message); + + bool isAllowed(WebServer &server) const; + + bool run(WebServer &server, Middleware::Callback next) override; + +private: + String _username; + String _password; + bool _hash = false; + WebServer::THandlerFunctionAuthCheck _callback; + + const char *_realm = nullptr; + HTTPAuthMethod _method = BASIC_AUTH; + String _authFailMsg; +}; + +#endif diff --git a/libraries/WebServer/src/Parsing.cpp b/libraries/WebServer/src/Parsing.cpp index 040338bb749..3030317eeea 100644 --- a/libraries/WebServer/src/Parsing.cpp +++ b/libraries/WebServer/src/Parsing.cpp @@ -78,8 +78,14 @@ bool WebServer::_parseRequest(NetworkClient &client) { String req = client.readStringUntil('\r'); client.readStringUntil('\n'); //reset header value - for (int i = 0; i < _headerKeysCount; ++i) { - _currentHeaders[i].value = String(); + if (_collectAllHeaders) { + // clear previous headers + collectAllHeaders(); + } else { + // clear previous headers + for (RequestArgument *header = _currentHeaders; header; header = header->next) { + header->value = String(); + } } // First line of HTTP request looks like "GET /path HTTP/1.1" @@ -154,9 +160,6 @@ bool WebServer::_parseRequest(NetworkClient &client) { headerValue.trim(); _collectHeader(headerName.c_str(), headerValue.c_str()); - log_v("headerName: %s", headerName.c_str()); - log_v("headerValue: %s", headerValue.c_str()); - if (headerName.equalsIgnoreCase(FPSTR(Content_Type))) { using namespace mime; if (headerValue.startsWith(FPSTR(mimeTable[txt].mimeType))) { @@ -254,9 +257,6 @@ bool WebServer::_parseRequest(NetworkClient &client) { headerValue = req.substring(headerDiv + 2); _collectHeader(headerName.c_str(), headerValue.c_str()); - log_v("headerName: %s", headerName.c_str()); - log_v("headerValue: %s", headerValue.c_str()); - if (headerName.equalsIgnoreCase("Host")) { _hostHeader = headerValue; } @@ -272,12 +272,29 @@ bool WebServer::_parseRequest(NetworkClient &client) { } bool WebServer::_collectHeader(const char *headerName, const char *headerValue) { - for (int i = 0; i < _headerKeysCount; i++) { - if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) { - _currentHeaders[i].value = headerValue; + RequestArgument *last = nullptr; + for (RequestArgument *header = _currentHeaders; header; header = header->next) { + if (header->next == nullptr) { + last = header; + } + if (header->key.equalsIgnoreCase(headerName)) { + header->value = headerValue; + log_v("header collected: %s: %s", headerName, headerValue); return true; } } + assert(last); + if (_collectAllHeaders) { + last->next = new RequestArgument(); + last->next->key = headerName; + last->next->value = headerValue; + _headerKeysCount++; + log_v("header collected: %s: %s", headerName, headerValue); + return true; + } + + log_v("header skipped: %s: %s", headerName, headerValue); + return false; } diff --git a/libraries/WebServer/src/WebServer.cpp b/libraries/WebServer/src/WebServer.cpp index 53c575d2c56..652a86f587f 100644 --- a/libraries/WebServer/src/WebServer.cpp +++ b/libraries/WebServer/src/WebServer.cpp @@ -41,31 +41,28 @@ static const char WWW_Authenticate[] = "WWW-Authenticate"; static const char Content_Length[] = "Content-Length"; static const char ETAG_HEADER[] = "If-None-Match"; -WebServer::WebServer(IPAddress addr, int port) - : _corsEnabled(false), _server(addr, port), _currentMethod(HTTP_ANY), _currentVersion(0), _currentStatus(HC_NONE), _statusChange(0), _nullDelay(true), - _currentHandler(nullptr), _firstHandler(nullptr), _lastHandler(nullptr), _currentArgCount(0), _currentArgs(nullptr), _postArgsLen(0), _postArgs(nullptr), - _headerKeysCount(0), _currentHeaders(nullptr), _contentLength(0), _clientContentLength(0), _chunked(false) { +WebServer::WebServer(IPAddress addr, int port) : _server(addr, port) { log_v("WebServer::Webserver(addr=%s, port=%d)", addr.toString().c_str(), port); } -WebServer::WebServer(int port) - : _corsEnabled(false), _server(port), _currentMethod(HTTP_ANY), _currentVersion(0), _currentStatus(HC_NONE), _statusChange(0), _nullDelay(true), - _currentHandler(nullptr), _firstHandler(nullptr), _lastHandler(nullptr), _currentArgCount(0), _currentArgs(nullptr), _postArgsLen(0), _postArgs(nullptr), - _headerKeysCount(0), _currentHeaders(nullptr), _contentLength(0), _clientContentLength(0), _chunked(false) { +WebServer::WebServer(int port) : _server(port) { log_v("WebServer::Webserver(port=%d)", port); } WebServer::~WebServer() { _server.close(); - if (_currentHeaders) { - delete[] _currentHeaders; - } + + _clearRequestHeaders(); + _clearResponseHeaders(); + delete _chain; + RequestHandler *handler = _firstHandler; while (handler) { RequestHandler *next = handler->next(); delete handler; handler = next; } + _firstHandler = nullptr; } void WebServer::begin() { @@ -436,7 +433,17 @@ void WebServer::handleClient() { _currentClient.setTimeout(HTTP_MAX_SEND_WAIT); /* / 1000 removed, WifiClient setTimeout changed to ms */ if (_parseRequest(_currentClient)) { _contentLength = CONTENT_LENGTH_NOT_SET; - _handleRequest(); + _responseCode = 0; + _clearResponseHeaders(); + + // Run server-level middlewares + if (_chain) { + _chain->runChain(*this, [this]() { + return _handleRequest(); + }); + } else { + _handleRequest(); + } if (_currentClient.isSSE()) { _currentStatus = HC_WAIT_CLOSE; @@ -495,16 +502,22 @@ void WebServer::stop() { } void WebServer::sendHeader(const String &name, const String &value, bool first) { - String headerLine = name; - headerLine += F(": "); - headerLine += value; - headerLine += "\r\n"; + RequestArgument *header = new RequestArgument(); + header->key = name; + header->value = value; - if (first) { - _responseHeaders = headerLine + _responseHeaders; + if (!_responseHeaders || first) { + header->next = _responseHeaders; + _responseHeaders = header; } else { - _responseHeaders += headerLine; + RequestArgument *last = _responseHeaders; + while (last->next) { + last = last->next; + } + last->next = header; } + + _responseHeaderCount++; } void WebServer::setContentLength(const size_t contentLength) { @@ -529,11 +542,14 @@ void WebServer::enableETag(bool enable, ETagFunction fn) { } void WebServer::_prepareHeader(String &response, int code, const char *content_type, size_t contentLength) { - response = String(F("HTTP/1.")) + String(_currentVersion) + ' '; - response += String(code); - response += ' '; - response += _responseCodeToString(code); - response += "\r\n"; + _responseCode = code; + + response.concat(version()); + response.concat(' '); + response.concat(String(code)); + response.concat(' '); + response.concat(responseCodeToString(code)); + response.concat(F("\r\n")); using namespace mime; if (!content_type) { @@ -558,9 +574,14 @@ void WebServer::_prepareHeader(String &response, int code, const char *content_t } sendHeader(String(F("Connection")), String(F("close"))); - response += _responseHeaders; - response += "\r\n"; - _responseHeaders = ""; + for (RequestArgument *header = _responseHeaders; header; header = header->next) { + response.concat(header->key); + response.concat(F(": ")); + response.concat(header->value); + response.concat(F("\r\n")); + } + + response.concat(F("\r\n")); } void WebServer::send(int code, const char *content_type, const String &content) { @@ -568,9 +589,6 @@ void WebServer::send(int code, const char *content_type, const String &content) // Can we assume the following? //if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET) // _contentLength = CONTENT_LENGTH_UNKNOWN; - if (content.length() == 0) { - log_w("content length is zero"); - } _prepareHeader(header, code, content_type, content.length()); _currentClientWrite(header.c_str(), header.length()); if (content.length()) { @@ -728,39 +746,43 @@ bool WebServer::hasArg(const String &name) const { } String WebServer::header(const String &name) const { - for (int i = 0; i < _headerKeysCount; ++i) { - if (_currentHeaders[i].key.equalsIgnoreCase(name)) { - return _currentHeaders[i].value; + for (RequestArgument *current = _currentHeaders; current; current = current->next) { + if (current->key.equalsIgnoreCase(name)) { + return current->value; } } return ""; } void WebServer::collectHeaders(const char *headerKeys[], const size_t headerKeysCount) { - _headerKeysCount = headerKeysCount + 2; - if (_currentHeaders) { - delete[] _currentHeaders; - } - _currentHeaders = new RequestArgument[_headerKeysCount]; - _currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER); - _currentHeaders[1].key = FPSTR(ETAG_HEADER); + collectAllHeaders(); + _collectAllHeaders = false; + + _headerKeysCount += headerKeysCount; + + RequestArgument *last = _currentHeaders->next; + for (int i = 2; i < _headerKeysCount; i++) { - _currentHeaders[i].key = headerKeys[i - 2]; + last->next = new RequestArgument(); + last->next->key = headerKeys[i - 2]; + last = last->next; } } String WebServer::header(int i) const { - if (i < _headerKeysCount) { - return _currentHeaders[i].value; + RequestArgument *current = _currentHeaders; + while (current && i--) { + current = current->next; } - return ""; + return current ? current->value : emptyString; } String WebServer::headerName(int i) const { - if (i < _headerKeysCount) { - return _currentHeaders[i].key; + RequestArgument *current = _currentHeaders; + while (current && i--) { + current = current->next; } - return ""; + return current ? current->key : emptyString; } int WebServer::headers() const { @@ -768,12 +790,7 @@ int WebServer::headers() const { } bool WebServer::hasHeader(const String &name) const { - for (int i = 0; i < _headerKeysCount; ++i) { - if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0)) { - return true; - } - } - return false; + return header(name).length() > 0; } String WebServer::hostHeader() const { @@ -788,16 +805,17 @@ void WebServer::onNotFound(THandlerFunction fn) { _notFoundHandler = fn; } -void WebServer::_handleRequest() { +bool WebServer::_handleRequest() { bool handled = false; - if (!_currentHandler) { - log_e("request handler not found"); - } else { - handled = _currentHandler->handle(*this, _currentMethod, _currentUri); + if (_currentHandler) { + handled = _currentHandler->process(*this, _currentMethod, _currentUri); if (!handled) { log_e("request handler failed to handle request"); } } + // DO NOT LOG if _currentHandler == null !! + // This is is valid use case to handle any other requests + // Also, this is just causing log flooding if (!handled && _notFoundHandler) { _notFoundHandler(); handled = true; @@ -811,6 +829,7 @@ void WebServer::_handleRequest() { _finalizeResponse(); } _currentUri = ""; + return handled; } void WebServer::_finalizeResponse() { @@ -819,7 +838,7 @@ void WebServer::_finalizeResponse() { } } -String WebServer::_responseCodeToString(int code) { +String WebServer::responseCodeToString(int code) { switch (code) { case 100: return F("Continue"); case 101: return F("Switching Protocols"); @@ -864,3 +883,108 @@ String WebServer::_responseCodeToString(int code) { default: return F(""); } } + +void WebServer::_clearResponseHeaders() { + _responseHeaderCount = 0; + RequestArgument *current = _responseHeaders; + while (current) { + RequestArgument *next = current->next; + delete current; + current = next; + } + _responseHeaders = nullptr; +} + +void WebServer::_clearRequestHeaders() { + _headerKeysCount = 0; + RequestArgument *current = _currentHeaders; + while (current) { + RequestArgument *next = current->next; + delete current; + current = next; + } + _currentHeaders = nullptr; +} + +void WebServer::collectAllHeaders() { + _clearRequestHeaders(); + + _currentHeaders = new RequestArgument(); + _currentHeaders->key = FPSTR(AUTHORIZATION_HEADER); + + _currentHeaders->next = new RequestArgument(); + _currentHeaders->next->key = FPSTR(ETAG_HEADER); + + _headerKeysCount = 2; + _collectAllHeaders = true; +} + +const String &WebServer::responseHeader(String name) const { + for (RequestArgument *current = _responseHeaders; current; current = current->next) { + if (current->key.equalsIgnoreCase(name)) { + return current->value; + } + } + return emptyString; +} + +const String &WebServer::responseHeader(int i) const { + RequestArgument *current = _responseHeaders; + while (current && i--) { + current = current->next; + } + return current ? current->value : emptyString; +} + +const String &WebServer::responseHeaderName(int i) const { + RequestArgument *current = _responseHeaders; + while (current && i--) { + current = current->next; + } + return current ? current->key : emptyString; +} + +bool WebServer::hasResponseHeader(const String &name) const { + return header(name).length() > 0; +} + +int WebServer::clientContentLength() const { + return _clientContentLength; +} + +const String WebServer::version() const { + String v; + v.reserve(8); + v.concat(F("HTTP/1.")); + v.concat(_currentVersion); + return v; +} +int WebServer::responseCode() const { + return _responseCode; +} +int WebServer::responseHeaders() const { + return _responseHeaderCount; +} + +WebServer &WebServer::addMiddleware(Middleware *middleware) { + if (!_chain) { + _chain = new MiddlewareChain(); + } + _chain->addMiddleware(middleware); + return *this; +} + +WebServer &WebServer::addMiddleware(Middleware::Function fn) { + if (!_chain) { + _chain = new MiddlewareChain(); + } + _chain->addMiddleware(fn); + return *this; +} + +WebServer &WebServer::removeMiddleware(Middleware *middleware) { + if (_chain) { + _chain->removeMiddleware(middleware); + } + return *this; +} diff --git a/libraries/WebServer/src/WebServer.h b/libraries/WebServer/src/WebServer.h index 0f3405430a7..8daf12c5c30 100644 --- a/libraries/WebServer/src/WebServer.h +++ b/libraries/WebServer/src/WebServer.h @@ -92,6 +92,7 @@ typedef struct { void *data; // additional data } HTTPRaw; +#include "middleware/Middleware.h" #include "detail/RequestHandler.h" namespace fs { @@ -158,6 +159,10 @@ class WebServer { void onNotFound(THandlerFunction fn); //called when handler is not assigned void onFileUpload(THandlerFunction ufn); //handle file uploads + WebServer &addMiddleware(Middleware *middleware); + WebServer &addMiddleware(Middleware::Function fn); + WebServer &removeMiddleware(Middleware *middleware); + String uri() const { return _currentUri; } @@ -181,17 +186,23 @@ class WebServer { int args() const; // get arguments count bool hasArg(const String &name) const; // check if argument exists void collectHeaders(const char *headerKeys[], const size_t headerKeysCount); // set the request headers to collect + void collectAllHeaders(); // collect all request headers String header(const String &name) const; // get request header value by name String header(int i) const; // get request header value by number String headerName(int i) const; // get request header name by number int headers() const; // get header count bool hasHeader(const String &name) const; // check if header exists - int clientContentLength() const { - return _clientContentLength; - } // return "content-length" of incoming HTTP header from "_currentClient" + int clientContentLength() const; // return "content-length" of incoming HTTP header from "_currentClient" + const String version() const; // get the HTTP version string + String hostHeader() const; // get request host header if available or empty String if not - String hostHeader() const; // get request host header if available or empty String if not + int responseCode() const; // get the HTTP response code set + int responseHeaders() const; // get the HTTP response headers count + const String &responseHeader(String name) const; // get the HTTP response header value by name + const String &responseHeader(int i) const; // get the HTTP response header value by number + const String &responseHeaderName(int i) const; // get the HTTP response header name by number + bool hasResponseHeader(const String &name) const; // check if response header exists // send response to the client // code - HTTP response code, can be 200 or 404 @@ -228,6 +239,8 @@ class WebServer { bool _eTagEnabled = false; ETagFunction _eTagFunction = nullptr; + static String responseCodeToString(int code); + protected: virtual size_t _currentClientWrite(const char *b, size_t l) { return _currentClient.write(b, l); @@ -237,11 +250,10 @@ class WebServer { } void _addRequestHandler(RequestHandler *handler); bool _removeRequestHandler(RequestHandler *handler); - void _handleRequest(); + bool _handleRequest(); void _finalizeResponse(); bool _parseRequest(NetworkClient &client); void _parseArguments(const String &data); - static String _responseCodeToString(int code); bool _parseForm(NetworkClient &client, const String &boundary, uint32_t len); bool _parseFormUploadAborted(); void _uploadWriteByte(uint8_t b); @@ -255,48 +267,57 @@ class WebServer { // for extracting Auth parameters String _extractParam(String &authReq, const String ¶m, const char delimit = '"'); + void _clearResponseHeaders(); + void _clearRequestHeaders(); + struct RequestArgument { String key; String value; + RequestArgument *next; }; - boolean _corsEnabled; + boolean _corsEnabled = false; NetworkServer _server; NetworkClient _currentClient; - HTTPMethod _currentMethod; + HTTPMethod _currentMethod = HTTP_ANY; String _currentUri; - uint8_t _currentVersion; - HTTPClientStatus _currentStatus; - unsigned long _statusChange; - boolean _nullDelay; - - RequestHandler *_currentHandler; - RequestHandler *_firstHandler; - RequestHandler *_lastHandler; - THandlerFunction _notFoundHandler; - THandlerFunction _fileUploadHandler; - - int _currentArgCount; - RequestArgument *_currentArgs; - int _postArgsLen; - RequestArgument *_postArgs; + uint8_t _currentVersion = 0; + HTTPClientStatus _currentStatus = HC_NONE; + unsigned long _statusChange = 0; + boolean _nullDelay = true; + + RequestHandler *_currentHandler = nullptr; + RequestHandler *_firstHandler = nullptr; + RequestHandler *_lastHandler = nullptr; + THandlerFunction _notFoundHandler = nullptr; + THandlerFunction _fileUploadHandler = nullptr; + + int _currentArgCount = 0; + RequestArgument *_currentArgs = nullptr; + int _postArgsLen = 0; + RequestArgument *_postArgs = nullptr; std::unique_ptr _currentUpload; std::unique_ptr _currentRaw; - int _headerKeysCount; - RequestArgument *_currentHeaders; - size_t _contentLength; - int _clientContentLength; // "Content-Length" from header of incoming POST or GET request - String _responseHeaders; + int _headerKeysCount = 0; + RequestArgument *_currentHeaders = nullptr; + size_t _contentLength = 0; + int _clientContentLength = 0; // "Content-Length" from header of incoming POST or GET request + RequestArgument *_responseHeaders = nullptr; String _hostHeader; - bool _chunked; + bool _chunked = false; String _snonce; // Store noance and opaque for future comparison String _sopaque; String _srealm; // Store the Auth realm between Calls + + int _responseHeaderCount = 0; + int _responseCode = 0; + bool _collectAllHeaders = false; + MiddlewareChain *_chain = nullptr; }; #endif //ESP8266WEBSERVER_H diff --git a/libraries/WebServer/src/detail/RequestHandler.h b/libraries/WebServer/src/detail/RequestHandler.h index c730ce25bcb..75e11c94ba8 100644 --- a/libraries/WebServer/src/detail/RequestHandler.h +++ b/libraries/WebServer/src/detail/RequestHandler.h @@ -6,7 +6,9 @@ class RequestHandler { public: - virtual ~RequestHandler() {} + virtual ~RequestHandler() { + delete _chain; + } /* note: old handler API for backward compatibility @@ -75,8 +77,14 @@ class RequestHandler { _next = r; } + RequestHandler &addMiddleware(Middleware *middleware); + RequestHandler &addMiddleware(Middleware::Function fn); + RequestHandler &removeMiddleware(Middleware *middleware); + bool process(WebServer &server, HTTPMethod requestMethod, String requestUri); + private: RequestHandler *_next = nullptr; + MiddlewareChain *_chain = nullptr; protected: std::vector pathArgs; diff --git a/libraries/WebServer/src/detail/RequestHandlersImpl.h b/libraries/WebServer/src/detail/RequestHandlersImpl.h index c66c294dd33..3750b594ab2 100644 --- a/libraries/WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/WebServer/src/detail/RequestHandlersImpl.h @@ -10,6 +10,39 @@ using namespace mime; +RequestHandler &RequestHandler::addMiddleware(Middleware *middleware) { + if (!_chain) { + _chain = new MiddlewareChain(); + } + _chain->addMiddleware(middleware); + return *this; +} + +RequestHandler &RequestHandler::addMiddleware(Middleware::Function fn) { + if (!_chain) { + _chain = new MiddlewareChain(); + } + _chain->addMiddleware(fn); + return *this; +} + +RequestHandler &RequestHandler::removeMiddleware(Middleware *middleware) { + if (_chain) { + _chain->removeMiddleware(middleware); + } + return *this; +} + +bool RequestHandler::process(WebServer &server, HTTPMethod requestMethod, String requestUri) { + if (_chain) { + return _chain->runChain(server, [this, &server, &requestMethod, &requestUri]() { + return handle(server, requestMethod, requestUri); + }); + } else { + return handle(server, requestMethod, requestUri); + } +} + class FunctionRequestHandler : public RequestHandler { public: FunctionRequestHandler(WebServer::THandlerFunction fn, WebServer::THandlerFunction ufn, const Uri &uri, HTTPMethod method) diff --git a/libraries/WebServer/src/middleware/AuthenticationMiddleware.cpp b/libraries/WebServer/src/middleware/AuthenticationMiddleware.cpp new file mode 100644 index 00000000000..cab25ba4e50 --- /dev/null +++ b/libraries/WebServer/src/middleware/AuthenticationMiddleware.cpp @@ -0,0 +1,82 @@ +#include "Middlewares.h" + +AuthenticationMiddleware &AuthenticationMiddleware::setUsername(const char *username) { + _username = username; + _callback = nullptr; + return *this; +} + +AuthenticationMiddleware &AuthenticationMiddleware::setPassword(const char *password) { + _password = password; + _hash = false; + _callback = nullptr; + return *this; +} + +AuthenticationMiddleware &AuthenticationMiddleware::setPasswordHash(const char *sha1AsBase64orHex) { + _password = sha1AsBase64orHex; + _hash = true; + _callback = nullptr; + return *this; +} + +AuthenticationMiddleware &AuthenticationMiddleware::setCallback(WebServer::THandlerFunctionAuthCheck fn) { + assert(fn); + _callback = fn; + _hash = false; + _username = emptyString; + _password = emptyString; + return *this; +} + +AuthenticationMiddleware &AuthenticationMiddleware::setRealm(const char *realm) { + _realm = realm; + return *this; +} + +AuthenticationMiddleware &AuthenticationMiddleware::setAuthMethod(HTTPAuthMethod method) { + _method = method; + return *this; +} + +AuthenticationMiddleware &AuthenticationMiddleware::setAuthFailureMessage(const char *message) { + _authFailMsg = message; + return *this; +} + +bool AuthenticationMiddleware::isAllowed(WebServer &server) const { + if (_callback) { + return server.authenticate(_callback); + } + + if (!_username.isEmpty() && !_password.isEmpty()) { + if (_hash) { + return server.authenticateBasicSHA1(_username.c_str(), _password.c_str()); + } else { + return server.authenticate(_username.c_str(), _password.c_str()); + } + } + + return true; +} + +bool AuthenticationMiddleware::run(WebServer &server, Middleware::Callback next) { + bool authenticationRequired = false; + + if (_callback) { + authenticationRequired = !server.authenticate(_callback); + } else if (!_username.isEmpty() && !_password.isEmpty()) { + if (_hash) { + authenticationRequired = !server.authenticateBasicSHA1(_username.c_str(), _password.c_str()); + } else { + authenticationRequired = !server.authenticate(_username.c_str(), _password.c_str()); + } + } + + if (authenticationRequired) { + server.requestAuthentication(_method, _realm, _authFailMsg); + return true; + } else { + return next(); + } +} diff --git a/libraries/WebServer/src/middleware/CorsMiddleware.cpp b/libraries/WebServer/src/middleware/CorsMiddleware.cpp new file mode 100644 index 00000000000..a52ccd59f23 --- /dev/null +++ b/libraries/WebServer/src/middleware/CorsMiddleware.cpp @@ -0,0 +1,47 @@ +#include "Middlewares.h" + +CorsMiddleware &CorsMiddleware::setOrigin(const char *origin) { + _origin = origin; + return *this; +} + +CorsMiddleware &CorsMiddleware::setMethods(const char *methods) { + _methods = methods; + return *this; +} + +CorsMiddleware &CorsMiddleware::setHeaders(const char *headers) { + _headers = headers; + return *this; +} + +CorsMiddleware &CorsMiddleware::setAllowCredentials(bool credentials) { + _credentials = credentials; + return *this; +} + +CorsMiddleware &CorsMiddleware::setMaxAge(uint32_t seconds) { + _maxAge = seconds; + return *this; +} + +void CorsMiddleware::addCORSHeaders(WebServer &server) { + server.sendHeader(F("Access-Control-Allow-Origin"), _origin.c_str()); + server.sendHeader(F("Access-Control-Allow-Methods"), _methods.c_str()); + server.sendHeader(F("Access-Control-Allow-Headers"), _headers.c_str()); + server.sendHeader(F("Access-Control-Allow-Credentials"), _credentials ? F("true") : F("false")); + server.sendHeader(F("Access-Control-Max-Age"), String(_maxAge).c_str()); +} + +bool CorsMiddleware::run(WebServer &server, Middleware::Callback next) { + // Origin header ? => CORS handling + if (server.hasHeader(F("Origin"))) { + addCORSHeaders(server); + // check if this is a preflight request => handle it and return + if (server.method() == HTTP_OPTIONS) { + server.send(200); + return true; + } + } + return next(); +} diff --git a/libraries/WebServer/src/middleware/LoggingMiddleware.cpp b/libraries/WebServer/src/middleware/LoggingMiddleware.cpp new file mode 100644 index 00000000000..e1f6d708c2e --- /dev/null +++ b/libraries/WebServer/src/middleware/LoggingMiddleware.cpp @@ -0,0 +1,74 @@ +#include "Middlewares.h" + +void LoggingMiddleware::setOutput(Print &output) { + _out = &output; +} + +bool LoggingMiddleware::run(WebServer &server, Middleware::Callback next) { + if (_out == nullptr) { + return next(); + } + + _out->print(F("* Connection from ")); + _out->print(server.client().remoteIP().toString()); + _out->print(F(":")); + _out->println(server.client().remotePort()); + + _out->print(F("> ")); + const HTTPMethod method = server.method(); + if (method == HTTP_ANY) { + _out->print(F("HTTP_ANY")); + } else { + _out->print(http_method_str(method)); + } + _out->print(F(" ")); + _out->print(server.uri()); + _out->print(F(" ")); + _out->println(server.version()); + + int n = server.headers(); + for (int i = 0; i < n; i++) { + String v = server.header(i); + if (!v.isEmpty()) { + // because these 2 are always there, eventually empty: "Authorization", "If-None-Match" + _out->print(F("> ")); + _out->print(server.headerName(i)); + _out->print(F(": ")); + _out->println(server.header(i)); + } + } + + _out->println(F(">")); + + uint32_t elapsed = millis(); + const bool ret = next(); + elapsed = millis() - elapsed; + + if (ret) { + _out->print(F("* Processed in ")); + _out->print(elapsed); + _out->println(F(" ms")); + _out->print(F("< ")); + _out->print(F("HTTP/1.")); + _out->print(server.version()); + _out->print(F(" ")); + _out->print(server.responseCode()); + _out->print(F(" ")); + _out->println(WebServer::responseCodeToString(server.responseCode())); + + n = server.responseHeaders(); + for (int i = 0; i < n; i++) { + _out->print(F("< ")); + _out->print(server.responseHeaderName(i)); + _out->print(F(": ")); + _out->println(server.responseHeader(i)); + } + + _out->println(F("<")); + + } else { + _out->println(F("* Not processed!")); + } + + return ret; +} diff --git a/libraries/WebServer/src/middleware/Middleware.h b/libraries/WebServer/src/middleware/Middleware.h new file mode 100644 index 00000000000..080f5be0aba --- /dev/null +++ b/libraries/WebServer/src/middleware/Middleware.h @@ -0,0 +1,54 @@ +#ifndef MIDDLEWARE_H +#define MIDDLEWARE_H + +#include +#include + +class MiddlewareChain; +class WebServer; + +class Middleware { +public: + typedef std::function Callback; + typedef std::function Function; + + virtual ~Middleware() {} + + virtual bool run(WebServer &server, Callback next) { + return next(); + }; + +private: + friend MiddlewareChain; + Middleware *_next = nullptr; + bool _freeOnRemoval = false; +}; + +class MiddlewareFunction : public Middleware { +public: + MiddlewareFunction(Middleware::Function fn) : _fn(fn) {} + + bool run(WebServer &server, Middleware::Callback next) override { + return _fn(server, next); + } + +private: + Middleware::Function _fn; +}; + +class MiddlewareChain { +public: + ~MiddlewareChain(); + + void addMiddleware(Middleware::Function fn); + void addMiddleware(Middleware *middleware); + bool removeMiddleware(Middleware *middleware); + + bool runChain(WebServer &server, Middleware::Callback finalizer); + +private: + Middleware *_root = nullptr; + Middleware *_current = nullptr; +}; + +#endif diff --git a/libraries/WebServer/src/middleware/MiddlewareChain.cpp b/libraries/WebServer/src/middleware/MiddlewareChain.cpp new file mode 100644 index 00000000000..56b3066caea --- /dev/null +++ b/libraries/WebServer/src/middleware/MiddlewareChain.cpp @@ -0,0 +1,73 @@ +#include "Middleware.h" + +MiddlewareChain::~MiddlewareChain() { + Middleware *current = _root; + while (current) { + Middleware *next = current->_next; + if (current->_freeOnRemoval) { + delete current; + } + current = next; + } + _root = nullptr; +} + +void MiddlewareChain::addMiddleware(Middleware::Function fn) { + MiddlewareFunction *middleware = new MiddlewareFunction(fn); + middleware->_freeOnRemoval = true; + addMiddleware(middleware); +} + +void MiddlewareChain::addMiddleware(Middleware *middleware) { + if (!_root) { + _root = middleware; + return; + } + Middleware *current = _root; + while (current->_next) { + current = current->_next; + } + current->_next = middleware; +} + +bool MiddlewareChain::removeMiddleware(Middleware *middleware) { + if (!_root) { + return false; + } + if (_root == middleware) { + _root = _root->_next; + if (middleware->_freeOnRemoval) { + delete middleware; + } + return true; + } + Middleware *current = _root; + while (current->_next) { + if (current->_next == middleware) { + current->_next = current->_next->_next; + if (middleware->_freeOnRemoval) { + delete middleware; + } + return true; + } + current = current->_next; + } + return false; +} + +bool MiddlewareChain::runChain(WebServer &server, Middleware::Callback finalizer) { + if (!_root) { + return finalizer(); + } + _current = _root; + Middleware::Callback next; + next = [this, &server, &next, finalizer]() { + if (!_current) { + return finalizer(); + } + Middleware *that = _current; + _current = _current->_next; + return that->run(server, next); + }; + return next(); +} From e3cc04084de36c066d5101d2998ee0ad838a94cf Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Tue, 7 Jan 2025 07:01:16 -0300 Subject: [PATCH 11/60] feat(matter): new Matter Endpoint for Thermostat (#10755) * feat(matter): add new matter endpoint for thermostat * fix(matter): not used variable from log_e() message * feat(matter): adds specifc type name for thermostat auto mode enabled * fix(matter): suggested changes in pr review * feat(matter): added the whole list of thermostat operational modes * fix(matter): fixed type in thermostat operational modes * ci(pre-commit): Apply automatic fixes * fix(matter): typos caught by CI codespell --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- CMakeLists.txt | 1 + .../MatterThermostat/MatterThermostat.ino | 243 ++++++++++++ .../Matter/examples/MatterThermostat/ci.json | 7 + libraries/Matter/keywords.txt | 42 ++ libraries/Matter/src/Matter.h | 2 + .../src/MatterEndpoints/MatterThermostat.cpp | 370 ++++++++++++++++++ .../src/MatterEndpoints/MatterThermostat.h | 207 ++++++++++ 7 files changed, 872 insertions(+) create mode 100644 libraries/Matter/examples/MatterThermostat/MatterThermostat.ino create mode 100644 libraries/Matter/examples/MatterThermostat/ci.json create mode 100644 libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp create mode 100644 libraries/Matter/src/MatterEndpoints/MatterThermostat.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3718b965386..9fd23d14d77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,6 +181,7 @@ set(ARDUINO_LIBRARY_Matter_SRCS libraries/Matter/src/MatterEndpoints/MatterPressureSensor.cpp libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.cpp libraries/Matter/src/MatterEndpoints/MatterOnOffPlugin.cpp + libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp libraries/Matter/src/Matter.cpp) set(ARDUINO_LIBRARY_PPP_SRCS diff --git a/libraries/Matter/examples/MatterThermostat/MatterThermostat.ino b/libraries/Matter/examples/MatterThermostat/MatterThermostat.ino new file mode 100644 index 00000000000..508b508573a --- /dev/null +++ b/libraries/Matter/examples/MatterThermostat/MatterThermostat.ino @@ -0,0 +1,243 @@ +// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + This example is an example code that will create a Matter Device which can be + commissioned and controlled from a Matter Environment APP. + Additionally the ESP32 will send debug messages indicating the Matter activity. + Turning DEBUG Level ON may be useful to following Matter Accessory and Controller messages. +*/ + +// Matter Manager +#include +#include + +// List of Matter Endpoints for this Node +// Matter Thermostat Endpoint +MatterThermostat SimulatedThermostat; + +// WiFi is manually set and started +const char *ssid = "your-ssid"; // Change this to your WiFi SSID +const char *password = "your-password"; // Change this to your WiFi password + +// set your board USER BUTTON pin here - decommissioning button +const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button. + +// Button control - decommision the Matter Node +uint32_t button_time_stamp = 0; // debouncing control +bool button_state = false; // false = released | true = pressed +const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission + +// Simulate a system that will activate heating/cooling in addition to a temperature sensor - add your preferred code here +float getSimulatedTemperature(bool isHeating, bool isCooling) { + // read sensor temperature and apply heating/cooling + float simulatedTempHWSensor = SimulatedThermostat.getLocalTemperature(); + + if (isHeating) { + // it will increase to simulate a heating system + simulatedTempHWSensor = simulatedTempHWSensor + 0.5; + } + if (isCooling) { + // it will decrease to simulate a colling system + simulatedTempHWSensor = simulatedTempHWSensor - 0.5; + } + // otherwise, it will keep the temperature stable + return simulatedTempHWSensor; +} + +void setup() { + // Initialize the USER BUTTON (Boot button) that will be used to decommission the Matter Node + pinMode(buttonPin, INPUT_PULLUP); + + Serial.begin(115200); + + // Manually connect to WiFi + WiFi.begin(ssid, password); + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(); + + // Simulated Thermostat in COOLING and HEATING mode with Auto Mode to keep the temperature between setpoints + // Auto Mode can only be used when the control sequence of operation is Cooling & Heating + SimulatedThermostat.begin(MatterThermostat::THERMOSTAT_SEQ_OP_COOLING_HEATING, MatterThermostat::THERMOSTAT_AUTO_MODE_ENABLED); + + // Matter beginning - Last step, after all EndPoints are initialized + Matter.begin(); + + // Check Matter Accessory Commissioning state, which may change during execution of loop() + if (!Matter.isDeviceCommissioned()) { + Serial.println(""); + Serial.println("Matter Node is not commissioned yet."); + Serial.println("Initiate the device discovery in your Matter environment."); + Serial.println("Commission it to your Matter hub with the manual pairing code or QR code"); + Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str()); + Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str()); + // waits for Matter Thermostat Commissioning. + uint32_t timeCount = 0; + while (!Matter.isDeviceCommissioned()) { + delay(100); + if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec + Serial.println("Matter Node not commissioned yet. Waiting for commissioning."); + } + } + Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use."); + + // after commissioning, set initial thermostat parameters + // start the thermostat in AUTO mode + SimulatedThermostat.setMode(MatterThermostat::THERMOSTAT_MODE_AUTO); + // cooling setpoint must be lower than heating setpoint by at least 2.5C (deadband), in auto mode + SimulatedThermostat.setCoolingHeatingSetpoints(20.0, 23.00); // the target cooler and heating setpoint + // set the local temperature sensor in Celsius + SimulatedThermostat.setLocalTemperature(12.50); + + Serial.println(); + Serial.printf( + "Initial Setpoints are %.01fC to %.01fC with a minimum 2.5C difference\r\n", SimulatedThermostat.getHeatingSetpoint(), + SimulatedThermostat.getCoolingSetpoint() + ); + Serial.printf("Auto mode is ON. Initial Temperature of %.01fC \r\n", SimulatedThermostat.getLocalTemperature()); + Serial.println("Local Temperature Sensor will be simulated every 10 seconds and changed by a simulated heater and cooler to move in between setpoints."); + } +} + +// This will simulate the thermostat control system (heating and cooling) +// User can set a local temperature using the Serial input (type a number and press Enter) +// New temperature can be an positive or negative temperature in Celsius, between -50C and 50C +// Initial local temperature is 10C as defined in getSimulatedTemperature() function +void readSerialForNewTemperature() { + static String newTemperatureStr; + + while (Serial.available()) { + char c = Serial.read(); + if (c == '\n' || c == '\r') { + if (newTemperatureStr.length() > 0) { + // convert the string to a float value + float newTemperature = newTemperatureStr.toFloat(); + // check if the new temperature is valid + if (newTemperature >= -50.0 && newTemperature <= 50.0) { + // set the new temperature + SimulatedThermostat.setLocalTemperature(newTemperature); + Serial.printf("New Temperature is %.01fC\r\n", newTemperature); + } else { + Serial.println("Invalid Temperature value. Please type a number between -50 and 50"); + } + newTemperatureStr = ""; + } + } else { + if (c == '+' || c == '-' || (c >= '0' && c <= '9') || c == '.') { + newTemperatureStr += c; + } else { + Serial.println("Invalid character. Please type a number between -50 and 50"); + newTemperatureStr = ""; + } + } + } +} + +// loop will simulate the thermostat control system +// User can set a local temperature using the Serial input (type a number and press Enter) +// User can change the thermostat mode using the Matter APP (smartphone) +// The loop will simulate a heating and cooling system and the associated local temperature change +void loop() { + static uint32_t timeCounter = 0; + + // Simulate the heating and cooling systems + static bool isHeating = false; + static bool isCooling = false; + + // check if a new temperature is typed in the Serial Monitor + readSerialForNewTemperature(); + + // simulate thermostat with heating/cooling system and the associated local temperature change, every 10s + if (!(timeCounter++ % 20)) { // delaying for 500ms x 20 = 10s + float localTemperature = getSimulatedTemperature(isHeating, isCooling); + // Print the current thermostat local temperature value + Serial.printf("Current Local Temperature is %.01fC\r\n", localTemperature); + SimulatedThermostat.setLocalTemperature(localTemperature); // publish the new temperature value + + // Simulate the thermostat control system - User has 4 modes: OFF, HEAT, COOL, AUTO + switch (SimulatedThermostat.getMode()) { + case MatterThermostat::THERMOSTAT_MODE_OFF: + // turn off the heating and cooling systems + isHeating = false; + isCooling = false; + break; + case MatterThermostat::THERMOSTAT_MODE_AUTO: + // User APP has set the thermostat to AUTO mode -- keeping the tempeature between both setpoints + // check if the heating system should be turned on or off + if (localTemperature < SimulatedThermostat.getHeatingSetpoint() + SimulatedThermostat.getDeadBand()) { + // turn on the heating system and turn off the cooling system + isHeating = true; + isCooling = false; + } + if (localTemperature > SimulatedThermostat.getCoolingSetpoint() - SimulatedThermostat.getDeadBand()) { + // turn off the heating system and turn on the cooling system + isHeating = false; + isCooling = true; + } + break; + case MatterThermostat::THERMOSTAT_MODE_HEAT: + // Simulate the heating system - User has turned the heating system ON + isHeating = true; + isCooling = false; // keep the cooling system off as it is in heating mode + // when the heating system is in HEATING mode, it will be turned off as soon as the local temperature is above the setpoint + if (localTemperature > SimulatedThermostat.getHeatingSetpoint()) { + // turn off the heating system + isHeating = false; + } + break; + case MatterThermostat::THERMOSTAT_MODE_COOL: + // Simulate the cooling system - User has turned the cooling system ON + if (SimulatedThermostat.getMode() == MatterThermostat::THERMOSTAT_MODE_COOL) { + isCooling = true; + isHeating = false; // keep the heating system off as it is in cooling mode + // when the cooling system is in COOLING mode, it will be turned off as soon as the local temperature is bellow the setpoint + if (localTemperature < SimulatedThermostat.getCoolingSetpoint()) { + // turn off the cooling system + isCooling = false; + } + } + break; + default: log_e("Invalid Thermostat Mode %d", SimulatedThermostat.getMode()); + } + // Reporting Heating and Cooling status + Serial.printf( + "\tThermostat Mode: %s >>> Heater is %s -- Cooler is %s\r\n", MatterThermostat::getThermostatModeString(SimulatedThermostat.getMode()), + isHeating ? "ON" : "OFF", isCooling ? "ON" : "OFF" + ); + } + // Check if the button has been pressed + if (digitalRead(buttonPin) == LOW && !button_state) { + // deals with button debouncing + button_time_stamp = millis(); // record the time while the button is pressed. + button_state = true; // pressed. + } + + if (digitalRead(buttonPin) == HIGH && button_state) { + button_state = false; // released + } + + // Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node + uint32_t time_diff = millis() - button_time_stamp; + if (button_state && time_diff > decommissioningTimeout) { + Serial.println("Decommissioning the Light Matter Accessory. It shall be commissioned again."); + Matter.decommission(); + button_time_stamp = millis(); // avoid running decommissining again, reboot takes a second or so + } + + delay(500); +} diff --git a/libraries/Matter/examples/MatterThermostat/ci.json b/libraries/Matter/examples/MatterThermostat/ci.json new file mode 100644 index 00000000000..556a8a9ee6b --- /dev/null +++ b/libraries/Matter/examples/MatterThermostat/ci.json @@ -0,0 +1,7 @@ +{ + "fqbn_append": "PartitionScheme=huge_app", + "requires": [ + "CONFIG_SOC_WIFI_SUPPORTED=y", + "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" + ] +} diff --git a/libraries/Matter/keywords.txt b/libraries/Matter/keywords.txt index 3f40e598ada..a63d9a65acb 100644 --- a/libraries/Matter/keywords.txt +++ b/libraries/Matter/keywords.txt @@ -24,6 +24,18 @@ MatterContactSensor KEYWORD1 MatterPressureSensor KEYWORD1 MatterOccupancySensor KEYWORD1 MatterOnOffPlugin KEYWORD1 +MatterThermostat KEYWORD1 +ControlSequenceOfOperation_t KEYWORD1 +ThermostatMode_t KEYWORD1 +EndPointCB KEYWORD1 +EndPointHeatingSetpointCB KEYWORD1 +EndPointCoolingSetpointCB KEYWORD1 +EndPointTemperatureCB KEYWORD1 +EndPointModeCB KEYWORD1 +EndPointSpeedCB KEYWORD1 +EndPointOnOffCB KEYWORD1 +EndPointBrightnessCB KEYWORD1 +EndPointRGBColorCB KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -78,6 +90,24 @@ setPressure KEYWORD2 getPressure KEYWORD2 setOccupancy KEYWORD2 getOccupancy KEYWORD2 +getControlSequence KEYWORD2 +getMinHeatSetpoint KEYWORD2 +getMaxHeatSetpoint KEYWORD2 +getMinCoolSetpoint KEYWORD2 +getMaxCoolSetpoint KEYWORD2 +getDeadBand KEYWORD2 +setCoolingSetpoint KEYWORD2 +getCoolingSetpoint KEYWORD2 +setHeatingSetpoint KEYWORD2 +getHeatingSetpoint KEYWORD2 +setCoolingHeatingSetpoints KEYWORD2 +setLocalTemperature KEYWORD2 +getLocalTemperature KEYWORD2 +getThermostatModeString KEYWORD2 +onChangeMode KEYWORD2 +onChangeLocalTemperature KEYWORD2 +onChangeCoolingSetpoint KEYWORD2 +onChangeHeatingSetpoint KEYWORD2 ####################################### # Constants (LITERAL1) @@ -104,3 +134,15 @@ FAN_MODE_SEQ_OFF_LOW_MED_HIGH_AUTO LITERAL1 FAN_MODE_SEQ_OFF_LOW_HIGH_AUTO LITERAL1 FAN_MODE_SEQ_OFF_HIGH_AUTO LITERAL1 FAN_MODE_SEQ_OFF_HIGH LITERAL1 +THERMOSTAT_SEQ_OP_COOLING LITERAL1 +THERMOSTAT_SEQ_OP_COOLING_REHEAT LITERAL1 +THERMOSTAT_SEQ_OP_HEATING LITERAL1 +THERMOSTAT_SEQ_OP_HEATING_REHEAT LITERAL1 +THERMOSTAT_SEQ_OP_COOLING_HEATING LITERAL1 +THERMOSTAT_SEQ_OP_COOLING_HEATING_REHEAT LITERAL1 +THERMOSTAT_MODE_OFF LITERAL1 +THERMOSTAT_MODE_AUTO LITERAL1 +THERMOSTAT_MODE_COOL LITERAL1 +THERMOSTAT_MODE_HEAT LITERAL1 +THERMOSTAT_AUTO_MODE_DISABLED LITERAL1 +THERMOSTAT_AUTO_MODE_ENABLED LITERAL1 diff --git a/libraries/Matter/src/Matter.h b/libraries/Matter/src/Matter.h index 7fcab363f11..e54ceb47e5e 100644 --- a/libraries/Matter/src/Matter.h +++ b/libraries/Matter/src/Matter.h @@ -32,6 +32,7 @@ #include #include #include +#include using namespace esp_matter; @@ -70,6 +71,7 @@ class ArduinoMatter { friend class MatterPressureSensor; friend class MatterOccupancySensor; friend class MatterOnOffPlugin; + friend class MatterThermostat; protected: static void _init(); diff --git a/libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp b/libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp new file mode 100644 index 00000000000..5a68421bd8a --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp @@ -0,0 +1,370 @@ +// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include + +using namespace esp_matter; +using namespace esp_matter::endpoint; +using namespace chip::app::Clusters; + +// string helper for the THERMOSTAT MODE +const char *MatterThermostat::thermostatModeString[5] = {"OFF", "AUTO", "UNKNOWN", "COOL", "HEAT"}; + +// endpoint for color light device +namespace esp_matter { +using namespace cluster; +namespace endpoint { +namespace multi_mode_thermostat { +typedef struct config { + cluster::descriptor::config_t descriptor; + cluster::identify::config_t identify; + cluster::scenes_management::config_t scenes_management; + cluster::groups::config_t groups; + cluster::thermostat::config_t thermostat; +} config_t; + +uint32_t get_device_type_id() { + return ESP_MATTER_THERMOSTAT_DEVICE_TYPE_ID; +} + +uint8_t get_device_type_version() { + return ESP_MATTER_THERMOSTAT_DEVICE_TYPE_VERSION; +} + +esp_err_t add(endpoint_t *endpoint, config_t *config) { + if (!endpoint) { + log_e("Endpoint cannot be NULL"); + return ESP_ERR_INVALID_ARG; + } + esp_err_t err = add_device_type(endpoint, get_device_type_id(), get_device_type_version()); + if (err != ESP_OK) { + log_e("Failed to add device type id:%" PRIu32 ",err: %d", get_device_type_id(), err); + return err; + } + + descriptor::create(endpoint, &(config->descriptor), CLUSTER_FLAG_SERVER); + identify::create(endpoint, &(config->identify), CLUSTER_FLAG_SERVER); + groups::create(endpoint, &(config->groups), CLUSTER_FLAG_SERVER); + uint32_t thermostatFeatures = 0; + switch (config->thermostat.control_sequence_of_operation) { + case MatterThermostat::THERMOSTAT_SEQ_OP_COOLING: + case MatterThermostat::THERMOSTAT_SEQ_OP_COOLING_REHEAT: thermostatFeatures = cluster::thermostat::feature::cooling::get_id(); break; + case MatterThermostat::THERMOSTAT_SEQ_OP_HEATING: + case MatterThermostat::THERMOSTAT_SEQ_OP_HEATING_REHEAT: thermostatFeatures = cluster::thermostat::feature::heating::get_id(); break; + case MatterThermostat::THERMOSTAT_SEQ_OP_COOLING_HEATING: + case MatterThermostat::THERMOSTAT_SEQ_OP_COOLING_HEATING_REHEAT: + thermostatFeatures = cluster::thermostat::feature::cooling::get_id() | cluster::thermostat::feature::heating::get_id(); + break; + } + cluster::thermostat::create(endpoint, &(config->thermostat), CLUSTER_FLAG_SERVER, thermostatFeatures); + return ESP_OK; +} + +endpoint_t *create(node_t *node, config_t *config, uint8_t flags, void *priv_data) { + endpoint_t *endpoint = endpoint::create(node, flags, priv_data); + add(endpoint, config); + return endpoint; +} +} // namespace multi_mode_thermostat +} // namespace endpoint +} // namespace esp_matter + +bool MatterThermostat::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) { + bool ret = true; + if (!started) { + log_e("Matter Thermostat device has not begun."); + return false; + } + log_d("Thermostat Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32); + + if (cluster_id == Thermostat::Id) { + switch (attribute_id) { + case Thermostat::Attributes::SystemMode::Id: + if (_onChangeModeCB != NULL) { + ret &= _onChangeModeCB((ThermostatMode_t)val->val.u8); + } + if (_onChangeCB != NULL) { + ret &= _onChangeCB(); + } + if (ret == true) { + currentMode = (ThermostatMode_t)val->val.u8; + log_v("Thermostat Mode updated to %d", val->val.u8); + } + break; + case Thermostat::Attributes::LocalTemperature::Id: + if (_onChangeTemperatureCB != NULL) { + ret &= _onChangeTemperatureCB((float)val->val.i16 / 100.00); + } + if (_onChangeCB != NULL) { + ret &= _onChangeCB(); + } + if (ret == true) { + localTemperature = val->val.i16; + log_v("Local Temperature updated to %.01fC", (float)val->val.i16 / 100.00); + } + break; + case Thermostat::Attributes::OccupiedCoolingSetpoint::Id: + if (_onChangeCoolingSetpointCB != NULL) { + ret &= _onChangeCoolingSetpointCB((float)val->val.i16 / 100.00); + } + if (_onChangeCB != NULL) { + ret &= _onChangeCB(); + } + if (ret == true) { + coolingSetpointTemperature = val->val.i16; + log_v("Cooling Setpoint updated to %.01fC", (float)val->val.i16 / 100.00); + } + break; + case Thermostat::Attributes::OccupiedHeatingSetpoint::Id: + if (_onChangeHeatingSetpointCB != NULL) { + ret &= _onChangeHeatingSetpointCB((float)val->val.i16 / 100.00); + } + if (_onChangeCB != NULL) { + ret &= _onChangeCB(); + } + if (ret == true) { + heatingSetpointTemperature = val->val.i16; + log_v("Heating Setpoint updated to %.01fC", (float)val->val.i16 / 100.00); + } + break; + default: log_w("Unhandled Thermostat Attribute ID: %u", attribute_id); break; + } + } + return ret; +} + +MatterThermostat::MatterThermostat() {} + +MatterThermostat::~MatterThermostat() { + end(); +} + +bool MatterThermostat::begin(ControlSequenceOfOperation_t _controlSequence, ThermostatAutoMode_t _autoMode) { + ArduinoMatter::_init(); + + if (getEndPointId() != 0) { + log_e("Matter Thermostat with Endpoint Id %d device has already been created.", getEndPointId()); + return false; + } + + // check if auto mode is allowed with the control sequence of operation - only allowed for Cooling & Heating + if (_autoMode == THERMOSTAT_AUTO_MODE_ENABLED && _controlSequence != THERMOSTAT_SEQ_OP_COOLING_HEATING + && _controlSequence != THERMOSTAT_SEQ_OP_COOLING_HEATING_REHEAT) { + log_e("Thermostat in Auto Mode requires a Cooling & Heating Control Sequence of Operation."); + return false; + } + + const int16_t _localTemperature = 2000; // initial value to be automatically changed by the Matter Thermostat + const int16_t _coolingSetpointTemperature = 2400; // 24C cooling setpoint + const int16_t _heatingSetpointTemperature = 1600; // 16C heating setpoint + const ThermostatMode_t _currentMode = THERMOSTAT_MODE_OFF; + + multi_mode_thermostat::config_t thermostat_config; + thermostat_config.thermostat.control_sequence_of_operation = (uint8_t)_controlSequence; + thermostat_config.thermostat.cooling.occupied_cooling_setpoint = _coolingSetpointTemperature; + thermostat_config.thermostat.heating.occupied_heating_setpoint = _heatingSetpointTemperature; + thermostat_config.thermostat.system_mode = (uint8_t)_currentMode; + thermostat_config.thermostat.local_temperature = _localTemperature; + + // endpoint handles can be used to add/modify clusters + endpoint_t *endpoint = multi_mode_thermostat::create(node::get(), &thermostat_config, ENDPOINT_FLAG_NONE, (void *)this); + if (endpoint == nullptr) { + log_e("Failed to create Thermostat endpoint"); + return false; + } + if (_autoMode == THERMOSTAT_AUTO_MODE_ENABLED) { + cluster_t *cluster = cluster::get(endpoint, Thermostat::Id); + thermostat_config.thermostat.auto_mode.min_setpoint_dead_band = kDefaultDeadBand; // fixed by default to 2.5C + cluster::thermostat::feature::auto_mode::add(cluster, &thermostat_config.thermostat.auto_mode); + } + + controlSequence = _controlSequence; + autoMode = _autoMode; + coolingSetpointTemperature = _coolingSetpointTemperature; + heatingSetpointTemperature = _heatingSetpointTemperature; + localTemperature = _localTemperature; + currentMode = _currentMode; + + setEndPointId(endpoint::get_id(endpoint)); + log_i("Thermostat created with endpoint_id %d", getEndPointId()); + started = true; + return true; +} + +void MatterThermostat::end() { + started = false; +} + +bool MatterThermostat::setMode(ThermostatMode_t _mode) { + if (!started) { + log_e("Matter Thermostat device has not begun."); + return false; + } + + if (autoMode == THERMOSTAT_AUTO_MODE_DISABLED && _mode == THERMOSTAT_MODE_AUTO) { + log_e("Thermostat can't set Auto Mode."); + return false; + } + // check if the requested mode is valid based on the control sequence of operation + // no restrictions for OFF mode + if (_mode != THERMOSTAT_MODE_OFF) { + // check HEAT, COOL and AUTO modes + switch (controlSequence) { + case THERMOSTAT_SEQ_OP_COOLING: + case THERMOSTAT_SEQ_OP_COOLING_REHEAT: + if (_mode == THERMOSTAT_MODE_HEAT || _mode == THERMOSTAT_MODE_AUTO) { + break; + } + log_e("Invalid Thermostat Mode for Cooling Control Sequence of Operation."); + return false; + case THERMOSTAT_SEQ_OP_HEATING: + case THERMOSTAT_SEQ_OP_HEATING_REHEAT: + if (_mode == THERMOSTAT_MODE_COOL || _mode == THERMOSTAT_MODE_AUTO) { + break; + } + log_e("Invalid Thermostat Mode for Heating Control Sequence of Operation."); + return false; + default: + // compiler warning about not handling all enum values + break; + } + } + + // avoid processing if there was no change + if (currentMode == _mode) { + return true; + } + + esp_matter_attr_val_t modeVal = esp_matter_invalid(NULL); + if (!getAttributeVal(Thermostat::Id, Thermostat::Attributes::SystemMode::Id, &modeVal)) { + log_e("Failed to get Thermostat Mode Attribute."); + return false; + } + if (modeVal.val.u8 != _mode) { + modeVal.val.u8 = _mode; + bool ret; + ret = updateAttributeVal(Thermostat::Id, Thermostat::Attributes::SystemMode::Id, &modeVal); + if (!ret) { + log_e("Failed to update Thermostat Mode Attribute."); + return false; + } + currentMode = _mode; + } + log_v("Thermostat Mode set to %d", _mode); + + return true; +} + +bool MatterThermostat::setRawTemperature(int16_t _rawTemperature, uint32_t attribute_id, int16_t *internalValue) { + if (!started) { + log_e("Matter Thermostat device has not begun."); + return false; + } + + // avoid processing if there was no change + if (*internalValue == _rawTemperature) { + return true; + } + + esp_matter_attr_val_t temperatureVal = esp_matter_invalid(NULL); + if (!getAttributeVal(Thermostat::Id, attribute_id, &temperatureVal)) { + log_e("Failed to get Thermostat Temperature or Setpoint Attribute."); + return false; + } + if (temperatureVal.val.i16 != _rawTemperature) { + temperatureVal.val.i16 = _rawTemperature; + bool ret; + ret = updateAttributeVal(Thermostat::Id, attribute_id, &temperatureVal); + if (!ret) { + log_e("Failed to update Thermostat Temperature or Setpoint Attribute."); + return false; + } + *internalValue = _rawTemperature; + } + log_v("Temperature set to %.01fC", (float)_rawTemperature / 100.00); + + return true; +} + +bool MatterThermostat::setCoolingHeatingSetpoints(double _setpointHeatingTemperature, double _setpointCollingTemperature) { + // at least one of the setpoints must be valid + bool settingCooling = _setpointCollingTemperature != (float)0xffff; + bool settingHeating = _setpointHeatingTemperature != (float)0xffff; + if (!settingCooling && !settingHeating) { + log_e("Invalid Setpoints values. Set correctly at least one of them in Celsius."); + return false; + } + int16_t _rawHeatValue = static_cast(_setpointHeatingTemperature * 100.0f); + int16_t _rawCoolValue = static_cast(_setpointCollingTemperature * 100.0f); + + // check limits for the setpoints + if (settingHeating && (_rawHeatValue < kDefaultMinHeatSetpointLimit || _rawHeatValue > kDefaultMaxHeatSetpointLimit)) { + log_e( + "Invalid Heating Setpoint value: %.01fC - valid range %d..%d", _setpointHeatingTemperature, kDefaultMinHeatSetpointLimit / 100, + kDefaultMaxHeatSetpointLimit / 100 + ); + return false; + } + if (settingCooling && (_rawCoolValue < kDefaultMinCoolSetpointLimit || _rawCoolValue > kDefaultMaxCoolSetpointLimit)) { + log_e( + "Invalid Cooling Setpoint value: %.01fC - valid range %d..%d", _setpointCollingTemperature, kDefaultMinCoolSetpointLimit / 100, + kDefaultMaxCoolSetpointLimit / 100 + ); + return false; + } + + // AUTO mode requires both setpoints to be valid to each other and respect the deadband + if (currentMode == THERMOSTAT_MODE_AUTO) { +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR + float deadband = getDeadBand(); +#endif + // only setting Cooling Setpoint + if (settingCooling && !settingHeating && _rawCoolValue < (heatingSetpointTemperature + (kDefaultDeadBand * 10))) { + log_e( + "AutoMode :: Invalid Cooling Setpoint value: %.01fC - must be higher or equal than %.01fC", _setpointCollingTemperature, getHeatingSetpoint() + deadband + ); + return false; + } + // only setting Heating Setpoint + if (!settingCooling && settingHeating && _rawHeatValue > (coolingSetpointTemperature - (kDefaultDeadBand * 10))) { + log_e( + "AutoMode :: Invalid Heating Setpoint value: %.01fC - must be lower or equal than %.01fC", _setpointHeatingTemperature, getCoolingSetpoint() - deadband + ); + return false; + } + // setting both setpoints + if (settingCooling && settingHeating && (_rawCoolValue <= _rawHeatValue || _rawCoolValue - _rawHeatValue < kDefaultDeadBand * 10.0)) { + log_e( + "AutoMode :: Error - Heating Setpoint %.01fC must be lower than Cooling Setpoint %.01fC with a minimum difference of %0.1fC", + _setpointHeatingTemperature, _setpointCollingTemperature, deadband + ); + return false; + } + } + + bool ret = true; + if (settingCooling) { + ret &= setRawTemperature(_rawCoolValue, Thermostat::Attributes::OccupiedCoolingSetpoint::Id, &coolingSetpointTemperature); + } + if (settingHeating) { + ret &= setRawTemperature(_rawHeatValue, Thermostat::Attributes::OccupiedHeatingSetpoint::Id, &heatingSetpointTemperature); + } + return ret; +} + +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterThermostat.h b/libraries/Matter/src/MatterEndpoints/MatterThermostat.h new file mode 100644 index 00000000000..2d64bdf3b01 --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterThermostat.h @@ -0,0 +1,207 @@ +// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include +#include + +using namespace chip::app::Clusters; + +class MatterThermostat : public MatterEndPoint { +public: + // clang-format off + enum ControlSequenceOfOperation_t { + THERMOSTAT_SEQ_OP_COOLING = (uint8_t) Thermostat::ControlSequenceOfOperationEnum::kCoolingOnly, + THERMOSTAT_SEQ_OP_COOLING_REHEAT = (uint8_t) Thermostat::ControlSequenceOfOperationEnum::kCoolingWithReheat, + THERMOSTAT_SEQ_OP_HEATING = (uint8_t) Thermostat::ControlSequenceOfOperationEnum::kHeatingOnly, + THERMOSTAT_SEQ_OP_HEATING_REHEAT = (uint8_t) Thermostat::ControlSequenceOfOperationEnum::kHeatingWithReheat, + THERMOSTAT_SEQ_OP_COOLING_HEATING = (uint8_t) Thermostat::ControlSequenceOfOperationEnum::kCoolingAndHeating, + THERMOSTAT_SEQ_OP_COOLING_HEATING_REHEAT = (uint8_t) Thermostat::ControlSequenceOfOperationEnum::kCoolingAndHeatingWithReheat, + }; + + enum ThermostatMode_t { + THERMOSTAT_MODE_OFF = (uint8_t) Thermostat::SystemModeEnum::kOff, + THERMOSTAT_MODE_AUTO = (uint8_t) Thermostat::SystemModeEnum::kAuto, + THERMOSTAT_MODE_COOL = (uint8_t) Thermostat::SystemModeEnum::kCool, + THERMOSTAT_MODE_HEAT = (uint8_t) Thermostat::SystemModeEnum::kHeat, + THERMOSTAT_MODE_EMERGENCY_HEAT = (uint8_t) Thermostat::SystemModeEnum::kEmergencyHeat, + THERMOSTAT_MODE_PRECOOLING = (uint8_t) Thermostat::SystemModeEnum::kPrecooling, + THERMOSTAT_MODE_FAN_ONLY = (uint8_t) Thermostat::SystemModeEnum::kFanOnly, + THERMOSTAT_MODE_DRY = (uint8_t) Thermostat::SystemModeEnum::kDry, + THERMOSTAT_MODE_SLEEP = (uint8_t) Thermostat::SystemModeEnum::kSleep + }; + + enum ThermostatAutoMode_t { + THERMOSTAT_AUTO_MODE_DISABLED = (uint8_t) Thermostat::SystemModeEnum::kOff, + THERMOSTAT_AUTO_MODE_ENABLED = (uint8_t) Thermostat::SystemModeEnum::kAuto, + }; + // clang-format on + + MatterThermostat(); + ~MatterThermostat(); + // begin Matter Thermostat endpoint with initial Operation Mode + bool begin(ControlSequenceOfOperation_t controlSequence = THERMOSTAT_SEQ_OP_COOLING, ThermostatAutoMode_t autoMode = THERMOSTAT_AUTO_MODE_DISABLED); + // this will stop processing Thermostat Matter events + void end(); + + // set the Thermostat Mode + bool setMode(ThermostatMode_t mode); + // get the Thermostat Mode + ThermostatMode_t getMode() { + return currentMode; + } + // returns a friendly string for the Fan Mode + static const char *getThermostatModeString(uint8_t mode) { + return thermostatModeString[mode]; + } + + // get the Thermostat Control Sequence of Operation + ControlSequenceOfOperation_t getControlSequence() { + return controlSequence; + } + + // get the minimum heating setpoint in 1/100th of a Celsio degree + float getMinHeatSetpoint() { + return (float)kDefaultMinHeatSetpointLimit / 100.00; + } + // get the maximum heating setpoint in 1/100th of a Celsio degree + float getMaxHeatSetpoint() { + return (float)kDefaultMaxHeatSetpointLimit / 100.00; + } + // get the minimum cooling setpoint in 1/100th of a Celsio degree + float getMinCoolSetpoint() { + return (float)kDefaultMinCoolSetpointLimit / 100.00; + } + // get the maximum cooling setpoint in 1/100th of a Celsio degree + float getMaxCoolSetpoint() { + return (float)kDefaultMaxCoolSetpointLimit / 100.00; + } + // get the deadband in 1/10th of a Celsio degree + float getDeadBand() { + return (float)kDefaultDeadBand / 10.00; + } + + // generic function for setting the cooling and heating setpoints - checks if the setpoints are valid + // it can be used to set both setpoints at the same time or only one of them, by setting the other to (float)0xffff + // Heating Setpoint must be lower than Cooling Setpoint + // When using AUTO mode the Cooling Setpoint must be higher than Heating Setpoint by at least the 2.5C (deadband) + // Thermostat Matter Server will enforce those rules and the Max/Min setpoints limits as in the Matter Specification + bool setCoolingHeatingSetpoints(double _setpointHeatingTemperature, double _setpointCollingTemperature); + + // set the heating setpoint in 1/100th of a Celsio degree + bool setHeatingSetpoint(double _setpointHeatingTemperature) { + return setCoolingHeatingSetpoints((double)0xffff, _setpointHeatingTemperature); + } + // get the heating setpoint in 1/100th of a Celsio degree + double getHeatingSetpoint() { + return heatingSetpointTemperature / 100.0; + } + // set the cooling setpoint in 1/100th of a Celsio degree + bool setCoolingSetpoint(double _setpointCollingTemperature) { + return setCoolingHeatingSetpoints(_setpointCollingTemperature, (double)0xffff); + } + // get the cooling setpoint in 1/100th of a Celsio degree + double getCoolingSetpoint() { + return coolingSetpointTemperature / 100.0; + } + + // set the local Thermostat temperature in Celsio degrees + bool setLocalTemperature(double temperature) { + // stores up to 1/100th of a Celsio degree precision + int16_t rawValue = static_cast(temperature * 100.0f); + return setRawTemperature(rawValue, Thermostat::Attributes::LocalTemperature::Id, &localTemperature); + } + // returns the local Thermostat float temperature with 1/100th of a Celsio degree precision + double getLocalTemperature() { + return (double)localTemperature / 100.0; + } + + // User Callback for whenever the Thermostat Mode is changed by the Matter Controller + using EndPointModeCB = std::function; + void onChangeMode(EndPointModeCB onChangeCB) { + _onChangeModeCB = onChangeCB; + } + + // User Callback for whenever the Local Temperature is changed by the Matter Controller + using EndPointTemperatureCB = std::function; + void onChangeLocalTemperature(EndPointTemperatureCB onChangeCB) { + _onChangeTemperatureCB = onChangeCB; + } + + // User Callback for whenever the Cooling or Heating Setpoint is changed by the Matter Controller + using EndPointCoolingSetpointCB = std::function; + void onChangeCoolingSetpoint(EndPointCoolingSetpointCB onChangeCB) { + _onChangeCoolingSetpointCB = onChangeCB; + } + + // User Callback for whenever the Cooling or Heating Setpoint is changed by the Matter Controller + using EndPointHeatingSetpointCB = std::function; + void onChangeHeatingSetpoint(EndPointHeatingSetpointCB onChangeCB) { + _onChangeHeatingSetpointCB = onChangeCB; + } + + // User Callback for whenever any parameter is changed by the Matter Controller + // Main parameters are Thermostat Mode, Local Temperature, Cooling Setpoint and Heating Setpoint + // Those can be obtained using getMode(), getTemperature(), getCoolingSetpoint() and getHeatingSetpoint() + using EndPointCB = std::function; + void onChange(EndPointCB onChangeCB) { + _onChangeCB = onChangeCB; + } + + // this function is called by Matter internal event processor. It could be overwritten by the application, if necessary. + bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val); + +protected: + bool started = false; + // implementation keeps temperature in 1/100th of a Celsio degree + int16_t coolingSetpointTemperature = 2400; // 24C cooling setpoint + int16_t localTemperature = 2000; // 20C local temperature + int16_t heatingSetpointTemperature = 1600; // 16C heating setpoint + + ThermostatMode_t currentMode = THERMOSTAT_MODE_OFF; + ControlSequenceOfOperation_t controlSequence = THERMOSTAT_SEQ_OP_COOLING; + ThermostatAutoMode_t autoMode = THERMOSTAT_AUTO_MODE_DISABLED; + + EndPointModeCB _onChangeModeCB = NULL; + EndPointTemperatureCB _onChangeTemperatureCB = NULL; + EndPointCoolingSetpointCB _onChangeCoolingSetpointCB = NULL; + EndPointHeatingSetpointCB _onChangeHeatingSetpointCB = NULL; + EndPointCB _onChangeCB = NULL; + + // internal function to set the raw temperature value (Matter Cluster) + bool setRawTemperature(int16_t _rawTemperature, uint32_t attribute_id, int16_t *internalValue); + + // clang-format off + // Default Thermostat values - can't be changed - defined in the Thermostat Cluster Server code + static const int16_t kDefaultAbsMinHeatSetpointLimit = 700; // 7C (44.5 F) + static const int16_t kDefaultMinHeatSetpointLimit = 700; // 7C (44.5 F) + static const int16_t kDefaultAbsMaxHeatSetpointLimit = 3000; // 30C (86 F) + static const int16_t kDefaultMaxHeatSetpointLimit = 3000; // 30C (86 F) + + static const int16_t kDefaultAbsMinCoolSetpointLimit = 1600; // 16C (61 F) + static const int16_t kDefaultMinCoolSetpointLimit = 1600; // 16C (61 F) + static const int16_t kDefaultAbsMaxCoolSetpointLimit = 3200; // 32C (90 F) + static const int16_t kDefaultMaxCoolSetpointLimit = 3200; // 32C (90 F) + + static const int8_t kDefaultDeadBand = 25; // 2.5C when in AUTO mode + // clang-format on + + // string helper for the THERMOSTAT MODE + static const char *thermostatModeString[5]; +}; +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ From 10d0bf8ea74a8c51a34ebb1762a1a9082c7cd0fb Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 7 Jan 2025 12:01:55 +0200 Subject: [PATCH 12/60] ci: Allow push to the component registry from a given git ref (#10757) Co-authored-by: Sergei Silnov Co-authored-by: Rodrigo Garcia --- .github/workflows/upload-idf-component.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/upload-idf-component.yml b/.github/workflows/upload-idf-component.yml index 8faddef7e5f..5ce4562c01a 100644 --- a/.github/workflows/upload-idf-component.yml +++ b/.github/workflows/upload-idf-component.yml @@ -4,7 +4,10 @@ on: workflow_dispatch: inputs: tag: - description: 'Tag to push to the component registry' + description: 'Version to push to the component registry' + required: true + git_ref: + description: 'Git ref with the source to push to the component registry' required: true workflow_run: workflows: ["ESP32 Arduino Release"] @@ -44,7 +47,7 @@ jobs: - uses: actions/checkout@v4 with: - ref: ${{ env.RELEASE_TAG }} + ref: ${{ inputs.git_ref || env.RELEASE_TAG }} submodules: "recursive" - name: Upload components to the component registry From 5aaccd8f890ca5448975b08179d4432083958721 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Tue, 7 Jan 2025 07:02:30 -0300 Subject: [PATCH 13/60] feat(matter): fixes matter temperature sensor endpoint to indicate celsius as measure unit (#10759) --- .../MatterTemperatureSensor/MatterTemperatureSensor.ino | 6 +++--- .../src/MatterEndpoints/MatterTemperatureSensor.cpp | 2 +- .../Matter/src/MatterEndpoints/MatterTemperatureSensor.h | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/Matter/examples/MatterTemperatureSensor/MatterTemperatureSensor.ino b/libraries/Matter/examples/MatterTemperatureSensor/MatterTemperatureSensor.ino index e8513f12aaa..a2866e4655e 100644 --- a/libraries/Matter/examples/MatterTemperatureSensor/MatterTemperatureSensor.ino +++ b/libraries/Matter/examples/MatterTemperatureSensor/MatterTemperatureSensor.ino @@ -42,7 +42,7 @@ const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s // Simulate a temperature sensor - add your preferred temperature sensor library code here float getSimulatedTemperature() { // The Endpoint implementation keeps an int16_t as internal value information, - // which stores data in 1/100th of any temperature unit + // which stores data in 1/100th Celsius. static float simulatedTempHWSensor = -10.0; // it will increase from -10C to 10C in 0.5C steps to simulate a temperature sensor @@ -70,7 +70,7 @@ void setup() { Serial.println(); // set initial temperature sensor measurement - // Simulated Sensor - it shall initially print -25 degrees and then move to the -10 to 10 range + // Simulated Sensor - it shall initially print -25C and then move to the -10C to 10C range SimulatedTemperatureSensor.begin(-25.00); // Matter beginning - Last step, after all EndPoints are initialized @@ -102,7 +102,7 @@ void loop() { // Print the current temperature value every 5s if (!(timeCounter++ % 10)) { // delaying for 500ms x 10 = 5s // Print the current temperature value - Serial.printf("Current Temperature is %.02f \r\n", SimulatedTemperatureSensor.getTemperature()); + Serial.printf("Current Temperature is %.02fC\r\n", SimulatedTemperatureSensor.getTemperature()); // Update Temperature from the (Simulated) Hardware Sensor // Matter APP shall display the updated temperature percent SimulatedTemperatureSensor.setTemperature(getSimulatedTemperature()); diff --git a/libraries/Matter/src/MatterEndpoints/MatterTemperatureSensor.cpp b/libraries/Matter/src/MatterEndpoints/MatterTemperatureSensor.cpp index 903e6b98d98..863f86386c1 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterTemperatureSensor.cpp +++ b/libraries/Matter/src/MatterEndpoints/MatterTemperatureSensor.cpp @@ -96,7 +96,7 @@ bool MatterTemperatureSensor::setRawTemperature(int16_t _rawTemperature) { } rawTemperature = _rawTemperature; } - log_v("Temperature Sensor set to %.02f Degrees", (float)_rawTemperature / 100.00); + log_v("Temperature Sensor set to %.02fC", (float)_rawTemperature / 100.00); return true; } diff --git a/libraries/Matter/src/MatterEndpoints/MatterTemperatureSensor.h b/libraries/Matter/src/MatterEndpoints/MatterTemperatureSensor.h index 27c61fdb978..3be6101166c 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterTemperatureSensor.h +++ b/libraries/Matter/src/MatterEndpoints/MatterTemperatureSensor.h @@ -23,7 +23,7 @@ class MatterTemperatureSensor : public MatterEndPoint { public: MatterTemperatureSensor(); ~MatterTemperatureSensor(); - // begin Matter Temperature Sensor endpoint with initial float temperature + // begin Matter Temperature Sensor endpoint with initial float temperature in Celsius bool begin(double temperature = 0.00) { return begin(static_cast(temperature * 100.0f)); } @@ -32,11 +32,11 @@ class MatterTemperatureSensor : public MatterEndPoint { // set the reported raw temperature bool setTemperature(double temperature) { - // stores up to 1/100th of a degree precision + // stores up to 1/100th Celsius precision int16_t rawValue = static_cast(temperature * 100.0f); return setRawTemperature(rawValue); } - // returns the reported float temperature with 1/100th of a degree precision + // returns the reported float temperature with 1/100th Celsius precision double getTemperature() { return (double)rawTemperature / 100.0; } @@ -55,7 +55,7 @@ class MatterTemperatureSensor : public MatterEndPoint { protected: bool started = false; - // implementation keeps temperature in 1/100th of a degree, any temperature unit + // implementation keeps temperature in 1/100th Celsius x 100 (int16_t) normalized value int16_t rawTemperature = 0; // internal function to set the raw temperature value (Matter Cluster) bool setRawTemperature(int16_t _rawTemperature); From 9eee1dd559ff66be6e6421472c1618b75318674c Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 7 Jan 2025 07:02:50 -0300 Subject: [PATCH 14/60] ci(dangerjs): Fix and update settings (#10770) --- .github/workflows/dangerjs.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/dangerjs.yml b/.github/workflows/dangerjs.yml index f4bdad3c16e..8fa7b737317 100644 --- a/.github/workflows/dangerjs.yml +++ b/.github/workflows/dangerjs.yml @@ -20,5 +20,8 @@ jobs: uses: espressif/shared-github-dangerjs@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + instructions-cla-link: "https://cla-assistant.io/espressif/arduino-esp32" + instructions-contributions-file: "docs/en/contributing.rst" rule-max-commits: "false" commit-messages-min-summary-length: "10" From 5f44c4ebc5f35a583004b1e45d5c122da95a567c Mon Sep 17 00:00:00 2001 From: Mattia Pennasilico Date: Tue, 7 Jan 2025 11:03:11 +0100 Subject: [PATCH 15/60] fix(client): Restore Arduino Client API (#10776) --- cores/esp32/Client.h | 2 -- libraries/Network/src/NetworkClient.h | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cores/esp32/Client.h b/cores/esp32/Client.h index 4ea10d070fb..ab013612401 100644 --- a/cores/esp32/Client.h +++ b/cores/esp32/Client.h @@ -26,9 +26,7 @@ class Client : public Stream { public: virtual int connect(IPAddress ip, uint16_t port) = 0; - virtual int connect(IPAddress ip, uint16_t port, int32_t timeout) = 0; virtual int connect(const char *host, uint16_t port) = 0; - virtual int connect(const char *host, uint16_t port, int32_t timeout) = 0; virtual size_t write(uint8_t) = 0; virtual size_t write(const uint8_t *buf, size_t size) = 0; virtual int available() = 0; diff --git a/libraries/Network/src/NetworkClient.h b/libraries/Network/src/NetworkClient.h index 1baf8b36139..52df868a0b0 100644 --- a/libraries/Network/src/NetworkClient.h +++ b/libraries/Network/src/NetworkClient.h @@ -29,6 +29,9 @@ class NetworkClientRxBuffer; class ESPLwIPClient : public Client { public: virtual void setConnectionTimeout(uint32_t milliseconds) = 0; + using Client::connect; + virtual int connect(IPAddress ip, uint16_t port, int32_t timeout) = 0; + virtual int connect(const char *host, uint16_t port, int32_t timeout) = 0; }; class NetworkClient : public ESPLwIPClient { From 2cb08193d80149dac3f5edfe4a1b87f6f416ad7c Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Tue, 7 Jan 2025 07:03:31 -0300 Subject: [PATCH 16/60] feat(matter): adds esp_matter:: namespace to attribute_t (#10807) * feat(matter): adds esp_matter:: namespace to attribute_t * feat(matter): adds esp_matter:: namespace to attribute_t * feat(matter): adds esp_matter:: namespace to attribute_t * feat(matter): adds esp_matter:: namespace to attribute_t * feat(matter): adds esp_matter:: namespace to attribute_t * feat(matter): adds esp_matter:: namespace to attribute_t --- libraries/Matter/src/MatterEndPoint.h | 8 ++++---- .../Matter/src/MatterEndpoints/MatterColorLight.cpp | 6 +++--- .../MatterEndpoints/MatterColorTemperatureLight.cpp | 10 +++++----- .../Matter/src/MatterEndpoints/MatterDimmableLight.cpp | 6 +++--- .../src/MatterEndpoints/MatterEnhancedColorLight.cpp | 10 +++++----- .../Matter/src/MatterEndpoints/MatterOnOffLight.cpp | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/libraries/Matter/src/MatterEndPoint.h b/libraries/Matter/src/MatterEndPoint.h index 6f99aa7cd33..5baa4747d18 100644 --- a/libraries/Matter/src/MatterEndPoint.h +++ b/libraries/Matter/src/MatterEndPoint.h @@ -38,7 +38,7 @@ class MatterEndPoint { } // helper functions for attribute manipulation - attribute_t *getAttribute(uint32_t cluster_id, uint32_t attribute_id) { + esp_matter::attribute_t *getAttribute(uint32_t cluster_id, uint32_t attribute_id) { if (endpoint_id == 0) { log_e("Endpoint ID is not set"); return NULL; @@ -53,7 +53,7 @@ class MatterEndPoint { log_e("Cluster [%d] not found", cluster_id); return NULL; } - attribute_t *attribute = attribute::get(cluster, attribute_id); + esp_matter::attribute_t *attribute = attribute::get(cluster, attribute_id); if (attribute == NULL) { log_e("Attribute [%d] not found", attribute_id); return NULL; @@ -63,7 +63,7 @@ class MatterEndPoint { // get the value of an attribute from its cluster id and attribute it bool getAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal) { - attribute_t *attribute = getAttribute(cluster_id, attribute_id); + esp_matter::attribute_t *attribute = getAttribute(cluster_id, attribute_id); if (attribute == NULL) { return false; } @@ -77,7 +77,7 @@ class MatterEndPoint { // set the value of an attribute from its cluster id and attribute it bool setAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal) { - attribute_t *attribute = getAttribute(cluster_id, attribute_id); + esp_matter::attribute_t *attribute = getAttribute(cluster_id, attribute_id); if (attribute == NULL) { return false; } diff --git a/libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp b/libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp index eaaf0bf2ffe..39d79e86325 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp +++ b/libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp @@ -194,7 +194,7 @@ bool MatterColorLight::begin(bool initialState, espHsvColor_t _colorHSV) { /* Mark deferred persistence for some attributes that might be changed rapidly */ cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id); - attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id); + esp_matter::attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id); attribute::set_deferred_persistence(current_level_attribute); started = true; @@ -220,7 +220,7 @@ bool MatterColorLight::setOnOff(bool newState) { endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); cluster_t *cluster = cluster::get(endpoint, OnOff::Id); - attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id); esp_matter_attr_val_t val = esp_matter_invalid(NULL); attribute::get_val(attribute, &val); @@ -271,7 +271,7 @@ bool MatterColorLight::setColorHSV(espHsvColor_t _hsvColor) { endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); cluster_t *cluster = cluster::get(endpoint, ColorControl::Id); // update hue - attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::CurrentHue::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::CurrentHue::Id); esp_matter_attr_val_t val = esp_matter_invalid(NULL); attribute::get_val(attribute, &val); if (val.val.u8 != colorHSV.h) { diff --git a/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp b/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp index 5ef69749bb1..3c4fccfa046 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp +++ b/libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp @@ -122,11 +122,11 @@ bool MatterColorTemperatureLight::begin(bool initialState, uint8_t brightness, u /* Mark deferred persistence for some attributes that might be changed rapidly */ cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id); - attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id); + esp_matter::attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id); attribute::set_deferred_persistence(current_level_attribute); cluster_t *color_control_cluster = cluster::get(endpoint, ColorControl::Id); - attribute_t *color_temp_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::ColorTemperatureMireds::Id); + esp_matter::attribute_t *color_temp_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::ColorTemperatureMireds::Id); attribute::set_deferred_persistence(color_temp_attribute); started = true; @@ -152,7 +152,7 @@ bool MatterColorTemperatureLight::setOnOff(bool newState) { endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); cluster_t *cluster = cluster::get(endpoint, OnOff::Id); - attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id); esp_matter_attr_val_t val = esp_matter_invalid(NULL); attribute::get_val(attribute, &val); @@ -193,7 +193,7 @@ bool MatterColorTemperatureLight::setBrightness(uint8_t newBrightness) { endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); cluster_t *cluster = cluster::get(endpoint, LevelControl::Id); - attribute_t *attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id); esp_matter_attr_val_t val = esp_matter_invalid(NULL); attribute::get_val(attribute, &val); @@ -224,7 +224,7 @@ bool MatterColorTemperatureLight::setColorTemperature(uint16_t newTemperature) { endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); cluster_t *cluster = cluster::get(endpoint, ColorControl::Id); - attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::ColorTemperatureMireds::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::ColorTemperatureMireds::Id); esp_matter_attr_val_t val = esp_matter_invalid(NULL); attribute::get_val(attribute, &val); diff --git a/libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp b/libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp index 9f6f872ca3e..5167cf1f21c 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp +++ b/libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp @@ -101,7 +101,7 @@ bool MatterDimmableLight::begin(bool initialState, uint8_t brightness) { /* Mark deferred persistence for some attributes that might be changed rapidly */ cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id); - attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id); + esp_matter::attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id); attribute::set_deferred_persistence(current_level_attribute); started = true; @@ -127,7 +127,7 @@ bool MatterDimmableLight::setOnOff(bool newState) { endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); cluster_t *cluster = cluster::get(endpoint, OnOff::Id); - attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id); esp_matter_attr_val_t val = esp_matter_invalid(NULL); attribute::get_val(attribute, &val); @@ -168,7 +168,7 @@ bool MatterDimmableLight::setBrightness(uint8_t newBrightness) { endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); cluster_t *cluster = cluster::get(endpoint, LevelControl::Id); - attribute_t *attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id); esp_matter_attr_val_t val = esp_matter_invalid(NULL); attribute::get_val(attribute, &val); diff --git a/libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.cpp b/libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.cpp index 022e62654df..9b245fb9408 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.cpp +++ b/libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.cpp @@ -214,7 +214,7 @@ bool MatterEnhancedColorLight::begin(bool initialState, espHsvColor_t _colorHSV, /* Mark deferred persistence for some attributes that might be changed rapidly */ cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id); - attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id); + esp_matter::attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id); attribute::set_deferred_persistence(current_level_attribute); started = true; @@ -240,7 +240,7 @@ bool MatterEnhancedColorLight::setOnOff(bool newState) { endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); cluster_t *cluster = cluster::get(endpoint, OnOff::Id); - attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id); esp_matter_attr_val_t val = esp_matter_invalid(NULL); attribute::get_val(attribute, &val); @@ -281,7 +281,7 @@ bool MatterEnhancedColorLight::setBrightness(uint8_t newBrightness) { endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); cluster_t *cluster = cluster::get(endpoint, LevelControl::Id); - attribute_t *attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id); esp_matter_attr_val_t val = esp_matter_invalid(NULL); attribute::get_val(attribute, &val); @@ -312,7 +312,7 @@ bool MatterEnhancedColorLight::setColorTemperature(uint16_t newTemperature) { endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); cluster_t *cluster = cluster::get(endpoint, ColorControl::Id); - attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::ColorTemperatureMireds::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::ColorTemperatureMireds::Id); esp_matter_attr_val_t val = esp_matter_invalid(NULL); attribute::get_val(attribute, &val); @@ -353,7 +353,7 @@ bool MatterEnhancedColorLight::setColorHSV(espHsvColor_t _hsvColor) { endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); cluster_t *cluster = cluster::get(endpoint, ColorControl::Id); // update hue - attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::CurrentHue::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, ColorControl::Attributes::CurrentHue::Id); esp_matter_attr_val_t val = esp_matter_invalid(NULL); attribute::get_val(attribute, &val); if (val.val.u8 != colorHSV.h) { diff --git a/libraries/Matter/src/MatterEndpoints/MatterOnOffLight.cpp b/libraries/Matter/src/MatterEndpoints/MatterOnOffLight.cpp index 3faba821528..f400390f9a7 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterOnOffLight.cpp +++ b/libraries/Matter/src/MatterEndpoints/MatterOnOffLight.cpp @@ -108,7 +108,7 @@ bool MatterOnOffLight::setOnOff(bool newState) { endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id); cluster_t *cluster = cluster::get(endpoint, OnOff::Id); - attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id); esp_matter_attr_val_t val = esp_matter_invalid(NULL); attribute::get_val(attribute, &val); From ed9a5d876d4da63fd9223b1d4280678dee91c977 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 7 Jan 2025 12:03:55 +0200 Subject: [PATCH 17/60] fix(udp): Add missing LwIP locks to init multicast (#10813) * fix(udp): Add missing LwIP locks to init multicast * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- libraries/AsyncUDP/src/AsyncUDP.cpp | 32 ++++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/libraries/AsyncUDP/src/AsyncUDP.cpp b/libraries/AsyncUDP/src/AsyncUDP.cpp index 48714bce5c5..f44cc839c97 100644 --- a/libraries/AsyncUDP/src/AsyncUDP.cpp +++ b/libraries/AsyncUDP/src/AsyncUDP.cpp @@ -601,76 +601,84 @@ static esp_err_t joinMulticastGroup(const ip_addr_t *addr, bool join, tcpip_adap return ESP_ERR_INVALID_ARG; } netif = (struct netif *)nif; + UDP_MUTEX_LOCK(); #if CONFIG_LWIP_IPV6 if (addr->type == IPADDR_TYPE_V4) { if (join) { if (igmp_joingroup_netif(netif, (const ip4_addr *)&(addr->u_addr.ip4))) { - return ESP_ERR_INVALID_STATE; + goto igmp_fail; } } else { if (igmp_leavegroup_netif(netif, (const ip4_addr *)&(addr->u_addr.ip4))) { - return ESP_ERR_INVALID_STATE; + goto igmp_fail; } } } else { if (join) { if (mld6_joingroup_netif(netif, &(addr->u_addr.ip6))) { - return ESP_ERR_INVALID_STATE; + goto igmp_fail; } } else { if (mld6_leavegroup_netif(netif, &(addr->u_addr.ip6))) { - return ESP_ERR_INVALID_STATE; + goto igmp_fail; } } } #else if (join) { if (igmp_joingroup_netif(netif, (const ip4_addr *)(addr))) { - return ESP_ERR_INVALID_STATE; + goto igmp_fail; } } else { if (igmp_leavegroup_netif(netif, (const ip4_addr *)(addr))) { - return ESP_ERR_INVALID_STATE; + goto igmp_fail; } } #endif + UDP_MUTEX_UNLOCK(); } else { + UDP_MUTEX_LOCK(); #if CONFIG_LWIP_IPV6 if (addr->type == IPADDR_TYPE_V4) { if (join) { if (igmp_joingroup((const ip4_addr *)IP4_ADDR_ANY, (const ip4_addr *)&(addr->u_addr.ip4))) { - return ESP_ERR_INVALID_STATE; + goto igmp_fail; } } else { if (igmp_leavegroup((const ip4_addr *)IP4_ADDR_ANY, (const ip4_addr *)&(addr->u_addr.ip4))) { - return ESP_ERR_INVALID_STATE; + goto igmp_fail; } } } else { if (join) { if (mld6_joingroup((const ip6_addr *)IP6_ADDR_ANY, &(addr->u_addr.ip6))) { - return ESP_ERR_INVALID_STATE; + goto igmp_fail; } } else { if (mld6_leavegroup((const ip6_addr *)IP6_ADDR_ANY, &(addr->u_addr.ip6))) { - return ESP_ERR_INVALID_STATE; + goto igmp_fail; } } } #else if (join) { if (igmp_joingroup((const ip4_addr *)IP4_ADDR_ANY, (const ip4_addr *)(addr))) { - return ESP_ERR_INVALID_STATE; + goto igmp_fail; } } else { if (igmp_leavegroup((const ip4_addr *)IP4_ADDR_ANY, (const ip4_addr *)(addr))) { - return ESP_ERR_INVALID_STATE; + goto igmp_fail; } } #endif + UDP_MUTEX_UNLOCK(); } return ESP_OK; + +igmp_fail: + UDP_MUTEX_UNLOCK(); + return ESP_ERR_INVALID_STATE; } bool AsyncUDP::listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl, tcpip_adapter_if_t tcpip_if) { From 30b97d1f9194da098d01548cb24d3c1af481e48f Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 7 Jan 2025 12:04:20 +0200 Subject: [PATCH 18/60] fix(camera): Update resolutions and remove face detection (#10814) * fix(camera): Update resolutions and remove face detection * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../Camera/CameraWebServer/camera_index.h | 1903 ++++++++--------- 1 file changed, 938 insertions(+), 965 deletions(-) diff --git a/libraries/ESP32/examples/Camera/CameraWebServer/camera_index.h b/libraries/ESP32/examples/Camera/CameraWebServer/camera_index.h index 040f1ec5267..07e2d661821 100644 --- a/libraries/ESP32/examples/Camera/CameraWebServer/camera_index.h +++ b/libraries/ESP32/examples/Camera/CameraWebServer/camera_index.h @@ -1,971 +1,944 @@ - -//File: index_ov2640.html.gz, Size: 6787 -#define index_ov2640_html_gz_len 6787 -const uint8_t index_ov2640_html_gz[] = { - 0x1F, 0x8B, 0x08, 0x08, 0x23, 0xFC, 0x69, 0x5E, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x6F, 0x76, 0x32, 0x36, 0x34, 0x30, 0x2E, 0x68, 0x74, 0x6D, - 0x6C, 0x00, 0xED, 0x3D, 0x6B, 0x73, 0xDB, 0x46, 0x92, 0xDF, 0xFD, 0x2B, 0x60, 0x24, 0x6B, 0x92, 0x25, 0x92, 0x22, 0x29, 0x4A, 0x96, 0x15, 0x89, 0x3E, 0x5B, - 0x96, 0x1F, 0xB5, 0x76, 0xE2, 0xB5, 0x12, 0xC7, 0x5B, 0xA9, 0x2D, 0x07, 0x04, 0x86, 0x24, 0x62, 0x10, 0xE0, 0x02, 0xA0, 0x48, 0x26, 0xA5, 0xDF, 0x71, 0x3F, - 0xE8, 0xFE, 0xD8, 0x75, 0xCF, 0x03, 0x18, 0x00, 0x83, 0x07, 0x49, 0x89, 0xF4, 0xFA, 0x8E, 0x4E, 0x45, 0x78, 0x4C, 0xF7, 0xF4, 0xBB, 0x7B, 0x66, 0x30, 0xC0, - 0xF9, 0x43, 0xCB, 0x33, 0xC3, 0xD5, 0x8C, 0x68, 0x93, 0x70, 0xEA, 0x0C, 0x1E, 0x9C, 0xB3, 0x3F, 0x1A, 0xFC, 0xCE, 0x27, 0xC4, 0xB0, 0xD8, 0x21, 0x3D, 0x9D, - 0x92, 0xD0, 0xD0, 0xCC, 0x89, 0xE1, 0x07, 0x24, 0xBC, 0xD0, 0xE7, 0xE1, 0xA8, 0x75, 0xAA, 0xA7, 0x6F, 0xBB, 0xC6, 0x94, 0x5C, 0xE8, 0x37, 0x36, 0x59, 0xCC, - 0x3C, 0x3F, 0xD4, 0x35, 0xD3, 0x73, 0x43, 0xE2, 0x42, 0xF3, 0x85, 0x6D, 0x85, 0x93, 0x0B, 0x8B, 0xDC, 0xD8, 0x26, 0x69, 0xD1, 0x93, 0xA6, 0xED, 0xDA, 0xA1, - 0x6D, 0x38, 0xAD, 0xC0, 0x34, 0x1C, 0x72, 0xD1, 0x95, 0x71, 0x85, 0x76, 0xE8, 0x90, 0xC1, 0xD5, 0xF5, 0xFB, 0xA3, 0x9E, 0xF6, 0xD3, 0xC7, 0x5E, 0xFF, 0xA4, - 0x73, 0x7E, 0xC8, 0xAE, 0xC5, 0x6D, 0x82, 0x70, 0x25, 0x9F, 0xE3, 0x6F, 0xE8, 0x59, 0x2B, 0xED, 0xAF, 0xC4, 0x25, 0xFC, 0x8D, 0x80, 0x88, 0xD6, 0xC8, 0x98, - 0xDA, 0xCE, 0xEA, 0x4C, 0x7B, 0xE6, 0x43, 0x9F, 0xCD, 0xD7, 0xC4, 0xB9, 0x21, 0xA1, 0x6D, 0x1A, 0xCD, 0xC0, 0x70, 0x83, 0x56, 0x40, 0x7C, 0x7B, 0xF4, 0x43, - 0x06, 0x70, 0x68, 0x98, 0x5F, 0xC6, 0xBE, 0x37, 0x77, 0xAD, 0x33, 0xED, 0xBB, 0xEE, 0x29, 0xFE, 0xCB, 0x36, 0x32, 0x3D, 0xC7, 0xF3, 0xE1, 0xFE, 0xD5, 0x4B, - 0xFC, 0x97, 0xBD, 0x4F, 0x7B, 0x0F, 0xEC, 0x3F, 0xC9, 0x99, 0xD6, 0x3D, 0x99, 0x2D, 0x13, 0xF7, 0x6F, 0x1F, 0x24, 0x4E, 0x27, 0xBD, 0x3C, 0xEA, 0x39, 0xFC, - 0x69, 0x31, 0x7C, 0x40, 0xCC, 0xD0, 0xF6, 0xDC, 0xF6, 0xD4, 0xB0, 0x5D, 0x05, 0x26, 0xCB, 0x0E, 0x66, 0x8E, 0x01, 0x32, 0x18, 0x39, 0xA4, 0x10, 0xCF, 0x77, - 0x53, 0xE2, 0xCE, 0x9B, 0x25, 0xD8, 0x10, 0x49, 0xCB, 0xB2, 0x7D, 0xD6, 0xEA, 0x0C, 0xE5, 0x30, 0x9F, 0xBA, 0xA5, 0x68, 0x8B, 0xE8, 0x72, 0x3D, 0x97, 0x28, - 0x04, 0x88, 0x1D, 0x2D, 0x7C, 0x63, 0x86, 0x0D, 0xF0, 0x6F, 0xB6, 0xC9, 0xD4, 0x76, 0x99, 0x51, 0x9D, 0x69, 0x47, 0xFD, 0xCE, 0x6C, 0x59, 0xA2, 0xCA, 0xA3, - 0x13, 0xFC, 0x97, 0x6D, 0x34, 0x33, 0x2C, 0xCB, 0x76, 0xC7, 0x67, 0xDA, 0xA9, 0x12, 0x85, 0xE7, 0x5B, 0xC4, 0x6F, 0xF9, 0x86, 0x65, 0xCF, 0x83, 0x33, 0xAD, - 0xAF, 0x6A, 0x33, 0x35, 0xFC, 0x31, 0xD0, 0x12, 0x7A, 0x40, 0x6C, 0xAB, 0xAB, 0xA4, 0x84, 0x37, 0xF1, 0xED, 0xF1, 0x24, 0x04, 0x95, 0x66, 0xDA, 0xA4, 0x85, - 0xC6, 0x5D, 0xA8, 0x4C, 0x9F, 0x85, 0x72, 0x53, 0x4B, 0xCD, 0x70, 0xEC, 0xB1, 0xDB, 0xB2, 0x43, 0x32, 0x05, 0x76, 0x82, 0xD0, 0x27, 0xA1, 0x39, 0x29, 0x22, - 0x65, 0x64, 0x8F, 0xE7, 0x3E, 0x51, 0x10, 0x12, 0xC9, 0xAD, 0x80, 0x61, 0xB8, 0x99, 0xBD, 0xD5, 0x5A, 0x90, 0xE1, 0x17, 0x3B, 0x6C, 0x71, 0x99, 0x0C, 0xC9, - 0xC8, 0xF3, 0x89, 0xB2, 0xA5, 0x68, 0xE1, 0x78, 0xE6, 0x97, 0x56, 0x10, 0x1A, 0x7E, 0x58, 0x05, 0xA1, 0x31, 0x0A, 0x89, 0x5F, 0x8E, 0x8F, 0xA0, 0x55, 0x94, - 0x63, 0xCB, 0xEF, 0x96, 0x37, 0xB0, 0x5D, 0xC7, 0x76, 0x49, 0x75, 0xF2, 0xF2, 0xFA, 0x4D, 0xA2, 0x63, 0xAD, 0x2A, 0x28, 0xC6, 0x9E, 0x8E, 0x8B, 0xAC, 0x84, - 0xF2, 0x9A, 0xED, 0x8C, 0xFB, 0x4D, 0xB7, 0xD3, 0xF9, 0x5B, 0xF6, 0xE6, 0x84, 0x30, 0x33, 0x35, 0xE6, 0xA1, 0xB7, 0xBD, 0x47, 0x64, 0xDC, 0x2A, 0xC5, 0xC7, - 0x7F, 0x4D, 0x89, 0x65, 0x1B, 0x5A, 0x5D, 0x72, 0xE7, 0xD3, 0x0E, 0xD8, 0x54, 0x43, 0x33, 0x5C, 0x4B, 0xAB, 0x7B, 0xBE, 0x0D, 0x8E, 0x60, 0xD0, 0x70, 0xE3, - 0xC0, 0x15, 0x48, 0x1C, 0x33, 0xD2, 0x50, 0xB0, 0x5C, 0xE0, 0x33, 0xB2, 0x44, 0xD4, 0x6E, 0x83, 0xBF, 0x0A, 0x21, 0x07, 0x7F, 0xA5, 0x0E, 0xA4, 0xE0, 0x91, - 0xA2, 0x2F, 0xD2, 0x97, 0x4C, 0x61, 0x9E, 0xCE, 0xF0, 0x37, 0x35, 0x96, 0xAD, 0x42, 0xDD, 0x89, 0x46, 0x42, 0x87, 0x90, 0x66, 0xCD, 0x3A, 0x34, 0xBD, 0x99, - 0x68, 0x2D, 0x0D, 0xA3, 0x64, 0x43, 0x0D, 0xC3, 0x91, 0xAA, 0x55, 0x8E, 0x3F, 0xD9, 0x28, 0xD6, 0x60, 0x57, 0xCD, 0x6A, 0x1C, 0x3B, 0xD8, 0x3F, 0x95, 0x0D, - 0x31, 0x4E, 0x72, 0xA3, 0x08, 0xFE, 0xAA, 0x47, 0x92, 0x18, 0x59, 0x69, 0x34, 0x51, 0x20, 0xCE, 0x8F, 0x28, 0x19, 0xBC, 0x79, 0xDE, 0xAD, 0xC0, 0x5A, 0x4C, - 0x42, 0xD5, 0xE8, 0xA2, 0x40, 0x5C, 0x44, 0x43, 0x69, 0x94, 0xC1, 0xDF, 0x6D, 0x85, 0x7A, 0xE3, 0xBB, 0xE1, 0x3C, 0x0C, 0x3D, 0x37, 0xD8, 0x2A, 0x45, 0xE5, - 0xF9, 0xD9, 0x1F, 0xF3, 0x20, 0xB4, 0x47, 0xAB, 0x16, 0x77, 0x69, 0xF0, 0xB3, 0x99, 0x01, 0x25, 0xE4, 0x90, 0x84, 0x0B, 0x42, 0x8A, 0xCB, 0x0D, 0xD7, 0xB8, - 0x81, 0xB8, 0x33, 0x1E, 0x3B, 0x2A, 0xDB, 0x33, 0xE7, 0x7E, 0x80, 0x75, 0xDB, 0xCC, 0xB3, 0x01, 0xB1, 0x9F, 0xED, 0x38, 0xE9, 0x83, 0x15, 0x3B, 0x6A, 0x99, - 0x43, 0x45, 0x5F, 0xDE, 0x3C, 0x44, 0x19, 0x2B, 0x35, 0xE1, 0x01, 0x3B, 0x76, 0xB8, 0x52, 0xDE, 0xE3, 0x9E, 0xA8, 0xB8, 0x23, 0x5C, 0xB0, 0x30, 0x2D, 0x24, - 0xE9, 0x3A, 0x33, 0x27, 0xC4, 0xFC, 0x42, 0xAC, 0x83, 0xD2, 0x32, 0xAC, 0xAC, 0x3C, 0x6C, 0xDB, 0xEE, 0x6C, 0x1E, 0xB6, 0xB0, 0x9C, 0x9A, 0xDD, 0x8B, 0xCE, - 0xA9, 0x41, 0x0A, 0x16, 0x7B, 0xBD, 0xA2, 0xA2, 0xE2, 0x78, 0xB6, 0x2C, 0x16, 0x82, 0x4C, 0xEC, 0xC0, 0x31, 0x86, 0xC4, 0x29, 0x22, 0x99, 0x3B, 0x43, 0x4E, - 0xD8, 0xE5, 0xB1, 0x2A, 0xBF, 0x76, 0xA3, 0x94, 0xC5, 0xC9, 0xAB, 0xFF, 0xF8, 0x6F, 0x95, 0xE5, 0x48, 0x8F, 0x9B, 0x89, 0x4B, 0x01, 0x71, 0xC0, 0xC1, 0xF2, - 0x4A, 0x6F, 0x68, 0xB3, 0x00, 0x1A, 0x0A, 0x3B, 0xF0, 0x0D, 0x77, 0x4C, 0x20, 0x16, 0x2C, 0x9B, 0xE2, 0xB0, 0x78, 0x60, 0x50, 0x89, 0x7D, 0x0C, 0xD5, 0xC7, - 0xC5, 0x03, 0x11, 0x16, 0x10, 0x9A, 0x5A, 0x9B, 0x1D, 0x6C, 0x50, 0x95, 0x48, 0xFA, 0x2D, 0x24, 0xA4, 0xAB, 0xB4, 0x0E, 0x56, 0x98, 0x28, 0x3D, 0x27, 0x69, - 0x5B, 0xCA, 0x42, 0xBF, 0x34, 0x34, 0x88, 0x21, 0xDF, 0x68, 0x54, 0x36, 0x68, 0x1C, 0x8D, 0x8E, 0x3A, 0x47, 0xFD, 0xD2, 0xCA, 0x49, 0xC9, 0x65, 0x6A, 0xE0, - 0xA8, 0x08, 0x1D, 0x51, 0x58, 0x29, 0x34, 0x82, 0xC0, 0xB8, 0x51, 0x16, 0xED, 0x5E, 0x60, 0xB3, 0x91, 0x9B, 0x31, 0x0C, 0x60, 0xEC, 0x16, 0x2A, 0x86, 0x5E, - 0xDC, 0xD0, 0x7B, 0x4A, 0xFA, 0x68, 0x49, 0xA7, 0x74, 0x01, 0x21, 0x5E, 0x35, 0xD9, 0x09, 0x0D, 0xA8, 0x9B, 0x48, 0x0A, 0x56, 0x16, 0x95, 0x21, 0x59, 0x86, - 0x2D, 0x8B, 0x98, 0x9E, 0xCF, 0xAA, 0xC1, 0x9C, 0x91, 0x63, 0x4A, 0x91, 0xE5, 0x16, 0x7B, 0x36, 0xF1, 0x6E, 0x88, 0xAF, 0x10, 0x56, 0x4A, 0xA9, 0xFD, 0x27, - 0x7D, 0xAB, 0x02, 0x36, 0x03, 0xD2, 0xA3, 0x52, 0xF6, 0x49, 0x74, 0xBD, 0xAE, 0xD9, 0x2B, 0xF4, 0x63, 0x86, 0xAE, 0x0D, 0x3E, 0x63, 0x0C, 0x1D, 0x62, 0x15, - 0x64, 0x33, 0x8B, 0x8C, 0x8C, 0xB9, 0x13, 0x96, 0x58, 0xA5, 0xD1, 0xC1, 0x7F, 0x45, 0x3D, 0xD2, 0x30, 0xF4, 0x1B, 0xCE, 0x0B, 0x5D, 0xD0, 0xC0, 0xF1, 0x2F, - 0x45, 0x9F, 0xA2, 0xD4, 0x30, 0x66, 0x33, 0x62, 0x40, 0x2B, 0x93, 0xE4, 0xE9, 0xA1, 0xD2, 0x10, 0x43, 0x1D, 0xE7, 0x2B, 0x8D, 0xDB, 0x4B, 0x1D, 0x36, 0x2A, - 0x1E, 0xD7, 0xE2, 0xF9, 0x6C, 0xE4, 0x99, 0x73, 0x55, 0x55, 0x53, 0xCD, 0xF1, 0xB2, 0xF8, 0xCE, 0x84, 0xC8, 0x02, 0xC7, 0xA6, 0xEE, 0x3F, 0x77, 0x5D, 0xD4, - 0x68, 0x2B, 0xF4, 0x81, 0x4D, 0x45, 0x47, 0xD5, 0x04, 0xB7, 0x51, 0x0C, 0x4B, 0x08, 0x36, 0x6F, 0xEE, 0x2A, 0x15, 0xA6, 0x14, 0xE1, 0x34, 0x8A, 0xB4, 0x1A, - 0xC4, 0x10, 0xDB, 0x12, 0xA8, 0xB6, 0x93, 0x4B, 0x38, 0x99, 0x4F, 0x55, 0x75, 0x94, 0xE8, 0xAC, 0x0B, 0x49, 0x9F, 0x75, 0xE7, 0x8F, 0x87, 0x46, 0xBD, 0xD3, - 0xEC, 0x34, 0x8F, 0xE0, 0x7F, 0x8A, 0xF1, 0x4C, 0xB1, 0x71, 0x71, 0xF1, 0xE6, 0x58, 0x5E, 0x2A, 0x44, 0x97, 0x4F, 0x2B, 0xE5, 0x05, 0xFB, 0x52, 0x5D, 0x54, - 0xF7, 0xA4, 0xE4, 0xFC, 0x52, 0xB7, 0x5D, 0x92, 0x87, 0x73, 0x4C, 0x7A, 0x7D, 0x43, 0x54, 0x58, 0xCB, 0xBA, 0x2A, 0x9E, 0x7A, 0x7F, 0xB6, 0x58, 0x11, 0xF2, - 0x7F, 0xDE, 0xDA, 0x25, 0x51, 0x7C, 0xD3, 0x96, 0xBE, 0xB6, 0x5C, 0x82, 0x7D, 0xDB, 0x46, 0x27, 0x5F, 0xEB, 0x2D, 0x5E, 0xF5, 0x01, 0x85, 0x2E, 0x8C, 0x41, - 0x7D, 0x18, 0x8C, 0xE6, 0x56, 0x86, 0x52, 0x9B, 0x0D, 0x64, 0x30, 0xB2, 0x1D, 0xA7, 0xE5, 0x78, 0x8B, 0xF2, 0x4A, 0xA4, 0xD8, 0x92, 0x33, 0x76, 0x5A, 0x6E, - 0xF2, 0x9B, 0x52, 0x3B, 0x87, 0xC8, 0xF5, 0x1F, 0x41, 0xED, 0xB7, 0xED, 0x70, 0x85, 0xAE, 0xB1, 0x59, 0xA2, 0xD8, 0xC0, 0x1E, 0xB7, 0xEB, 0xA8, 0x92, 0x29, - 0xB1, 0x4A, 0xB0, 0x78, 0xD8, 0xB3, 0xB0, 0x43, 0x73, 0xB2, 0xC1, 0xD0, 0x33, 0x1E, 0x18, 0xF9, 0xC4, 0x31, 0xB0, 0x82, 0xDF, 0x68, 0x86, 0xA2, 0x74, 0xF8, - 0x26, 0x83, 0x57, 0xE1, 0x84, 0x8A, 0xEE, 0xEB, 0x99, 0x5D, 0x6A, 0xB3, 0xDA, 0x21, 0x3F, 0x56, 0xAB, 0xCD, 0xBA, 0xA4, 0xDC, 0x4F, 0x7A, 0x86, 0xBA, 0xD1, - 0x1A, 0x11, 0x5D, 0x04, 0xED, 0xB1, 0x4F, 0x56, 0x15, 0x98, 0x69, 0xF2, 0xBF, 0x67, 0x6C, 0xFE, 0x78, 0xF3, 0xA9, 0x12, 0x9A, 0x00, 0xB8, 0x15, 0xB5, 0xFB, - 0x41, 0x85, 0xAE, 0xF3, 0xBB, 0xAC, 0x62, 0x8F, 0xD1, 0xEC, 0xA8, 0xAE, 0x57, 0x08, 0x37, 0x05, 0x29, 0x54, 0x6D, 0xAA, 0x22, 0xFB, 0xAA, 0xC7, 0xF3, 0x64, - 0x14, 0xE6, 0x2C, 0xFE, 0xD0, 0x3A, 0xF5, 0xA8, 0x38, 0xBA, 0xB5, 0xA4, 0xD9, 0x94, 0xD2, 0xC8, 0x11, 0x4D, 0x62, 0xE6, 0x5B, 0x9F, 0x12, 0x33, 0x46, 0xCF, - 0xB5, 0x91, 0xE7, 0xAB, 0x44, 0x94, 0xCF, 0x54, 0xCD, 0xD0, 0x66, 0xCA, 0x53, 0x3E, 0xA8, 0x87, 0x7C, 0xAA, 0xF7, 0x4E, 0x94, 0x6B, 0x2B, 0x05, 0x8D, 0x8B, - 0x48, 0xCB, 0x9D, 0x05, 0xCC, 0xA6, 0xAC, 0xDC, 0x01, 0xB2, 0x1C, 0x8B, 0x94, 0x8A, 0x2A, 0xF6, 0xCA, 0xA2, 0x08, 0x93, 0x9D, 0xC9, 0x2A, 0x34, 0x76, 0x7B, - 0x6A, 0x40, 0xD9, 0x8B, 0xE6, 0x6A, 0x00, 0x46, 0x95, 0xFE, 0xAA, 0x98, 0xBB, 0x34, 0xC7, 0xDA, 0x3D, 0xE9, 0x94, 0x74, 0x69, 0x3A, 0x5E, 0xB0, 0xE5, 0x04, - 0x58, 0xFE, 0xFC, 0x97, 0xF2, 0x4E, 0xA5, 0xD4, 0x5D, 0xE8, 0x53, 0xC5, 0xEE, 0x98, 0x92, 0x79, 0xB7, 0xA3, 0x8C, 0xB4, 0x85, 0xB3, 0x94, 0x74, 0x06, 0x8D, - 0xAE, 0x5F, 0x9E, 0x69, 0x26, 0x51, 0x87, 0xD1, 0xE4, 0x44, 0x5D, 0x95, 0xA9, 0xD2, 0x42, 0x3D, 0x4C, 0x6C, 0xCB, 0x22, 0x85, 0x73, 0xC1, 0x38, 0xE6, 0xAD, - 0x58, 0x3C, 0x20, 0xFD, 0xAA, 0x49, 0xA9, 0x7B, 0x71, 0x8A, 0xC2, 0xC7, 0x1A, 0xBA, 0xF7, 0xED, 0x31, 0x3C, 0xD1, 0xE4, 0xCD, 0xA4, 0x27, 0x4B, 0x91, 0x42, - 0x52, 0x95, 0xCE, 0x1D, 0xCD, 0xB5, 0xA2, 0xC8, 0x40, 0x0E, 0xD8, 0x2A, 0x1B, 0xCD, 0x53, 0x54, 0xD1, 0x85, 0x94, 0x36, 0x5F, 0x5B, 0xE2, 0xCB, 0x80, 0xAD, - 0xBC, 0xD5, 0x95, 0x3B, 0x5C, 0x6A, 0xA3, 0x16, 0x90, 0xEE, 0x37, 0x57, 0x34, 0x7B, 0xAA, 0x8C, 0x0A, 0x88, 0x8C, 0x52, 0x8C, 0x78, 0xB8, 0x2A, 0xD9, 0x6A, - 0x53, 0xE7, 0x38, 0x3F, 0x94, 0x9E, 0x86, 0x3B, 0x3F, 0x8C, 0x1F, 0xDC, 0x3B, 0xC7, 0x47, 0xE2, 0xE4, 0x87, 0xE6, 0x78, 0x3F, 0xA6, 0x63, 0x04, 0xC1, 0x85, - 0x8E, 0x8F, 0x76, 0xE9, 0xC9, 0x67, 0xE8, 0xCE, 0x2D, 0xFB, 0x46, 0xB3, 0xAD, 0x0B, 0xDD, 0xF1, 0xC6, 0x5E, 0xEA, 0x1E, 0xBD, 0xCF, 0xB4, 0x0C, 0x79, 0xEC, - 0x42, 0x4F, 0xAC, 0x2F, 0xEA, 0x14, 0x2A, 0xBE, 0xA4, 0x0F, 0x1E, 0x7D, 0xF7, 0xE4, 0xF1, 0xE3, 0x93, 0x1F, 0x1E, 0xB9, 0xC3, 0x60, 0xC6, 0xFF, 0xFF, 0x33, - 0x5B, 0x8E, 0xFD, 0xE9, 0x63, 0xEF, 0xA4, 0x0F, 0xC3, 0x3D, 0x12, 0x86, 0x60, 0x7A, 0xC1, 0xF9, 0x21, 0x45, 0x9A, 0x22, 0xE4, 0x10, 0x28, 0xC9, 0xA1, 0x8D, - 0x97, 0x3B, 0x2A, 0xF2, 0x44, 0x93, 0x00, 0x32, 0xF8, 0xD0, 0xF0, 0x15, 0x4D, 0x68, 0x33, 0x56, 0x4C, 0xD3, 0x50, 0xA2, 0x53, 0x9D, 0x0C, 0xBD, 0x65, 0x9A, - 0x03, 0xCA, 0x14, 0x57, 0x18, 0x6F, 0x45, 0xAC, 0x3C, 0x84, 0x00, 0x46, 0xC1, 0x71, 0x71, 0x15, 0xDA, 0x28, 0x1B, 0x25, 0x54, 0x80, 0x8D, 0x97, 0xA6, 0xF3, - 0x45, 0xE8, 0x5E, 0x17, 0x4A, 0x71, 0xBD, 0x90, 0x85, 0xCA, 0x9C, 0xAE, 0x12, 0xAC, 0x72, 0x18, 0x69, 0xD9, 0x90, 0x71, 0x01, 0xA2, 0x6D, 0x51, 0xEC, 0xEC, - 0x5A, 0x31, 0x26, 0x8A, 0x4D, 0xD2, 0xAB, 0x00, 0xD6, 0x07, 0x9F, 0x2E, 0xDF, 0xFE, 0x5D, 0x7B, 0xF7, 0xFA, 0x4F, 0xA5, 0x86, 0xCA, 0x88, 0xC2, 0x18, 0x5D, - 0xA1, 0x67, 0x0A, 0xC6, 0xF4, 0x21, 0x64, 0xA2, 0x73, 0xCD, 0x50, 0x0C, 0x98, 0xED, 0x1D, 0xE2, 0x8E, 0xC3, 0xC9, 0x85, 0xDE, 0xD5, 0xF1, 0x91, 0x16, 0x71, - 0xD6, 0xD3, 0x35, 0x8C, 0xDF, 0xF4, 0xE0, 0xC6, 0x70, 0xE6, 0x78, 0xD4, 0xA9, 0xC2, 0x6B, 0xD6, 0xB4, 0x94, 0xCD, 0x78, 0x60, 0x89, 0x64, 0x2C, 0x05, 0xE2, - 0xA4, 0x94, 0xF5, 0xC1, 0x35, 0x09, 0xCF, 0x0F, 0xD9, 0xAD, 0x12, 0xAD, 0x15, 0xF7, 0x0D, 0x9E, 0xCC, 0xCC, 0xA1, 0xC8, 0x84, 0x8A, 0x14, 0x3F, 0xF2, 0x8D, - 0x29, 0x41, 0xA9, 0x54, 0xD2, 0xBC, 0xAC, 0xF5, 0x08, 0x52, 0x1F, 0x7C, 0x20, 0xB4, 0x20, 0x02, 0x32, 0x2A, 0x29, 0xFE, 0x9C, 0xD7, 0xA8, 0x89, 0xFE, 0x23, - 0x7B, 0xE6, 0x6B, 0x52, 0x2D, 0x83, 0x99, 0x79, 0x05, 0xB9, 0x3F, 0x6C, 0xB5, 0xB4, 0xDE, 0xBB, 0xF7, 0x5A, 0xAB, 0x55, 0xA1, 0xB1, 0x37, 0xA3, 0xEE, 0xC4, - 0xF5, 0xDF, 0x3D, 0xD2, 0x07, 0xBF, 0x7C, 0x7A, 0xF5, 0xAC, 0x0E, 0x75, 0x61, 0x67, 0xD9, 0xED, 0x75, 0x3A, 0x8D, 0xF3, 0x43, 0xD6, 0x64, 0x7D, 0x5C, 0x3D, - 0xD0, 0x2B, 0xC5, 0xD5, 0x3B, 0x05, 0x5C, 0x9D, 0x5E, 0x7F, 0x0B, 0x5C, 0x5D, 0x7D, 0xF0, 0xFA, 0x05, 0xC3, 0xF4, 0xB8, 0xB7, 0x0D, 0x51, 0x60, 0xE0, 0x94, - 0x26, 0x20, 0x67, 0xF9, 0xF8, 0xE4, 0x74, 0x73, 0x4C, 0x4F, 0x80, 0xBB, 0x8F, 0x80, 0xE9, 0x14, 0x04, 0x75, 0xB2, 0x8D, 0x9C, 0x4E, 0xF5, 0x01, 0xE2, 0x81, - 0x88, 0xBE, 0xEC, 0x9F, 0x6E, 0x81, 0xE7, 0x31, 0x88, 0x08, 0x11, 0x01, 0x92, 0xE5, 0xD1, 0x36, 0x32, 0x3A, 0xD1, 0x07, 0x97, 0x6F, 0x5E, 0xD6, 0xFB, 0xC0, - 0x58, 0xEF, 0xC9, 0xC9, 0xE6, 0x78, 0x8E, 0xF5, 0xC1, 0x3F, 0x90, 0x20, 0x20, 0x66, 0xD9, 0xEB, 0x6F, 0x41, 0x50, 0x5F, 0x1F, 0x00, 0x3C, 0xE2, 0xD8, 0x18, - 0x05, 0xD8, 0xF5, 0x6B, 0x4A, 0x0C, 0x22, 0xEA, 0x3E, 0xDE, 0x82, 0x2B, 0xB0, 0xEA, 0x7F, 0xA0, 0x78, 0x00, 0xC9, 0xB2, 0xDB, 0xDF, 0xC6, 0xA6, 0x01, 0x11, - 0x25, 0x09, 0x7C, 0x0D, 0x5D, 0x6D, 0x73, 0x4C, 0x60, 0xD3, 0x4F, 0x4E, 0x96, 0x4F, 0x4E, 0xAA, 0x21, 0xC0, 0x18, 0x89, 0xF1, 0xA6, 0x28, 0x8A, 0x16, 0x07, - 0xD9, 0xA2, 0x00, 0xFA, 0xEF, 0x39, 0x0C, 0x8B, 0xC2, 0xD5, 0xDA, 0xE1, 0x93, 0xC3, 0x81, 0x4C, 0xD8, 0x41, 0xB5, 0xC8, 0x29, 0x51, 0x12, 0x3D, 0xA1, 0xA3, - 0x0F, 0xFA, 0x15, 0x32, 0x54, 0xA2, 0x84, 0xA1, 0xB0, 0x09, 0xFA, 0x69, 0xDA, 0x44, 0xCB, 0xC3, 0x84, 0x09, 0x2E, 0x71, 0xA4, 0x4B, 0x11, 0x64, 0xA3, 0xD0, - 0xAC, 0xA0, 0xD5, 0x58, 0xEA, 0x83, 0x93, 0xA3, 0xD2, 0x94, 0xB6, 0xB9, 0x32, 0x86, 0x74, 0x00, 0xEE, 0x92, 0x20, 0x58, 0x5B, 0x1F, 0x31, 0xA8, 0x3E, 0x78, - 0x1E, 0x1D, 0x6F, 0xA3, 0x95, 0x56, 0x6F, 0x0B, 0xB5, 0x48, 0xE4, 0x30, 0xCD, 0xB4, 0x7A, 0x5C, 0x35, 0x71, 0xF1, 0x72, 0xB7, 0x8A, 0x29, 0xA3, 0x76, 0x1B, - 0xBD, 0x60, 0x01, 0xEE, 0x1B, 0x41, 0xB8, 0xB6, 0x56, 0x04, 0x20, 0x44, 0x68, 0x7E, 0xB4, 0x37, 0x8D, 0x44, 0xA4, 0x7C, 0x03, 0xFA, 0x08, 0x8C, 0x70, 0xCE, - 0x9E, 0x85, 0x5A, 0x5B, 0x23, 0x31, 0x28, 0xD4, 0x03, 0xD1, 0xF1, 0xDE, 0xB4, 0x22, 0x91, 0xF3, 0x2D, 0xE8, 0x65, 0x46, 0x4C, 0xDB, 0x70, 0x3E, 0x93, 0xD1, - 0x08, 0x12, 0xD6, 0xFA, 0xBA, 0x49, 0x80, 0x83, 0x7E, 0xD8, 0xB9, 0x76, 0x45, 0xCF, 0xD7, 0xAE, 0xCD, 0x53, 0xE8, 0x36, 0x2F, 0xD0, 0xD3, 0xD9, 0x9B, 0x4F, - 0x52, 0x13, 0x3A, 0x24, 0x62, 0x47, 0xFA, 0xE0, 0x47, 0x2F, 0xA2, 0x73, 0xF3, 0x02, 0xE3, 0x47, 0x32, 0xA6, 0x73, 0xC0, 0xDB, 0x54, 0x3B, 0xAF, 0x7C, 0x63, - 0x45, 0x37, 0x19, 0x6E, 0x53, 0x7C, 0x7D, 0x20, 0x96, 0xF6, 0xB3, 0xED, 0x6E, 0xCE, 0x4C, 0x1F, 0x09, 0x21, 0xC4, 0xDD, 0x0E, 0x0B, 0x94, 0xA4, 0xCF, 0xE1, - 0x60, 0x3B, 0x24, 0x27, 0x38, 0x5E, 0x9D, 0xD9, 0xC6, 0xD7, 0x50, 0x6E, 0x19, 0x8B, 0xE1, 0xDA, 0x6E, 0x01, 0x30, 0xFA, 0xE0, 0xD9, 0xAF, 0xCF, 0xD7, 0x0E, - 0x52, 0x6C, 0x25, 0xB5, 0x8A, 0x85, 0xC7, 0xF3, 0x11, 0xD8, 0x59, 0x66, 0xA2, 0x48, 0xED, 0x39, 0x55, 0x27, 0x8B, 0x14, 0x7C, 0x09, 0x02, 0xE9, 0xC2, 0x93, - 0x2E, 0xB1, 0x59, 0x8D, 0xC7, 0xFB, 0x8B, 0x60, 0x40, 0xC4, 0xE7, 0xB1, 0x61, 0xAF, 0x9F, 0x57, 0x04, 0x20, 0xD5, 0x94, 0xF6, 0x0A, 0x8E, 0x76, 0xA5, 0x2E, - 0xD6, 0xED, 0xDE, 0x74, 0xC6, 0xB9, 0xDE, 0xB7, 0xE2, 0x80, 0x90, 0xA9, 0x67, 0xAD, 0x3F, 0x0D, 0xC4, 0xE1, 0xF4, 0x01, 0x68, 0xED, 0x1D, 0x1C, 0xAC, 0x9D, - 0x65, 0x04, 0x82, 0x7B, 0x4E, 0x2F, 0xCF, 0xE6, 0xA1, 0xB7, 0x4D, 0x66, 0xB9, 0x9E, 0xBB, 0xEE, 0x6A, 0x9B, 0xB4, 0x72, 0xE9, 0x78, 0x73, 0x6B, 0x73, 0x0C, - 0x90, 0x53, 0x7E, 0x1A, 0x8D, 0x6C, 0x73, 0xF3, 0xAC, 0x04, 0x19, 0xE5, 0xB5, 0x37, 0xAD, 0x08, 0x7F, 0xCF, 0x51, 0x9C, 0x98, 0xEB, 0x07, 0x08, 0x62, 0x82, - 0x16, 0xAF, 0x2E, 0xB5, 0xEB, 0xAB, 0x1F, 0xAF, 0x7F, 0xFA, 0xB0, 0x9B, 0xE8, 0x00, 0x7D, 0xEE, 0x29, 0x30, 0x20, 0xB7, 0xFB, 0x8E, 0x09, 0x40, 0x44, 0x6F, - 0x13, 0x3D, 0xF5, 0x98, 0xA2, 0x5E, 0x5C, 0xBF, 0xDF, 0x95, 0x96, 0x7A, 0xFB, 0x53, 0x53, 0xEF, 0x6B, 0xD0, 0xD3, 0x67, 0x87, 0xDC, 0x10, 0x67, 0x03, 0x5D, - 0x31, 0x40, 0xD4, 0x97, 0xF6, 0x16, 0x8F, 0xF6, 0x36, 0x90, 0x8B, 0x48, 0xF9, 0x06, 0x86, 0x71, 0x60, 0x15, 0x9F, 0x29, 0xD1, 0x9B, 0x38, 0x0F, 0x83, 0xD4, - 0x07, 0x57, 0xCB, 0x99, 0x17, 0xCC, 0xFD, 0x8A, 0x09, 0x55, 0xAD, 0x91, 0xCE, 0x56, 0x0A, 0x11, 0xA4, 0x30, 0x8D, 0x74, 0xB8, 0x42, 0x70, 0x91, 0x44, 0x5A, - 0x3F, 0xEB, 0xDF, 0xA9, 0x56, 0x10, 0xF9, 0x7D, 0x2A, 0x66, 0xBC, 0x41, 0xDE, 0x19, 0x63, 0xDE, 0x79, 0x75, 0xB9, 0x9B, 0x50, 0x36, 0xDE, 0x5B, 0xC2, 0x19, - 0xEF, 0x35, 0xE1, 0x68, 0x7C, 0x0D, 0x5B, 0x48, 0x61, 0xC3, 0x41, 0x04, 0x07, 0x84, 0xB1, 0xF3, 0x26, 0x03, 0x08, 0xC9, 0x73, 0xBA, 0xCB, 0x6D, 0x5C, 0x47, - 0x90, 0x91, 0xF4, 0x9C, 0xA3, 0xD8, 0x6F, 0x8E, 0xEF, 0xD4, 0x6B, 0x8E, 0x4A, 0xA9, 0xDD, 0xC6, 0x69, 0x90, 0x13, 0x93, 0xD8, 0x0E, 0x6E, 0x65, 0x5E, 0x57, - 0x21, 0x12, 0x2C, 0xD3, 0x89, 0x76, 0xC9, 0xCE, 0xB6, 0xD1, 0x4D, 0x6F, 0x1B, 0xDD, 0xC8, 0x14, 0x25, 0xD5, 0x73, 0x72, 0x4F, 0x99, 0xA6, 0xDB, 0x3B, 0xBD, - 0x4F, 0xF5, 0x0C, 0x67, 0xEB, 0xC7, 0x34, 0x80, 0xD1, 0x07, 0xCF, 0xDF, 0xEF, 0x26, 0xA6, 0x61, 0x67, 0x15, 0x63, 0xDA, 0x56, 0x11, 0x8C, 0x32, 0xB5, 0xEF, - 0x52, 0x6C, 0xB1, 0x81, 0x36, 0x16, 0x48, 0xF8, 0xAF, 0x3B, 0xD2, 0xC6, 0xA2, 0xBA, 0x36, 0xEE, 0x38, 0xC3, 0x2C, 0xBE, 0x06, 0xFD, 0xF8, 0xC6, 0xE2, 0xF3, - 0x78, 0x6A, 0xAC, 0xAD, 0x23, 0x0E, 0xA7, 0x0F, 0x3E, 0x18, 0x0B, 0xED, 0xD5, 0xBB, 0x67, 0x3B, 0xD1, 0x95, 0xE8, 0x74, 0x3F, 0xFA, 0x8A, 0x58, 0xDE, 0xB7, - 0xCE, 0x1C, 0xE2, 0xAE, 0xEF, 0x54, 0x08, 0xA4, 0x0F, 0xDE, 0x12, 0x37, 0xD0, 0x2E, 0x3D, 0x9F, 0xBF, 0x76, 0x6E, 0x27, 0x5A, 0xA3, 0x3D, 0xEF, 0x47, 0x65, - 0x8C, 0xE9, 0x7D, 0xEB, 0x6B, 0x32, 0xB5, 0x7D, 0xDF, 0xF3, 0xD7, 0x56, 0x19, 0x87, 0xD3, 0x07, 0xAF, 0x5B, 0xEF, 0xE8, 0xD1, 0x4E, 0xD4, 0x25, 0x7A, 0xDD, - 0x8F, 0xC6, 0x22, 0x9E, 0xF7, 0xAD, 0xB4, 0x9B, 0x91, 0x63, 0xCF, 0xD6, 0x56, 0x19, 0x85, 0xD2, 0x07, 0x1F, 0x5B, 0x2F, 0xE1, 0xEF, 0x4E, 0xD4, 0xC5, 0x7A, - 0xDC, 0x8F, 0xB2, 0x38, 0xB7, 0xFB, 0x56, 0x95, 0x65, 0x2E, 0xD6, 0x56, 0x14, 0xC0, 0xE8, 0x83, 0x17, 0x97, 0xBF, 0x6A, 0xF5, 0x17, 0xDE, 0xC2, 0xC5, 0x07, - 0x2E, 0xB5, 0xAB, 0x1F, 0x1B, 0x3B, 0xD1, 0x18, 0x76, 0xBD, 0x1F, 0x7D, 0x51, 0xA6, 0xF7, 0xAD, 0x2D, 0xBA, 0xAF, 0x66, 0x68, 0xAC, 0x1F, 0x0E, 0x05, 0x20, - 0x3E, 0xFB, 0x02, 0x47, 0xDA, 0x73, 0x63, 0x37, 0x01, 0x31, 0xEA, 0x77, 0x17, 0x45, 0x7B, 0xCC, 0xE4, 0xBE, 0xF5, 0xE4, 0x10, 0xAB, 0x82, 0x8A, 0x92, 0x25, - 0x86, 0xF5, 0x19, 0xB7, 0xA8, 0xE0, 0xD6, 0xCD, 0x15, 0xD4, 0x1A, 0x57, 0x2F, 0xB4, 0x37, 0xE2, 0xB4, 0x02, 0x37, 0x1B, 0xCF, 0xD9, 0xE5, 0x0D, 0x6D, 0x93, - 0xF4, 0x24, 0x07, 0xB7, 0xBD, 0xE3, 0xE3, 0xED, 0x86, 0xB7, 0x79, 0xD3, 0xA8, 0xC7, 0xC7, 0xF7, 0xA8, 0x93, 0x91, 0x61, 0x92, 0xCF, 0x16, 0x09, 0x37, 0x79, - 0x18, 0x46, 0x82, 0xD5, 0x07, 0x2F, 0xE1, 0x44, 0x7B, 0x41, 0x4F, 0x76, 0x55, 0x06, 0xCA, 0xFD, 0xEF, 0xC2, 0x93, 0x12, 0xFC, 0xEE, 0xDB, 0x99, 0x28, 0x31, - 0x50, 0x74, 0x7B, 0x63, 0x77, 0xA3, 0xBD, 0x05, 0x09, 0x70, 0xAE, 0xBE, 0x0F, 0xEC, 0x7C, 0xB7, 0x0A, 0x8C, 0x89, 0xD8, 0x99, 0x0E, 0x25, 0xBE, 0x77, 0xA1, - 0x46, 0x79, 0x83, 0x11, 0x7F, 0x33, 0x67, 0x99, 0xA6, 0xF8, 0x46, 0x17, 0x3A, 0x9D, 0x46, 0xC2, 0x56, 0x10, 0xDA, 0x8E, 0xA3, 0x0F, 0x5E, 0x91, 0x50, 0xBB, - 0xC6, 0xC3, 0x8A, 0x3B, 0x5B, 0x24, 0x2C, 0x62, 0x5B, 0x5B, 0xE8, 0x13, 0x63, 0xAA, 0x0F, 0xAE, 0xF1, 0x9D, 0xA5, 0x80, 0x0B, 0xCF, 0xD6, 0x47, 0x46, 0x85, - 0x48, 0x5C, 0xDF, 0x03, 0xA2, 0x22, 0x25, 0xF1, 0x77, 0xA1, 0xE9, 0x9A, 0x38, 0x92, 0xAE, 0x0D, 0xAE, 0x68, 0x63, 0x0D, 0xAD, 0xAC, 0xBC, 0xBB, 0xCA, 0x5B, - 0x6E, 0xE8, 0x1E, 0x3B, 0xDC, 0x34, 0x97, 0x7C, 0xA3, 0x31, 0x68, 0x95, 0xED, 0xA0, 0x1D, 0x9C, 0x07, 0x33, 0xC3, 0x15, 0xCD, 0xE8, 0xF6, 0xD2, 0x05, 0xDF, - 0x2F, 0x38, 0xF4, 0x1C, 0x0B, 0x1A, 0x3E, 0xB3, 0x6E, 0xF0, 0xED, 0x4E, 0x96, 0x76, 0x1D, 0xED, 0x7C, 0x43, 0x10, 0x30, 0x0B, 0x81, 0xA1, 0x44, 0xB7, 0x13, - 0x5F, 0xA0, 0x67, 0x7B, 0x14, 0xF1, 0x7D, 0x38, 0x05, 0xCA, 0xCD, 0xD9, 0xAC, 0xE7, 0x93, 0x71, 0x24, 0x48, 0xD5, 0x1E, 0x4E, 0xE5, 0xD6, 0xBD, 0x0F, 0x64, - 0x6C, 0x07, 0x40, 0xA3, 0x06, 0x66, 0x71, 0x48, 0xB7, 0x3B, 0x31, 0x53, 0xAE, 0xB6, 0x95, 0x4E, 0xEE, 0x92, 0x6F, 0x04, 0x56, 0x6E, 0x90, 0x5C, 0xAB, 0x62, - 0x4C, 0x6F, 0x67, 0x4C, 0x62, 0x2C, 0x33, 0xFA, 0x87, 0xAD, 0xD6, 0xA4, 0x8F, 0x1B, 0xB7, 0x34, 0xC1, 0xDA, 0xF9, 0xE1, 0xA4, 0x5F, 0xB6, 0xE7, 0xA8, 0x74, - 0xD7, 0x1D, 0x70, 0xBA, 0xF1, 0xA6, 0x3B, 0x94, 0xD2, 0x00, 0xA8, 0x69, 0x6A, 0xEF, 0x8C, 0xE0, 0x4B, 0x53, 0xFB, 0x88, 0xF9, 0x7D, 0x87, 0x7B, 0xEF, 0x90, - 0x76, 0xC3, 0xB2, 0xFC, 0xDC, 0xFD, 0x77, 0xFD, 0xC4, 0xFE, 0xBB, 0x13, 0xB1, 0xFF, 0x4E, 0x9A, 0x69, 0x5F, 0x76, 0xBB, 0xDD, 0x2A, 0x9C, 0x57, 0xDC, 0x82, - 0x77, 0x27, 0x2C, 0x4D, 0x41, 0x98, 0x15, 0x59, 0xEA, 0x0B, 0x96, 0xFA, 0x12, 0x4B, 0xA7, 0x77, 0xB9, 0xA9, 0xF0, 0x4E, 0x38, 0xE2, 0xEB, 0xB8, 0x5F, 0x09, - 0x4B, 0x95, 0xF6, 0x49, 0x52, 0xDB, 0xBE, 0xAB, 0x6D, 0x92, 0xB4, 0x49, 0x3A, 0x18, 0x1E, 0x17, 0xC6, 0x42, 0x0A, 0xC2, 0x7C, 0xFE, 0xD5, 0x5D, 0xFA, 0xFC, - 0x78, 0x0B, 0x9F, 0x1F, 0x67, 0x7C, 0x7E, 0x87, 0xCE, 0x2E, 0x08, 0xFF, 0xC6, 0x1C, 0x5E, 0xB0, 0xB5, 0x86, 0xD3, 0x2B, 0xD9, 0xDA, 0xAD, 0x87, 0x44, 0x96, - 0xF0, 0xEA, 0x2E, 0x3D, 0x24, 0xC7, 0x6E, 0x37, 0x32, 0x52, 0x1E, 0x73, 0x06, 0xBB, 0xC9, 0x49, 0xB4, 0x92, 0x92, 0xD5, 0xC9, 0x7B, 0xC7, 0x8D, 0x86, 0x47, - 0x7D, 0x5E, 0x36, 0xDD, 0x85, 0x7A, 0xAA, 0xEF, 0xC7, 0xCE, 0x6D, 0x72, 0x37, 0x45, 0x19, 0xBE, 0x11, 0x61, 0x26, 0x55, 0xB8, 0x95, 0x0B, 0xB3, 0xCB, 0xB7, - 0x7F, 0x5F, 0xAF, 0x16, 0x4B, 0xF7, 0xB4, 0xBB, 0x7A, 0x6C, 0x33, 0x6B, 0x95, 0x05, 0xC6, 0x69, 0x87, 0x88, 0x83, 0x6F, 0x26, 0xE8, 0x7D, 0x8A, 0x38, 0x57, - 0x8C, 0x0D, 0xA5, 0xA0, 0x10, 0x81, 0xE5, 0x0D, 0xFA, 0x68, 0x20, 0xE4, 0x73, 0x96, 0x70, 0x2C, 0xE2, 0x9A, 0xE6, 0x8D, 0x46, 0xF4, 0x73, 0x57, 0x8F, 0x31, - 0x60, 0x04, 0x5F, 0xF0, 0x7A, 0xA7, 0x1B, 0x91, 0xA4, 0x1A, 0xF2, 0xC5, 0x14, 0x46, 0xB4, 0x51, 0x13, 0xE3, 0x86, 0x76, 0x67, 0x22, 0x38, 0x62, 0x22, 0x78, - 0xF1, 0xE6, 0xA3, 0x4A, 0x06, 0xCC, 0xD7, 0x3A, 0x59, 0x11, 0x1C, 0x6D, 0xFE, 0x6E, 0x85, 0x6E, 0x65, 0x69, 0x75, 0x62, 0x69, 0x1D, 0x8D, 0xE2, 0x2D, 0xA2, - 0xDB, 0x84, 0x2C, 0x85, 0x04, 0x8E, 0xD9, 0x43, 0xE0, 0xDA, 0x7B, 0xD9, 0x03, 0x2A, 0xD9, 0xC1, 0xF1, 0x3A, 0x76, 0x60, 0x1D, 0x6D, 0x61, 0x06, 0xC7, 0x39, - 0x66, 0x70, 0x57, 0x32, 0xE8, 0xEB, 0x83, 0xF7, 0x9B, 0x98, 0x41, 0xBF, 0xA2, 0x19, 0x1C, 0x09, 0x33, 0x88, 0xF7, 0x0F, 0xF7, 0xAB, 0x0A, 0x4B, 0xB2, 0x82, - 0xC7, 0x23, 0x7C, 0x6C, 0xE6, 0x71, 0x35, 0x4F, 0xD8, 0x5D, 0xCC, 0x5D, 0xD8, 0xEE, 0xFA, 0xF1, 0xF6, 0x57, 0xDB, 0xB5, 0xBC, 0xC5, 0x7A, 0x21, 0x57, 0xEE, - 0xE8, 0x6B, 0x0F, 0xB7, 0xEB, 0x8D, 0x5A, 0x71, 0x66, 0xA7, 0xB5, 0xC4, 0xCA, 0xDE, 0x0D, 0x3C, 0x5F, 0xCB, 0xBE, 0x39, 0x24, 0xB1, 0x01, 0x51, 0xB4, 0xAE, - 0x56, 0x04, 0x64, 0xB7, 0x5C, 0xBC, 0x79, 0xA9, 0x6D, 0xF0, 0x5A, 0x07, 0x05, 0xB2, 0x2E, 0x7B, 0xF9, 0x85, 0xB6, 0xC1, 0xDB, 0x2F, 0x14, 0xD8, 0x72, 0xB6, - 0xA8, 0xE0, 0x8B, 0x48, 0xB4, 0xCD, 0xDE, 0x44, 0x52, 0xBA, 0x5B, 0x83, 0xB5, 0xDA, 0x3C, 0xA5, 0x44, 0xE3, 0x32, 0xE6, 0xAD, 0x50, 0x63, 0x55, 0xDB, 0x62, - 0x4F, 0x71, 0x4A, 0x06, 0xC0, 0xC1, 0x97, 0x74, 0x37, 0x4B, 0x40, 0xAA, 0x6D, 0x4A, 0x4D, 0x13, 0xB6, 0x46, 0x65, 0xF8, 0xE9, 0x4C, 0x0A, 0x66, 0x51, 0xE7, - 0x6B, 0x06, 0xB3, 0xB8, 0xCE, 0x07, 0x63, 0xDA, 0xFB, 0xE0, 0xE5, 0x9F, 0x0A, 0x96, 0x56, 0x9B, 0xB3, 0x74, 0x74, 0x57, 0x2C, 0x6D, 0x91, 0xAA, 0x22, 0xEB, - 0x0A, 0xBD, 0xD0, 0x70, 0x36, 0x36, 0x2E, 0x06, 0x0D, 0xB6, 0xC5, 0x62, 0xAE, 0x76, 0x0D, 0xAC, 0xEE, 0xD4, 0xC0, 0x04, 0x01, 0xD5, 0x94, 0xD1, 0xCF, 0x2A, - 0xE3, 0xF4, 0x6B, 0xB3, 0x2F, 0xC6, 0x51, 0x55, 0xF3, 0x52, 0x70, 0x74, 0xF2, 0x35, 0x99, 0x97, 0x37, 0x0F, 0xF1, 0xEA, 0xC6, 0xC1, 0x8B, 0x81, 0x63, 0xF0, - 0xA2, 0x47, 0xBB, 0x37, 0xB0, 0x88, 0x82, 0x8D, 0xF5, 0x71, 0x74, 0xA7, 0xAF, 0x3C, 0xBB, 0x8B, 0x08, 0xC6, 0x58, 0xDA, 0xC2, 0xC4, 0x7A, 0xFD, 0x1D, 0x9A, - 0x98, 0xB4, 0xD0, 0xC4, 0xF3, 0x20, 0x2F, 0x60, 0x74, 0xBE, 0x36, 0x10, 0x17, 0x34, 0xEB, 0xAC, 0x24, 0xA9, 0xB3, 0xF2, 0xF9, 0x21, 0x14, 0x85, 0x59, 0x04, - 0x39, 0x74, 0x9E, 0xB3, 0x2F, 0x27, 0xAA, 0x3B, 0x8C, 0xDF, 0x74, 0x48, 0x97, 0xD5, 0xE2, 0x77, 0xEA, 0x46, 0x85, 0x66, 0xFA, 0x5D, 0xBB, 0xA5, 0x6F, 0x15, - 0x3C, 0x37, 0xF8, 0x2B, 0x38, 0x6E, 0x08, 0x5F, 0x03, 0xD4, 0x26, 0x3E, 0x19, 0x5D, 0xE8, 0xDF, 0x45, 0x38, 0xB9, 0xB4, 0xB0, 0x89, 0xAE, 0x41, 0x48, 0x76, - 0x1D, 0xCF, 0xC0, 0x62, 0xD5, 0x98, 0x85, 0x40, 0x69, 0xFB, 0x8F, 0x19, 0x4E, 0xF2, 0x1A, 0xF8, 0xBE, 0x06, 0xA3, 0xDA, 0x4A, 0x33, 0x7D, 0x3B, 0x2F, 0x7F, - 0xB0, 0x06, 0x0F, 0xA3, 0x35, 0xC3, 0xFF, 0xF9, 0xEF, 0xB2, 0xA9, 0x19, 0xFC, 0x7E, 0x66, 0x2C, 0x00, 0x30, 0x23, 0xDF, 0xBC, 0xD0, 0x81, 0x52, 0xDF, 0x0B, - 0xA0, 0x14, 0xB5, 0xC7, 0x76, 0x8E, 0xAA, 0xF2, 0xA4, 0x7D, 0xA8, 0x12, 0x77, 0xAA, 0xB1, 0x62, 0x6C, 0x72, 0x1E, 0x98, 0xBE, 0x3D, 0x83, 0x52, 0xCD, 0xF2, - 0xCC, 0xF9, 0x94, 0xB8, 0x61, 0xDB, 0xB0, 0xAC, 0xAB, 0x1B, 0x38, 0x78, 0x8B, 0x33, 0xCC, 0x20, 0xF9, 0x7A, 0xED, 0xC5, 0x4F, 0xEF, 0x2E, 0xD9, 0x3B, 0x2B, - 0xDF, 0x82, 0xBC, 0x88, 0x55, 0x6B, 0x6A, 0xA3, 0xB9, 0xCB, 0xAA, 0xF7, 0x3A, 0xC1, 0xB6, 0xEC, 0x3B, 0xA6, 0x37, 0x86, 0xAF, 0x0D, 0x8D, 0x80, 0xBC, 0xF6, - 0x82, 0x50, 0xBB, 0xD0, 0x22, 0x8C, 0x8E, 0x67, 0xD2, 0x77, 0xA2, 0xB4, 0x19, 0x5F, 0xBC, 0x25, 0x63, 0xFC, 0x17, 0xDF, 0x81, 0xA6, 0x11, 0xD4, 0x81, 0x56, - 0x3B, 0x3B, 0xED, 0xD6, 0xD0, 0xFE, 0xA2, 0x2E, 0x46, 0xF8, 0x65, 0x52, 0x68, 0x57, 0x9F, 0xFB, 0x4E, 0x53, 0x33, 0x87, 0x0D, 0xF6, 0x9E, 0x51, 0x7A, 0x19, - 0xAF, 0x89, 0x17, 0x50, 0xB7, 0xC3, 0x09, 0x71, 0xEB, 0x31, 0x65, 0xE0, 0x0C, 0x33, 0xCF, 0x0D, 0x12, 0x1F, 0x59, 0xB5, 0x47, 0xF1, 0xF5, 0x36, 0x14, 0xF4, - 0xE1, 0x3C, 0xD0, 0x1E, 0x5E, 0x5C, 0x68, 0x58, 0xE0, 0x26, 0xDE, 0x5F, 0x6A, 0x0E, 0xD3, 0xED, 0x9A, 0x5A, 0xEA, 0xC2, 0xCF, 0x10, 0x1A, 0xA4, 0x37, 0x65, - 0xDF, 0x6A, 0xC4, 0x49, 0xBD, 0xAA, 0x39, 0x02, 0xC0, 0x28, 0x52, 0x6F, 0x24, 0x09, 0xAC, 0x5B, 0x46, 0x68, 0x34, 0x92, 0xEF, 0x4C, 0x85, 0x5E, 0x81, 0x92, - 0xA6, 0x46, 0x6F, 0xC9, 0x2F, 0x70, 0xBD, 0x6D, 0xB4, 0x41, 0x86, 0xC0, 0x6F, 0x04, 0x4D, 0x7C, 0x3F, 0xFD, 0xF5, 0x58, 0x80, 0x6E, 0x75, 0x9B, 0x1A, 0xDE, - 0x49, 0xC2, 0x4A, 0x44, 0x3E, 0x10, 0xD7, 0x84, 0xD0, 0x8A, 0xD1, 0x2A, 0x50, 0x32, 0x74, 0xB7, 0x09, 0x15, 0x41, 0xEC, 0xF9, 0x40, 0xC6, 0x20, 0xB1, 0x71, - 0x93, 0x0F, 0xA0, 0x9B, 0x74, 0xF4, 0xDC, 0x64, 0x41, 0x51, 0xD2, 0xDA, 0xE1, 0x21, 0xB8, 0x34, 0x04, 0x25, 0x02, 0x56, 0x31, 0xAE, 0xD7, 0xF8, 0x02, 0x26, - 0x58, 0x54, 0xAD, 0xB3, 0xAC, 0x1D, 0x00, 0x82, 0x76, 0xE8, 0x5D, 0x87, 0xBE, 0xED, 0x8E, 0x61, 0xE8, 0xD1, 0x88, 0xB1, 0xD1, 0xDB, 0x88, 0x32, 0x75, 0x9F, - 0x5E, 0xA7, 0x9D, 0xA4, 0x6F, 0xD4, 0xF9, 0xF5, 0x83, 0x5A, 0xA3, 0xC6, 0x89, 0xA7, 0xE7, 0x60, 0x6E, 0x75, 0x76, 0xF0, 0x88, 0xD2, 0xD8, 0xD0, 0xCE, 0xCF, - 0x79, 0x37, 0xAC, 0x15, 0x5E, 0x84, 0x46, 0xF4, 0x4F, 0xEA, 0x56, 0x64, 0x8A, 0xBF, 0x7F, 0xFF, 0x97, 0xB0, 0xD9, 0xDB, 0x43, 0xA0, 0xFA, 0x29, 0xCE, 0x20, - 0x7C, 0xFF, 0x17, 0xFC, 0xFF, 0xF6, 0x11, 0x9D, 0x36, 0xF8, 0xFE, 0x2F, 0xFC, 0x73, 0xFB, 0x08, 0x7A, 0x82, 0x63, 0xDA, 0xDF, 0xED, 0xEF, 0x54, 0x0E, 0x59, - 0xE9, 0x8D, 0x73, 0xA5, 0x17, 0x89, 0x6D, 0x6D, 0x9A, 0xC6, 0x05, 0x44, 0xFD, 0x1E, 0xFB, 0x6F, 0xDD, 0xF4, 0x2C, 0x50, 0x4F, 0x08, 0x96, 0x2C, 0x94, 0xEE, - 0x80, 0x4A, 0x84, 0xA0, 0xA2, 0x97, 0x08, 0xDB, 0x23, 0xDA, 0x52, 0xE3, 0xAE, 0x12, 0x1B, 0x88, 0x68, 0x39, 0x33, 0xFC, 0x80, 0xBC, 0x71, 0xC3, 0x7A, 0x98, - 0x70, 0x8A, 0x1C, 0x89, 0x0F, 0x06, 0x09, 0x16, 0xF0, 0x07, 0x70, 0xD0, 0xAE, 0xC6, 0x95, 0x16, 0x19, 0xDB, 0x83, 0xC8, 0x0E, 0x63, 0x4A, 0xD9, 0xCD, 0x1C, - 0x3B, 0xFC, 0x64, 0x3A, 0x5F, 0xEA, 0xF8, 0x5A, 0xD3, 0x74, 0xA8, 0xC8, 0x88, 0x08, 0x1B, 0x3D, 0xC5, 0xFF, 0x81, 0x5C, 0xF0, 0x4F, 0xAE, 0x7E, 0x00, 0x2B, - 0x2B, 0xE1, 0xEB, 0x74, 0x0A, 0xE0, 0xF3, 0xB2, 0xA9, 0xB1, 0x83, 0x15, 0x78, 0x86, 0x6B, 0xE1, 0x39, 0xFE, 0x59, 0x09, 0xED, 0xE1, 0x05, 0x7E, 0x04, 0xD7, - 0x68, 0xCD, 0x8A, 0x97, 0xD8, 0x01, 0xB6, 0xA2, 0x35, 0x06, 0x6D, 0xC5, 0x8E, 0xE0, 0x1A, 0xBE, 0xE2, 0x07, 0x6C, 0xB7, 0xA9, 0x0D, 0x6D, 0xD7, 0xA5, 0x07, - 0x25, 0xD4, 0xC7, 0xA9, 0xFE, 0x69, 0xB0, 0x04, 0x0E, 0x38, 0x69, 0xB7, 0x8F, 0x82, 0x55, 0x74, 0xB6, 0xBA, 0x7D, 0x44, 0xF0, 0x1E, 0x25, 0x12, 0x8E, 0x57, - 0xFC, 0x18, 0xAE, 0x03, 0x7D, 0x78, 0x47, 0x10, 0x4C, 0x2F, 0xAC, 0xE2, 0x0B, 0xD0, 0x22, 0xC4, 0xFB, 0x9C, 0x78, 0x38, 0x5B, 0x45, 0x67, 0x08, 0x4D, 0x61, - 0x39, 0x1B, 0x70, 0xBA, 0x8A, 0x4F, 0xE1, 0x2E, 0x7D, 0x5D, 0x11, 0x12, 0xC1, 0x78, 0xBA, 0x7D, 0xC4, 0x79, 0x82, 0x4B, 0xFC, 0x28, 0x2D, 0x6A, 0x8C, 0x09, - 0x21, 0x8F, 0x22, 0xCF, 0x59, 0x92, 0x96, 0xF2, 0x07, 0xF8, 0xC7, 0x95, 0x43, 0xF0, 0xF0, 0xF9, 0xEA, 0x8D, 0x55, 0xAF, 0xF1, 0x05, 0xD9, 0x1A, 0xC6, 0x30, - 0x19, 0xA6, 0xED, 0xB9, 0xA6, 0x63, 0x9B, 0xE8, 0x28, 0xF5, 0x86, 0x76, 0x31, 0xE0, 0x71, 0x0C, 0x0D, 0x1A, 0x9A, 0xCB, 0x46, 0x9A, 0x8B, 0x5A, 0x2C, 0x29, - 0xD6, 0x1A, 0x6D, 0x6A, 0x87, 0xDC, 0xD6, 0x10, 0x05, 0x77, 0xC1, 0x6A, 0x38, 0xB0, 0xB1, 0x02, 0x47, 0xC6, 0x5B, 0x0A, 0x91, 0xD0, 0xD6, 0x12, 0x16, 0x8A, - 0x46, 0x0E, 0xB5, 0x9D, 0x54, 0x94, 0x2D, 0xF0, 0x6A, 0xE1, 0xC0, 0x0F, 0xD3, 0x0E, 0x0C, 0xAA, 0xF2, 0xC3, 0x7A, 0xED, 0x0A, 0x9F, 0xFB, 0xFF, 0xAD, 0x76, - 0x80, 0x8D, 0x0E, 0x6A, 0xFF, 0x3A, 0xD3, 0x6A, 0x07, 0xB2, 0x27, 0xDF, 0xA6, 0x5D, 0x8E, 0x69, 0x6C, 0x5C, 0x51, 0x63, 0x63, 0x49, 0x63, 0xE3, 0xBB, 0xD5, - 0x98, 0xBC, 0x10, 0xBC, 0x8D, 0xD6, 0xE4, 0x95, 0xD7, 0x02, 0xCD, 0x95, 0xC2, 0x73, 0xA5, 0x71, 0x6D, 0x8D, 0x55, 0xDA, 0xDA, 0x44, 0x4D, 0x2C, 0xC5, 0x81, - 0xF7, 0x10, 0xFF, 0xF5, 0xCF, 0xEF, 0xDE, 0x62, 0xA8, 0x54, 0xAB, 0x2C, 0xD2, 0x58, 0xBA, 0x1C, 0x51, 0x60, 0xC0, 0xDC, 0x99, 0x08, 0xDC, 0x89, 0x1C, 0x7A, - 0x50, 0xD3, 0xEA, 0x14, 0x25, 0x66, 0xD0, 0x12, 0x43, 0xE0, 0x81, 0xB7, 0x9A, 0xEF, 0x62, 0xB0, 0x15, 0xCE, 0x1B, 0x43, 0x15, 0xD8, 0x02, 0x02, 0x54, 0x52, - 0x22, 0xC3, 0x9C, 0x71, 0x18, 0x29, 0x27, 0xEC, 0xDC, 0x45, 0xA8, 0xBF, 0x06, 0x55, 0x83, 0x9A, 0x88, 0xE9, 0x71, 0x6C, 0x0B, 0x4A, 0xA5, 0xC3, 0x23, 0x7F, - 0x25, 0x01, 0xF1, 0x39, 0x6C, 0x85, 0x81, 0x8B, 0x4C, 0x50, 0x09, 0x8D, 0x98, 0xBA, 0xCC, 0xC7, 0xB3, 0x5A, 0x07, 0xCF, 0x4A, 0x81, 0x87, 0x67, 0x9E, 0x4A, - 0x68, 0xF8, 0x44, 0x57, 0x2E, 0x96, 0x6A, 0xC4, 0xF0, 0xC9, 0x25, 0x15, 0x4F, 0x3C, 0xD3, 0x55, 0xE3, 0x89, 0x4F, 0x8A, 0xE4, 0xE3, 0xA9, 0x28, 0x1B, 0x3E, - 0x13, 0xA1, 0xB0, 0xE7, 0x74, 0x35, 0xD2, 0x11, 0xFF, 0x6D, 0x5E, 0x7F, 0x8C, 0x0C, 0x88, 0x16, 0xF1, 0x9F, 0xBD, 0x78, 0xC9, 0xF8, 0xA3, 0xA8, 0x16, 0x89, - 0xA3, 0xCC, 0x06, 0xC4, 0x69, 0x1B, 0x21, 0xC4, 0x27, 0x18, 0xC7, 0x93, 0xA0, 0x8D, 0x15, 0x6E, 0x24, 0xC6, 0xCC, 0xAD, 0xB6, 0x0B, 0x04, 0x50, 0x84, 0x8D, - 0xB3, 0x4E, 0xDA, 0x3C, 0x33, 0xB8, 0xD8, 0xE5, 0x3C, 0x74, 0xEC, 0x6E, 0x0E, 0x46, 0x9E, 0x61, 0x92, 0x10, 0x78, 0x31, 0x0F, 0x1B, 0x1D, 0xC5, 0x48, 0xB8, - 0x7A, 0xC7, 0xC7, 0xD9, 0x3C, 0xC3, 0x3B, 0xE0, 0x5F, 0x4F, 0x43, 0x0C, 0x38, 0x1F, 0x15, 0x0F, 0xCD, 0x4C, 0x28, 0x04, 0xB5, 0x9A, 0x58, 0xB2, 0xAB, 0x9D, - 0x65, 0x2A, 0x6E, 0x80, 0xE0, 0x8B, 0x70, 0xDA, 0x53, 0x46, 0x63, 0xE2, 0x9B, 0x20, 0x43, 0x18, 0x7C, 0x47, 0x9F, 0xFE, 0x62, 0xC8, 0xE8, 0x3E, 0x8A, 0x08, - 0x13, 0xBB, 0x86, 0x03, 0xD7, 0xD4, 0x25, 0xB6, 0xB6, 0xD3, 0xF2, 0x5C, 0xA2, 0xEE, 0x35, 0x51, 0xBF, 0xF3, 0x8E, 0xF8, 0x19, 0x7F, 0x38, 0x3D, 0x06, 0xF3, - 0x49, 0x38, 0xF7, 0x5D, 0x5E, 0xCF, 0x67, 0xEB, 0x1B, 0xE5, 0x50, 0x72, 0x87, 0xB6, 0x79, 0x78, 0xA8, 0x3D, 0x0B, 0x43, 0x03, 0x14, 0x80, 0xEB, 0x94, 0x13, - 0x94, 0x8F, 0x66, 0xF0, 0x49, 0x09, 0xCF, 0x47, 0xA3, 0x64, 0x0F, 0x15, 0x13, 0xE6, 0xB7, 0xF8, 0xA5, 0x34, 0xE1, 0xCE, 0x14, 0x55, 0xFB, 0xDF, 0x73, 0xE2, - 0xAF, 0xAE, 0xA9, 0xC0, 0x3C, 0xFF, 0x99, 0xE3, 0xD4, 0x6B, 0xED, 0x78, 0xD9, 0xB9, 0xC6, 0xC6, 0xE0, 0x6D, 0x40, 0x75, 0x05, 0x7D, 0x80, 0x8E, 0x63, 0x9B, - 0x67, 0xDC, 0x44, 0x7A, 0x87, 0x71, 0xD7, 0x05, 0x57, 0x46, 0x7A, 0xD0, 0x0F, 0x2D, 0x3C, 0xF7, 0x0B, 0x59, 0xCD, 0x67, 0x20, 0xFE, 0x78, 0x18, 0x9F, 0x9A, - 0x58, 0xE0, 0xD2, 0x21, 0x6D, 0x68, 0x79, 0xC9, 0x07, 0x72, 0xDD, 0x23, 0x45, 0xA3, 0x58, 0x05, 0xD4, 0x3A, 0xD1, 0x13, 0xB3, 0x1F, 0x92, 0xB9, 0x7D, 0xA0, - 0x3E, 0x53, 0x4C, 0x81, 0x70, 0x02, 0xB9, 0xF0, 0x44, 0xF2, 0x4A, 0xF5, 0x90, 0x9A, 0x9E, 0xB8, 0x6D, 0x3C, 0x88, 0x23, 0xC3, 0x7C, 0x66, 0x19, 0x21, 0x49, - 0x06, 0x87, 0xC8, 0x16, 0xC4, 0xCD, 0xA9, 0x17, 0x92, 0x54, 0xC4, 0xB0, 0x71, 0x6F, 0x86, 0xE1, 0x7C, 0x8C, 0xAD, 0xF1, 0x5E, 0xDD, 0x5F, 0xE1, 0xE3, 0x6B, - 0xF8, 0x7F, 0x66, 0x0E, 0xA2, 0xDA, 0xB8, 0x39, 0x63, 0x21, 0x51, 0x3C, 0x88, 0xAD, 0x44, 0x96, 0x43, 0x22, 0x2C, 0xF0, 0xFB, 0xA2, 0xA7, 0x87, 0x0F, 0xE9, - 0xD1, 0x83, 0x48, 0x69, 0x22, 0x7A, 0x5C, 0x68, 0xF1, 0x8D, 0x94, 0x82, 0xB3, 0xB8, 0x53, 0x38, 0x04, 0x72, 0x09, 0x03, 0xF3, 0xAD, 0x48, 0xBD, 0x33, 0xA8, - 0x36, 0xD1, 0x16, 0xFE, 0x3F, 0xEA, 0x7F, 0x45, 0x51, 0xFF, 0xFE, 0x42, 0x7C, 0x81, 0x6D, 0xA7, 0x3C, 0x80, 0xC1, 0xA9, 0xA7, 0x05, 0x0F, 0x6A, 0x50, 0xED, - 0x28, 0xE7, 0xFD, 0x78, 0xE8, 0x8E, 0xED, 0x6B, 0x62, 0x5B, 0x8C, 0xE8, 0xD8, 0xB2, 0x50, 0x46, 0x38, 0x7D, 0x8F, 0x13, 0xDC, 0x38, 0xDB, 0x5D, 0xAF, 0xB1, - 0xB5, 0x05, 0x1A, 0x8F, 0x6F, 0xE3, 0x92, 0x64, 0xE2, 0x2D, 0x8A, 0x20, 0x7D, 0x88, 0x3A, 0x37, 0x24, 0x05, 0x1C, 0x41, 0xF3, 0xED, 0x3C, 0xA5, 0x5D, 0x8B, - 0x6D, 0x3F, 0x3C, 0x19, 0x40, 0x03, 0x71, 0x05, 0x40, 0x43, 0x9F, 0x7A, 0x8D, 0x84, 0x96, 0xB8, 0x65, 0x58, 0x05, 0x59, 0x85, 0x88, 0x69, 0x99, 0x97, 0xC4, - 0xCC, 0x42, 0xE9, 0x1A, 0x41, 0x56, 0xBE, 0x0C, 0x10, 0xC9, 0xD3, 0x0B, 0xCD, 0x9D, 0x3B, 0x0E, 0xD8, 0x20, 0xB2, 0x00, 0x36, 0x28, 0xDF, 0x55, 0x86, 0xE8, - 0xFF, 0xDC, 0x78, 0x16, 0x51, 0x9E, 0x90, 0xC0, 0xA3, 0x47, 0x49, 0x6C, 0xB8, 0xC8, 0xC0, 0xCA, 0xF8, 0xA8, 0x37, 0xD6, 0xFE, 0xD2, 0x73, 0x47, 0xF6, 0x38, - 0xCE, 0xB3, 0x9C, 0x24, 0x48, 0xD6, 0x0F, 0x13, 0x82, 0x97, 0x6A, 0x1C, 0x20, 0xC4, 0xB6, 0xA8, 0x80, 0xE8, 0x8B, 0x39, 0x33, 0xB3, 0xB1, 0x4F, 0xA9, 0xD5, - 0xD7, 0x09, 0x7F, 0x65, 0x5F, 0x03, 0xE4, 0x8F, 0xC6, 0x1C, 0x5F, 0x10, 0xF5, 0x4E, 0xD4, 0x95, 0x8C, 0x71, 0x9C, 0xC0, 0x88, 0x8C, 0xA5, 0xE8, 0xC6, 0x1F, - 0xC5, 0x87, 0x2F, 0xAF, 0xE2, 0x2F, 0xD0, 0x92, 0x3F, 0x02, 0x4A, 0x3B, 0x07, 0x34, 0xF8, 0x82, 0x2D, 0x29, 0xB5, 0x67, 0x6B, 0x03, 0xDA, 0x30, 0x07, 0x09, - 0xED, 0x20, 0x8B, 0xA4, 0x90, 0x72, 0xF1, 0xF6, 0x62, 0x85, 0x40, 0x28, 0xBA, 0xC5, 0x10, 0x45, 0x41, 0x7B, 0x85, 0xC3, 0x22, 0x54, 0xA9, 0x9D, 0x8F, 0x0A, - 0x84, 0xCC, 0x11, 0xEB, 0x6C, 0x63, 0x1F, 0x1B, 0xA2, 0x23, 0x72, 0xEE, 0x63, 0xC9, 0xEB, 0xB9, 0x3D, 0x69, 0xA9, 0x4D, 0xD2, 0x99, 0x7E, 0x06, 0x5A, 0xAB, - 0x2B, 0xA8, 0x87, 0xA6, 0xAF, 0xF0, 0x29, 0x81, 0x88, 0x87, 0xE8, 0x42, 0xB2, 0x7C, 0x15, 0x6E, 0x1D, 0x4D, 0x5F, 0xCB, 0x76, 0xC6, 0x92, 0x6C, 0x9C, 0x61, - 0x63, 0x43, 0xBE, 0xA3, 0x9C, 0xD3, 0xD5, 0xE4, 0xEF, 0x0B, 0xCA, 0x99, 0x61, 0xCB, 0x74, 0x53, 0x80, 0x93, 0x2D, 0xEB, 0xA6, 0x91, 0xCE, 0x87, 0x53, 0x3B, - 0x54, 0x20, 0xAC, 0x75, 0x6B, 0xEB, 0x64, 0x2E, 0xD9, 0xCB, 0x59, 0xA4, 0xA4, 0x45, 0x3D, 0x20, 0x4A, 0x4C, 0xC7, 0xD3, 0xEF, 0x71, 0x78, 0xCE, 0xD3, 0x1B, - 0xC3, 0xC7, 0x49, 0x76, 0x54, 0x70, 0x6A, 0xF1, 0x87, 0xA1, 0x60, 0xAB, 0x96, 0x14, 0x45, 0x72, 0xDD, 0x52, 0xAC, 0x15, 0x26, 0xC7, 0x00, 0xF2, 0x62, 0xD9, - 0xEF, 0x3E, 0x01, 0xB8, 0x00, 0x27, 0x35, 0xB4, 0xEF, 0xFF, 0xA2, 0x28, 0x6E, 0xB5, 0x11, 0x44, 0x99, 0x60, 0x42, 0x2C, 0xBA, 0x24, 0x11, 0xE2, 0x27, 0x39, - 0x71, 0xC1, 0x27, 0xB1, 0x4E, 0x79, 0xFB, 0x7B, 0x64, 0x21, 0x51, 0x92, 0x2A, 0x1D, 0xA6, 0xD0, 0xE5, 0xEC, 0xE2, 0x11, 0x0A, 0x2B, 0xEC, 0x15, 0x93, 0x52, - 0xF8, 0x63, 0x11, 0xC8, 0x69, 0x43, 0x35, 0x04, 0xDD, 0xFC, 0x08, 0x75, 0x4F, 0xCA, 0x4C, 0x1B, 0x7C, 0x80, 0x05, 0x1A, 0xB0, 0x44, 0xA8, 0x64, 0x3A, 0xC2, - 0x61, 0x14, 0x13, 0x53, 0x42, 0xC2, 0x8C, 0x19, 0xCE, 0x4B, 0xF9, 0x5A, 0x2F, 0xAF, 0x1F, 0x22, 0x59, 0xFC, 0x11, 0xC0, 0xB0, 0xA8, 0xF1, 0x20, 0x12, 0x43, - 0x16, 0x07, 0x76, 0x20, 0x21, 0x48, 0x88, 0x28, 0x4F, 0x4C, 0xC9, 0xED, 0xD6, 0xB5, 0x38, 0x52, 0xE5, 0x8E, 0xEA, 0x34, 0x39, 0xD3, 0xD2, 0x34, 0x4B, 0xFB, - 0xFD, 0x8D, 0x9A, 0xCC, 0xBF, 0xF8, 0x0C, 0x8C, 0x14, 0xF1, 0x1A, 0xEB, 0x90, 0x93, 0x19, 0x5C, 0x96, 0x90, 0x72, 0x87, 0x65, 0xB6, 0xF8, 0x41, 0x78, 0xA3, - 0xF8, 0xA0, 0x98, 0xCD, 0x0C, 0x2F, 0xE5, 0x5A, 0x30, 0x62, 0x30, 0x71, 0x96, 0x1C, 0xED, 0x49, 0xE2, 0x51, 0xD6, 0x81, 0xB9, 0xE2, 0x62, 0xD6, 0xC5, 0xDC, - 0xF5, 0xC6, 0x26, 0x8B, 0xC2, 0x89, 0x57, 0xFA, 0x1C, 0x02, 0x95, 0x57, 0x0C, 0x70, 0x19, 0x3D, 0x77, 0x52, 0x0A, 0x19, 0x3F, 0xA3, 0x22, 0xE1, 0xA0, 0x0F, - 0xA0, 0x54, 0x5B, 0x17, 0xA1, 0x4D, 0x13, 0xA0, 0x88, 0xB5, 0x1C, 0x36, 0xB1, 0x4B, 0x5D, 0x82, 0x97, 0x53, 0x4F, 0x11, 0xBC, 0xB4, 0x31, 0x5D, 0x82, 0xA6, - 0x7E, 0x5F, 0x0E, 0x2C, 0x3F, 0xED, 0x22, 0xD3, 0x6E, 0xDC, 0x54, 0x00, 0x8E, 0x1F, 0xD1, 0x91, 0x40, 0x45, 0x36, 0x2B, 0x02, 0x8C, 0x5E, 0x8A, 0x52, 0x93, - 0xF4, 0x1B, 0x84, 0xDE, 0x8C, 0x6D, 0xCE, 0x4F, 0x85, 0xA1, 0x05, 0x9D, 0x25, 0x6D, 0xE3, 0xFD, 0x3A, 0x2F, 0xAE, 0x64, 0xD9, 0x26, 0x97, 0x42, 0xE4, 0x4D, - 0xFE, 0xB5, 0x64, 0x65, 0x4C, 0xA7, 0x59, 0x95, 0x3D, 0xA0, 0xA5, 0xB4, 0x03, 0xDF, 0x64, 0xC9, 0x20, 0x7A, 0xA0, 0x05, 0x63, 0x15, 0x1E, 0xFE, 0xCE, 0xFA, - 0xC4, 0xE4, 0x9D, 0xB0, 0xA9, 0x46, 0x29, 0x2D, 0xDE, 0x2C, 0x4D, 0x4A, 0x3C, 0x15, 0xC5, 0xFC, 0x3B, 0xC0, 0x2C, 0xC0, 0x5F, 0x90, 0xF0, 0x40, 0x93, 0xED, - 0x2D, 0x27, 0x28, 0xC7, 0x62, 0xE2, 0x51, 0x30, 0x49, 0xBC, 0x94, 0xC9, 0xD8, 0xD3, 0x50, 0x4F, 0x3F, 0x9B, 0x43, 0x48, 0x5E, 0x2F, 0xC0, 0xF9, 0xC0, 0xDB, - 0x17, 0xF5, 0xC6, 0x6D, 0x11, 0x3B, 0x4C, 0x5C, 0xB1, 0xED, 0x54, 0x25, 0x82, 0xA6, 0x09, 0x35, 0xB6, 0x84, 0x7C, 0xD4, 0xE8, 0x64, 0x87, 0xB9, 0x72, 0xC5, - 0xE0, 0x26, 0x4F, 0xB0, 0x17, 0x59, 0xD1, 0xB2, 0xFA, 0x36, 0x81, 0x20, 0x4E, 0x00, 0x19, 0x62, 0x53, 0x25, 0xAC, 0x64, 0x17, 0xA2, 0x81, 0xA0, 0x5D, 0xF6, - 0xC1, 0x1C, 0xDA, 0x93, 0xD5, 0x7F, 0xAA, 0x5C, 0x64, 0x02, 0x88, 0x9C, 0x29, 0x07, 0x05, 0x3E, 0x45, 0x65, 0x1A, 0xEE, 0x8D, 0x11, 0xC8, 0x4E, 0x63, 0x02, - 0x41, 0x21, 0xE1, 0x7E, 0x53, 0xD7, 0x59, 0x03, 0x9D, 0x3B, 0x00, 0x3B, 0x6B, 0xD3, 0x6D, 0x27, 0x38, 0x8E, 0x41, 0x1B, 0xA0, 0x27, 0x89, 0xDB, 0xEC, 0x43, - 0xD1, 0xE2, 0x3E, 0x3B, 0x63, 0x0D, 0xA2, 0x5E, 0xF0, 0xD3, 0xCC, 0x6D, 0x63, 0x36, 0x23, 0xAE, 0x75, 0x39, 0xB1, 0x1D, 0xAB, 0xCE, 0x40, 0xA3, 0x07, 0x6D, - 0x7C, 0x8D, 0x7E, 0xE6, 0x98, 0x3E, 0xD4, 0xC1, 0xB1, 0x82, 0x37, 0x5F, 0xB2, 0x6B, 0xF5, 0x5A, 0xCF, 0x12, 0xCF, 0xE4, 0xF0, 0x66, 0x6D, 0xCB, 0x37, 0x16, - 0x6F, 0xF0, 0x99, 0x3F, 0x6A, 0x0E, 0xCD, 0x4E, 0xB3, 0xC3, 0x1B, 0x84, 0x50, 0x6C, 0x09, 0x91, 0x23, 0x5E, 0x7C, 0x36, 0xEA, 0x97, 0x0F, 0x6F, 0x63, 0xBC, - 0xA1, 0xF7, 0x82, 0x5D, 0xAA, 0xD7, 0xE8, 0x43, 0x83, 0x87, 0x7F, 0xCC, 0x70, 0xA9, 0x59, 0xA4, 0x19, 0x49, 0x8C, 0xF8, 0x3C, 0x20, 0x8A, 0x8A, 0x35, 0xFF, - 0x41, 0x46, 0x0A, 0x97, 0x5D, 0xC8, 0x13, 0x68, 0xEE, 0x75, 0x15, 0xA8, 0x78, 0x5A, 0x10, 0xC1, 0x91, 0x93, 0x97, 0x30, 0xBC, 0xFD, 0x27, 0x31, 0x7C, 0xD0, - 0xC7, 0x81, 0x56, 0xD7, 0x3B, 0xFA, 0x41, 0x9D, 0x5E, 0x7F, 0x07, 0xEC, 0x4C, 0xEA, 0x8D, 0x83, 0x6E, 0xA3, 0xD1, 0x0E, 0x40, 0x67, 0xA4, 0xDE, 0xEA, 0x89, - 0x26, 0xF0, 0x87, 0xB6, 0x61, 0x9D, 0xE4, 0xDF, 0x7F, 0xED, 0xCD, 0xFD, 0xA0, 0xA8, 0xC1, 0x3B, 0xDB, 0xC5, 0x4C, 0x5C, 0xD4, 0xE4, 0x1A, 0x06, 0x2F, 0xAE, - 0x95, 0x69, 0xA2, 0xD3, 0x87, 0x1C, 0xC5, 0x48, 0x93, 0x3E, 0xFB, 0x05, 0x25, 0xBF, 0x54, 0xEC, 0xF3, 0x7A, 0x93, 0xE0, 0x4C, 0x79, 0x5D, 0xAC, 0x5D, 0xDD, - 0xCA, 0xC6, 0x11, 0x57, 0x74, 0x7C, 0xAE, 0x21, 0xA3, 0xFF, 0x54, 0xC0, 0xE2, 0x35, 0x52, 0x66, 0x0E, 0xBD, 0x4A, 0x19, 0xAA, 0xAC, 0xAF, 0x0A, 0xEB, 0xD1, - 0xE4, 0x44, 0x73, 0x7A, 0x8C, 0x9D, 0xAC, 0x3D, 0x2F, 0xE7, 0xE0, 0xE3, 0x53, 0x11, 0x51, 0xD9, 0x35, 0x1C, 0x70, 0x46, 0xA1, 0x1F, 0x06, 0xA0, 0x45, 0xF9, - 0x08, 0x6E, 0x4B, 0x19, 0x8C, 0x8F, 0x56, 0x4B, 0x00, 0xA4, 0x57, 0x89, 0x4B, 0xB0, 0xD2, 0x28, 0xB8, 0xB0, 0x5E, 0x48, 0xBF, 0xFC, 0x9A, 0xA2, 0x00, 0xAC, - 0x59, 0xCE, 0x15, 0x51, 0x06, 0xDA, 0x35, 0xA2, 0xA0, 0x87, 0x40, 0x7C, 0xD8, 0x26, 0x85, 0xBC, 0x9C, 0x61, 0x7D, 0x76, 0x48, 0x9F, 0x8A, 0x85, 0x79, 0x43, - 0xF9, 0xEC, 0x30, 0xFE, 0x56, 0x32, 0x10, 0xF1, 0x51, 0x81, 0x58, 0x84, 0xA4, 0x58, 0xDE, 0x44, 0x96, 0xB7, 0x98, 0xCE, 0x28, 0x81, 0x90, 0xBF, 0x7D, 0xC0, - 0xC4, 0x45, 0x2A, 0x8A, 0x8B, 0x70, 0x71, 0x21, 0x40, 0x3C, 0xC2, 0x2D, 0x9F, 0x5B, 0x89, 0xEC, 0xFF, 0xD7, 0xE7, 0x31, 0x67, 0x8B, 0x61, 0x21, 0x9D, 0x7C, - 0xEE, 0x42, 0x62, 0xAF, 0x18, 0x20, 0xF1, 0x81, 0x24, 0xC6, 0xD6, 0x62, 0x58, 0x8D, 0x2D, 0x31, 0xF7, 0x81, 0x00, 0x31, 0x5B, 0xEA, 0x19, 0x12, 0xC1, 0x4A, - 0xF4, 0xB2, 0x32, 0xCD, 0x70, 0x2D, 0x2D, 0xFA, 0x4C, 0x76, 0x44, 0x2C, 0x7B, 0xD3, 0x57, 0x69, 0xB9, 0xC9, 0x9A, 0x49, 0x4C, 0x46, 0x73, 0x2C, 0xA5, 0xA0, - 0x51, 0x4B, 0x09, 0x3A, 0xA2, 0xA3, 0x10, 0x5A, 0x34, 0x62, 0x45, 0x63, 0x74, 0x5A, 0x49, 0x58, 0x51, 0xEB, 0xD8, 0x71, 0x62, 0x04, 0x62, 0x8E, 0xE6, 0x38, - 0x3D, 0xA7, 0xC7, 0x06, 0x2F, 0x8C, 0xD9, 0xD4, 0x10, 0x45, 0x6E, 0x10, 0xB1, 0x94, 0x68, 0x13, 0x39, 0x08, 0x83, 0xCF, 0x23, 0xB3, 0x94, 0x14, 0xB6, 0xE6, - 0xA9, 0xBF, 0x77, 0x08, 0xCE, 0x87, 0xF0, 0x7D, 0x8C, 0xB8, 0x07, 0xD1, 0xF3, 0x35, 0xC7, 0x5B, 0x10, 0x5C, 0xBF, 0x14, 0x8F, 0x9B, 0x68, 0x43, 0x02, 0x31, - 0x95, 0xB0, 0x59, 0x2D, 0x8C, 0x41, 0xE1, 0xC4, 0x0E, 0x60, 0x10, 0x8E, 0x9F, 0xB3, 0x24, 0x0F, 0xF5, 0x28, 0x21, 0x96, 0xB2, 0x97, 0x9D, 0xED, 0x4F, 0x88, - 0x93, 0xC1, 0xC4, 0xB2, 0x7C, 0xC8, 0x79, 0xCC, 0x04, 0xA2, 0xA2, 0x79, 0xB4, 0x35, 0x44, 0x18, 0xDD, 0xFE, 0x6A, 0xA5, 0xA8, 0x66, 0xA0, 0x54, 0x90, 0x11, - 0x58, 0x2C, 0xCB, 0x98, 0xD7, 0x8C, 0x34, 0x55, 0x93, 0x95, 0x05, 0x1A, 0xC5, 0x39, 0x7B, 0x65, 0x94, 0xCF, 0xD7, 0x0A, 0x93, 0x38, 0x4B, 0xAC, 0xEC, 0x77, - 0x7E, 0x28, 0x76, 0x16, 0xB0, 0x33, 0x2C, 0x18, 0x07, 0x0F, 0xCE, 0x0F, 0x27, 0xE1, 0xD4, 0x19, 0x3C, 0xF8, 0x5F, 0x94, 0x18, 0xA6, 0xBF, 0xBA, 0xAB, 0x00, - 0x00 +//File: index_ov2640.html.gz, Size: 6578 +#define index_ov2640_html_gz_len 6578 +const unsigned char index_ov2640_html_gz[] = { + 0x1F, 0x8B, 0x08, 0x08, 0x99, 0xA8, 0x7B, 0x67, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x6F, 0x76, 0x32, 0x36, 0x34, 0x30, 0x2E, 0x68, 0x74, 0x6D, + 0x6C, 0x00, 0xED, 0x3D, 0x6B, 0x73, 0xDB, 0x46, 0x92, 0xDF, 0xFD, 0x2B, 0x60, 0x24, 0x6B, 0x92, 0x65, 0x92, 0x22, 0x29, 0x4A, 0x96, 0x15, 0x89, 0x3E, 0x5B, + 0x56, 0x6C, 0xD7, 0xDA, 0x89, 0xD7, 0x4A, 0x1C, 0x6F, 0xA5, 0xB6, 0x6C, 0x10, 0x18, 0x92, 0x88, 0x41, 0x80, 0x0B, 0x80, 0xA2, 0x98, 0x94, 0x7E, 0xC7, 0xFD, + 0xA0, 0xFB, 0x63, 0xD7, 0x3D, 0x0F, 0x60, 0x00, 0x0C, 0x5E, 0xA4, 0x4C, 0x7A, 0x7D, 0x47, 0xA7, 0x22, 0x3C, 0x7A, 0x7A, 0xFA, 0xDD, 0x3D, 0x33, 0x18, 0xE0, + 0xEC, 0xBE, 0xE5, 0x99, 0xE1, 0x7A, 0x41, 0xB4, 0x59, 0x38, 0x77, 0x46, 0xF7, 0xCE, 0xD8, 0x1F, 0x0D, 0x7E, 0x67, 0x33, 0x62, 0x58, 0xEC, 0x90, 0x9E, 0xCE, + 0x49, 0x68, 0x68, 0xE6, 0xCC, 0xF0, 0x03, 0x12, 0x9E, 0xEB, 0xCB, 0x70, 0xD2, 0x39, 0xD1, 0xD3, 0xB7, 0x5D, 0x63, 0x4E, 0xCE, 0xF5, 0x6B, 0x9B, 0xAC, 0x16, + 0x9E, 0x1F, 0xEA, 0x9A, 0xE9, 0xB9, 0x21, 0x71, 0x01, 0x7C, 0x65, 0x5B, 0xE1, 0xEC, 0xDC, 0x22, 0xD7, 0xB6, 0x49, 0x3A, 0xF4, 0xA4, 0x6D, 0xBB, 0x76, 0x68, + 0x1B, 0x4E, 0x27, 0x30, 0x0D, 0x87, 0x9C, 0xF7, 0x65, 0x5C, 0xA1, 0x1D, 0x3A, 0x64, 0x74, 0x79, 0xF5, 0xF6, 0x70, 0xA0, 0xFD, 0xFC, 0x7E, 0x30, 0x3C, 0xEE, + 0x9D, 0x1D, 0xB0, 0x6B, 0x31, 0x4C, 0x10, 0xAE, 0xE5, 0x73, 0xFC, 0x8D, 0x3D, 0x6B, 0xAD, 0xFD, 0x95, 0xB8, 0x84, 0xBF, 0x09, 0x10, 0xD1, 0x99, 0x18, 0x73, + 0xDB, 0x59, 0x9F, 0x6A, 0x4F, 0x7D, 0xE8, 0xB3, 0xFD, 0x92, 0x38, 0xD7, 0x24, 0xB4, 0x4D, 0xA3, 0x1D, 0x18, 0x6E, 0xD0, 0x09, 0x88, 0x6F, 0x4F, 0x7E, 0xC8, + 0x34, 0x1C, 0x1B, 0xE6, 0xE7, 0xA9, 0xEF, 0x2D, 0x5D, 0xEB, 0x54, 0xFB, 0xAE, 0x7F, 0x82, 0xFF, 0xB2, 0x40, 0xA6, 0xE7, 0x78, 0x3E, 0xDC, 0xBF, 0xFC, 0x11, + 0xFF, 0x65, 0xEF, 0xD3, 0xDE, 0x03, 0xFB, 0x4F, 0x72, 0xAA, 0xF5, 0x8F, 0x17, 0x37, 0x89, 0xFB, 0xB7, 0xF7, 0x12, 0xA7, 0xB3, 0x41, 0x1E, 0xF5, 0xBC, 0xFD, + 0x49, 0x71, 0xFB, 0x80, 0x98, 0xA1, 0xED, 0xB9, 0xDD, 0xB9, 0x61, 0xBB, 0x0A, 0x4C, 0x96, 0x1D, 0x2C, 0x1C, 0x03, 0x64, 0x30, 0x71, 0x48, 0x21, 0x9E, 0xEF, + 0xE6, 0xC4, 0x5D, 0xB6, 0x4B, 0xB0, 0x21, 0x92, 0x8E, 0x65, 0xFB, 0x0C, 0xEA, 0x14, 0xE5, 0xB0, 0x9C, 0xBB, 0xA5, 0x68, 0x8B, 0xE8, 0x72, 0x3D, 0x97, 0x28, + 0x04, 0x88, 0x1D, 0xAD, 0x7C, 0x63, 0x81, 0x00, 0xF8, 0x37, 0x0B, 0x32, 0xB7, 0x5D, 0x66, 0x54, 0xA7, 0xDA, 0xE1, 0xB0, 0xB7, 0xB8, 0x29, 0x51, 0xE5, 0xE1, + 0x31, 0xFE, 0xCB, 0x02, 0x2D, 0x0C, 0xCB, 0xB2, 0xDD, 0xE9, 0xA9, 0x76, 0xA2, 0x44, 0xE1, 0xF9, 0x16, 0xF1, 0x3B, 0xBE, 0x61, 0xD9, 0xCB, 0xE0, 0x54, 0x1B, + 0xAA, 0x60, 0xE6, 0x86, 0x3F, 0x05, 0x5A, 0x42, 0x0F, 0x88, 0xED, 0xF4, 0x95, 0x94, 0x70, 0x10, 0xDF, 0x9E, 0xCE, 0x42, 0x50, 0x69, 0x06, 0x26, 0x2D, 0x34, + 0xEE, 0x42, 0x65, 0xFA, 0x2C, 0x94, 0x9B, 0x5A, 0x6A, 0x86, 0x63, 0x4F, 0xDD, 0x8E, 0x1D, 0x92, 0x39, 0xB0, 0x13, 0x84, 0x3E, 0x09, 0xCD, 0x59, 0x11, 0x29, + 0x13, 0x7B, 0xBA, 0xF4, 0x89, 0x82, 0x90, 0x48, 0x6E, 0x05, 0x0C, 0xC3, 0xCD, 0xEC, 0xAD, 0xCE, 0x8A, 0x8C, 0x3F, 0xDB, 0x61, 0x87, 0xCB, 0x64, 0x4C, 0x26, + 0x9E, 0x4F, 0x94, 0x90, 0x02, 0xC2, 0xF1, 0xCC, 0xCF, 0x9D, 0x20, 0x34, 0xFC, 0xB0, 0x0A, 0x42, 0x63, 0x12, 0x12, 0xBF, 0x1C, 0x1F, 0x41, 0xAB, 0x28, 0xC7, + 0x96, 0xDF, 0x2D, 0x07, 0xB0, 0x5D, 0xC7, 0x76, 0x49, 0x75, 0xF2, 0xF2, 0xFA, 0x4D, 0xA2, 0x63, 0x50, 0x15, 0x14, 0x63, 0xCF, 0xA7, 0x45, 0x56, 0x42, 0x79, + 0xCD, 0x76, 0xC6, 0xFD, 0xA6, 0xDF, 0xEB, 0xFD, 0x2D, 0x7B, 0x73, 0x46, 0x98, 0x99, 0x1A, 0xCB, 0xD0, 0xDB, 0xDE, 0x23, 0x32, 0x6E, 0x95, 0xE2, 0xE3, 0xBF, + 0xE6, 0xC4, 0xB2, 0x0D, 0xAD, 0x29, 0xB9, 0xF3, 0x49, 0x0F, 0x6C, 0xAA, 0xA5, 0x19, 0xAE, 0xA5, 0x35, 0x3D, 0xDF, 0x06, 0x47, 0x30, 0x68, 0xB8, 0x71, 0xE0, + 0x0A, 0x24, 0x8E, 0x05, 0x69, 0x29, 0x58, 0x2E, 0xF0, 0x19, 0x59, 0x22, 0x6A, 0xB7, 0xC1, 0x5F, 0x85, 0x90, 0x83, 0xBF, 0x52, 0x07, 0x52, 0xF0, 0x48, 0xD1, + 0x17, 0xE9, 0x4B, 0xA6, 0x30, 0x4F, 0x67, 0xF8, 0x9B, 0x1B, 0x37, 0x9D, 0x42, 0xDD, 0x09, 0x20, 0xA1, 0x43, 0x48, 0xB3, 0x66, 0x13, 0x40, 0xAF, 0x67, 0x5A, + 0x47, 0xC3, 0x28, 0xD9, 0x52, 0xB7, 0xE1, 0x48, 0xD5, 0x2A, 0xC7, 0x9F, 0x6C, 0x14, 0x35, 0xD8, 0x55, 0xB3, 0x1A, 0xC7, 0x0E, 0xF6, 0x4F, 0x65, 0x43, 0x8C, + 0x93, 0xDC, 0x28, 0x82, 0xBF, 0xEA, 0x91, 0x24, 0x46, 0x56, 0x1A, 0x4D, 0x14, 0x88, 0xF3, 0x23, 0x4A, 0x06, 0x6F, 0x9E, 0x77, 0x2B, 0xB0, 0x16, 0x93, 0x50, + 0x35, 0xBA, 0x28, 0x10, 0x17, 0xD1, 0x50, 0x1A, 0x65, 0xF0, 0x77, 0x5B, 0xA1, 0xDE, 0xF8, 0x6E, 0xBC, 0x0C, 0x43, 0xCF, 0x0D, 0xB6, 0x4A, 0x51, 0x79, 0x7E, + 0xF6, 0xC7, 0x32, 0x08, 0xED, 0xC9, 0xBA, 0xC3, 0x5D, 0x1A, 0xFC, 0x6C, 0x61, 0x40, 0x09, 0x39, 0x26, 0xE1, 0x8A, 0x90, 0xE2, 0x72, 0xC3, 0x35, 0xAE, 0x21, + 0xEE, 0x4C, 0xA7, 0x8E, 0xCA, 0xF6, 0xCC, 0xA5, 0x1F, 0x60, 0xDD, 0xB6, 0xF0, 0x6C, 0x40, 0xEC, 0x67, 0x3B, 0x4E, 0xFA, 0x60, 0xC5, 0x8E, 0x3A, 0xE6, 0x58, + 0xD1, 0x97, 0xB7, 0x0C, 0x51, 0xC6, 0x4A, 0x4D, 0x78, 0xC0, 0x8E, 0x1D, 0xAE, 0x95, 0xF7, 0xB8, 0x27, 0x2A, 0xEE, 0x08, 0x17, 0x2C, 0x4C, 0x0B, 0x49, 0xBA, + 0x4E, 0xCD, 0x19, 0x31, 0x3F, 0x13, 0xEB, 0x61, 0x69, 0x19, 0x56, 0x56, 0x1E, 0x76, 0x6D, 0x77, 0xB1, 0x0C, 0x3B, 0x58, 0x4E, 0x2D, 0xBE, 0x88, 0xCE, 0xA9, + 0x41, 0x0A, 0x16, 0x07, 0x83, 0xA2, 0xA2, 0xE2, 0x68, 0x71, 0x53, 0x2C, 0x04, 0x99, 0xD8, 0x91, 0x63, 0x8C, 0x89, 0x53, 0x44, 0x32, 0x77, 0x86, 0x9C, 0xB0, + 0xCB, 0x63, 0x55, 0x7E, 0xED, 0x46, 0x29, 0x8B, 0x93, 0xD7, 0xF0, 0xD1, 0xDF, 0x2A, 0xCB, 0x91, 0x1E, 0xB7, 0x13, 0x97, 0x02, 0xE2, 0x80, 0x83, 0xE5, 0x95, + 0xDE, 0x00, 0xB3, 0x02, 0x1A, 0x0A, 0x3B, 0xF0, 0x0D, 0x77, 0x4A, 0x20, 0x16, 0xDC, 0xB4, 0xC5, 0x61, 0xF1, 0xC0, 0xA0, 0x12, 0xFB, 0x18, 0xAA, 0x8F, 0x8A, + 0x07, 0x22, 0x2C, 0x20, 0xB4, 0xB5, 0x2E, 0x3B, 0xD8, 0xA0, 0x2A, 0x91, 0xF4, 0x5B, 0x48, 0x48, 0x5F, 0x69, 0x1D, 0xAC, 0x30, 0x51, 0x7A, 0x4E, 0xD2, 0xB6, + 0x94, 0x85, 0x7E, 0x69, 0x68, 0x10, 0x43, 0xBE, 0xC9, 0xA4, 0x6C, 0xD0, 0x38, 0x99, 0x1C, 0xF6, 0x0E, 0x87, 0xA5, 0x95, 0x93, 0x92, 0xCB, 0xD4, 0xC0, 0x51, + 0x11, 0x3A, 0xA2, 0xB0, 0x52, 0x68, 0x04, 0x81, 0x71, 0xAD, 0x2C, 0xDA, 0xBD, 0xC0, 0x66, 0x23, 0x37, 0x63, 0x1C, 0xC0, 0xD8, 0x2D, 0x54, 0x0C, 0xBD, 0xB8, + 0xA1, 0x0F, 0x94, 0xF4, 0xD1, 0x92, 0x4E, 0xE9, 0x02, 0x42, 0xBC, 0x6A, 0xB2, 0x13, 0x1A, 0x50, 0x83, 0x48, 0x0A, 0x56, 0x16, 0x95, 0x21, 0xB9, 0x09, 0x3B, + 0x16, 0x31, 0x3D, 0x9F, 0x55, 0x83, 0x39, 0x23, 0xC7, 0x94, 0x22, 0xCB, 0x2D, 0xF6, 0x74, 0xE6, 0x5D, 0x13, 0x5F, 0x21, 0xAC, 0x94, 0x52, 0x87, 0x8F, 0x87, + 0x56, 0x05, 0x6C, 0x06, 0xA4, 0x47, 0xA5, 0xEC, 0x93, 0xE8, 0x06, 0x7D, 0x73, 0x50, 0xE8, 0xC7, 0x0C, 0x5D, 0x17, 0x7C, 0xC6, 0x18, 0x3B, 0xC4, 0x2A, 0xC8, + 0x66, 0x16, 0x99, 0x18, 0x4B, 0x27, 0x2C, 0xB1, 0x4A, 0xA3, 0x87, 0xFF, 0x8A, 0x7A, 0xA4, 0x61, 0xE8, 0x77, 0x9C, 0x17, 0x3A, 0xA7, 0x81, 0xE3, 0x5F, 0x8A, + 0x3E, 0x45, 0xA9, 0x61, 0x2C, 0x16, 0xC4, 0x00, 0x28, 0x93, 0xE4, 0xE9, 0xA1, 0xD2, 0x10, 0x43, 0x1D, 0xE7, 0x2B, 0x8D, 0xDB, 0x4B, 0x1D, 0x36, 0x2A, 0x1E, + 0x6B, 0xF1, 0x7C, 0x3A, 0xF1, 0xCC, 0xA5, 0xAA, 0xAA, 0xA9, 0xE6, 0x78, 0x59, 0x7C, 0xA7, 0x42, 0x64, 0x81, 0x63, 0x53, 0xF7, 0x5F, 0xBA, 0x2E, 0x6A, 0xB4, + 0x13, 0xFA, 0xC0, 0xA6, 0xA2, 0xA3, 0x6A, 0x82, 0xDB, 0x28, 0x86, 0x25, 0x04, 0x9B, 0x37, 0x77, 0x95, 0x0A, 0x53, 0x8A, 0x70, 0x1A, 0x45, 0x5A, 0x0D, 0x62, + 0x88, 0x6D, 0x09, 0x54, 0xDB, 0xC9, 0x25, 0x9C, 0x2D, 0xE7, 0xAA, 0x3A, 0x4A, 0x74, 0xD6, 0x87, 0xA4, 0xCF, 0xBA, 0xF3, 0xA7, 0x63, 0xA3, 0xD9, 0x6B, 0xF7, + 0xDA, 0x87, 0xF0, 0x3F, 0xC5, 0x78, 0xA6, 0xD8, 0xB8, 0xB8, 0x78, 0x73, 0x2C, 0x2F, 0x15, 0xA2, 0xCB, 0xA7, 0x95, 0xF2, 0x82, 0x7D, 0xA9, 0x2E, 0xAA, 0x7B, + 0x52, 0x72, 0x7E, 0xA9, 0xDF, 0x2D, 0xC9, 0xC3, 0x39, 0x26, 0x5D, 0xDF, 0x10, 0x15, 0xD6, 0x52, 0x57, 0xC5, 0x73, 0xEF, 0xCF, 0x0E, 0x2B, 0x42, 0xFE, 0xCF, + 0x5B, 0xBB, 0x24, 0x8A, 0x6F, 0xDA, 0xD2, 0x6B, 0xCB, 0x25, 0xD8, 0xB7, 0x6D, 0xF4, 0xF2, 0xB5, 0xDE, 0xE1, 0x55, 0x1F, 0x50, 0xE8, 0xC2, 0x18, 0xD4, 0x87, + 0xC1, 0x68, 0x6E, 0x65, 0x28, 0xC1, 0x6C, 0x20, 0x83, 0x89, 0xED, 0x38, 0x1D, 0xC7, 0x5B, 0x95, 0x57, 0x22, 0xC5, 0x96, 0x9C, 0xB1, 0xD3, 0x72, 0x93, 0xDF, + 0x94, 0xDA, 0x25, 0x44, 0xAE, 0xFF, 0x08, 0x6A, 0xBF, 0x6D, 0x87, 0x2B, 0x74, 0x8D, 0xCD, 0x12, 0xC5, 0x06, 0xF6, 0xB8, 0x5D, 0x47, 0x95, 0x4C, 0x89, 0x55, + 0x82, 0xC5, 0xC3, 0x9E, 0x95, 0x1D, 0x9A, 0xB3, 0x0D, 0x86, 0x9E, 0xF1, 0xC0, 0xC8, 0x27, 0x8E, 0x81, 0x15, 0xFC, 0x46, 0x33, 0x14, 0xA5, 0xC3, 0x37, 0xB9, + 0x79, 0x15, 0x4E, 0xA8, 0xE8, 0xBE, 0x9E, 0xD9, 0xA5, 0x2E, 0xAB, 0x1D, 0xF2, 0x63, 0xB5, 0xDA, 0xAC, 0x4B, 0xCA, 0xFD, 0xA4, 0x67, 0xA8, 0x81, 0x6A, 0x44, + 0x74, 0x11, 0xB4, 0xA7, 0x3E, 0x59, 0x57, 0x60, 0xA6, 0xCD, 0xFF, 0x9E, 0xB2, 0xF9, 0xE3, 0xCD, 0xA7, 0x4A, 0x68, 0x02, 0xE0, 0x56, 0xD4, 0x1D, 0x06, 0x15, + 0xBA, 0xCE, 0xEF, 0xB2, 0x8A, 0x3D, 0x46, 0xB3, 0xA3, 0xBA, 0x5E, 0x21, 0xDC, 0x14, 0xA4, 0x50, 0xB5, 0xA9, 0x8A, 0xEC, 0xAB, 0x1E, 0xCF, 0x93, 0x49, 0x98, + 0xB3, 0xF8, 0x43, 0xEB, 0xD4, 0xC3, 0xE2, 0xE8, 0xD6, 0x91, 0x66, 0x53, 0x4A, 0x23, 0x47, 0x34, 0x89, 0x99, 0x6F, 0x7D, 0x4A, 0xCC, 0x18, 0x3D, 0x6B, 0x23, + 0xCF, 0x57, 0x89, 0x28, 0x9F, 0xA9, 0x9A, 0x01, 0x66, 0xCE, 0x53, 0x3E, 0xA8, 0x87, 0x7C, 0x68, 0x0E, 0x8E, 0x95, 0x6B, 0x2B, 0x05, 0xC0, 0x45, 0xA4, 0xE5, + 0xCE, 0x02, 0x66, 0x53, 0x56, 0xEE, 0x00, 0x59, 0x8E, 0x45, 0x4A, 0x45, 0x15, 0x7B, 0x65, 0x51, 0x84, 0xC9, 0xCE, 0x64, 0x15, 0x1A, 0xBB, 0x3D, 0x37, 0xA0, + 0xEC, 0x45, 0x73, 0x35, 0x00, 0xA3, 0x4A, 0x7F, 0x55, 0xCC, 0x5D, 0x9A, 0x63, 0xED, 0x1F, 0xF7, 0x4A, 0xBA, 0x34, 0x1D, 0x2F, 0xD8, 0x72, 0x02, 0x2C, 0x7F, + 0xFE, 0x4B, 0x79, 0xA7, 0x52, 0xEA, 0x2E, 0xF4, 0xA9, 0x62, 0x77, 0x4C, 0xC9, 0xBC, 0xDF, 0x53, 0x46, 0xDA, 0xC2, 0x59, 0x4A, 0x3A, 0x83, 0x46, 0xD7, 0x2F, + 0x4F, 0x35, 0x93, 0xA8, 0xC3, 0x68, 0x72, 0xA2, 0xAE, 0xCA, 0x54, 0x69, 0xA1, 0x1E, 0x66, 0xB6, 0x65, 0x91, 0xC2, 0xB9, 0x60, 0x1C, 0xF3, 0x56, 0x2C, 0x1E, + 0x90, 0x7E, 0xD5, 0xA4, 0xD4, 0x17, 0x71, 0x8A, 0xC2, 0xC7, 0x1A, 0xFA, 0x5F, 0xDA, 0x63, 0x78, 0xA2, 0xC9, 0x9B, 0x49, 0x4F, 0x96, 0x22, 0x85, 0xA4, 0x2A, + 0x9D, 0x3B, 0x9A, 0x6B, 0x45, 0x91, 0x81, 0x1C, 0x10, 0x2A, 0x1B, 0xCD, 0x53, 0x54, 0xD1, 0x85, 0x94, 0x2E, 0x5F, 0x5B, 0xE2, 0xCB, 0x80, 0x9D, 0xBC, 0xD5, + 0x95, 0x3B, 0x5C, 0x6A, 0xA3, 0x16, 0x90, 0xEE, 0x37, 0x57, 0x34, 0x7B, 0xAA, 0x8C, 0x0A, 0x88, 0x8C, 0x52, 0x8C, 0x78, 0xB8, 0x2A, 0x09, 0xB5, 0xA9, 0x73, + 0x9C, 0x1D, 0x48, 0x4F, 0xC3, 0x9D, 0x1D, 0xC4, 0x0F, 0xEE, 0x9D, 0xE1, 0x23, 0x71, 0xF2, 0x43, 0x73, 0xBC, 0x1F, 0xD3, 0x31, 0x82, 0xE0, 0x5C, 0xC7, 0x47, + 0xBB, 0xF4, 0xE4, 0x33, 0x74, 0x67, 0x96, 0x7D, 0xAD, 0xD9, 0xD6, 0xB9, 0xEE, 0x78, 0x53, 0x2F, 0x75, 0x8F, 0xDE, 0x67, 0x5A, 0x86, 0x3C, 0x76, 0xAE, 0x27, + 0xD6, 0x17, 0x75, 0xDA, 0x2A, 0xBE, 0xA4, 0x8F, 0x1E, 0x7C, 0xF7, 0xF8, 0xD1, 0xA3, 0xE3, 0x1F, 0x1E, 0xB8, 0xE3, 0x60, 0xC1, 0xFF, 0xFF, 0x0B, 0x5B, 0x8E, + 0xFD, 0xF9, 0xFD, 0xE0, 0x78, 0x08, 0xC3, 0x3D, 0x12, 0x86, 0x60, 0x7A, 0xC1, 0xD9, 0x01, 0x45, 0x9A, 0x22, 0xE4, 0x00, 0x28, 0xC9, 0xA1, 0x8D, 0x97, 0x3B, + 0x2A, 0xF2, 0x04, 0x48, 0x00, 0x19, 0x7C, 0x6C, 0xF8, 0x0A, 0x10, 0x0A, 0xC6, 0x8A, 0x69, 0x1A, 0x4A, 0x74, 0xAA, 0x93, 0xB1, 0x77, 0x93, 0xE6, 0x80, 0x32, + 0xC5, 0x15, 0xC6, 0xA1, 0x88, 0x95, 0x87, 0x10, 0x9A, 0xD1, 0xE6, 0xB8, 0xB8, 0x0A, 0x30, 0x4A, 0xA0, 0x84, 0x0A, 0x10, 0xF8, 0xC6, 0x74, 0x3E, 0x0B, 0xDD, + 0xEB, 0x42, 0x29, 0xAE, 0x17, 0xB2, 0x50, 0x99, 0xD3, 0x55, 0x82, 0x55, 0xDE, 0x46, 0x5A, 0x36, 0x64, 0x5C, 0x80, 0x68, 0x3B, 0x14, 0x3B, 0xBB, 0x56, 0x8C, + 0x89, 0x62, 0x93, 0xF4, 0x2A, 0x1A, 0xEB, 0xA3, 0x0F, 0x17, 0xAF, 0xFF, 0xAE, 0xBD, 0x79, 0xF9, 0xA7, 0x52, 0x43, 0x65, 0x44, 0x61, 0x8C, 0xAE, 0xD0, 0x33, + 0x6D, 0xC6, 0xF4, 0x21, 0x64, 0xA2, 0x73, 0xCD, 0x50, 0x0C, 0x98, 0xED, 0x1D, 0xE2, 0x4E, 0xC3, 0xD9, 0xB9, 0xDE, 0xD7, 0xF1, 0x91, 0x16, 0x71, 0x36, 0xD0, + 0x35, 0x8C, 0xDF, 0xF4, 0xE0, 0xDA, 0x70, 0x96, 0x78, 0xD4, 0xAB, 0xC2, 0x6B, 0xD6, 0xB4, 0x94, 0x60, 0x3C, 0xB0, 0x44, 0x32, 0x96, 0x02, 0x71, 0x52, 0xCA, + 0xFA, 0xE8, 0x8A, 0x84, 0x67, 0x07, 0xEC, 0x56, 0x89, 0xD6, 0x8A, 0xFB, 0x06, 0x4F, 0x66, 0xE6, 0x50, 0x64, 0x42, 0x45, 0x8A, 0x9F, 0xF8, 0xC6, 0x9C, 0xA0, + 0x54, 0x2A, 0x69, 0x5E, 0xD6, 0x7A, 0xD4, 0x52, 0x1F, 0xBD, 0x23, 0xB4, 0x20, 0x02, 0x32, 0x2A, 0x29, 0xFE, 0x8C, 0xD7, 0xA8, 0x89, 0xFE, 0x23, 0x7B, 0xE6, + 0x6B, 0x52, 0x1D, 0x83, 0x99, 0x79, 0x05, 0xB9, 0xDF, 0xEF, 0x74, 0xB4, 0xC1, 0x9B, 0xB7, 0x5A, 0xA7, 0x53, 0x01, 0xD8, 0x5B, 0x50, 0x77, 0xE2, 0xFA, 0xEF, + 0x1F, 0xE9, 0xA3, 0x5F, 0x3F, 0xBC, 0x78, 0xDA, 0x84, 0xBA, 0xB0, 0x77, 0xD3, 0x1F, 0xF4, 0x7A, 0xAD, 0xB3, 0x03, 0x06, 0x52, 0x1F, 0xD7, 0x10, 0xF4, 0x4A, + 0x71, 0x0D, 0x4E, 0x00, 0x57, 0x6F, 0x30, 0xDC, 0x02, 0xD7, 0xA1, 0x3E, 0x7A, 0xF9, 0x9C, 0x61, 0x7A, 0x34, 0xD8, 0x86, 0xA8, 0x01, 0x78, 0x25, 0xD2, 0x04, + 0xE4, 0xDC, 0x3C, 0x3A, 0x3E, 0xD9, 0x02, 0x53, 0x1F, 0xD8, 0x7B, 0x0F, 0xA8, 0x4E, 0x40, 0x52, 0xC7, 0x5B, 0x09, 0x0A, 0x9C, 0x0E, 0x11, 0x41, 0x4C, 0xBF, + 0x19, 0x9E, 0x6C, 0x81, 0xE8, 0x31, 0x08, 0x09, 0x11, 0x01, 0x92, 0x9B, 0xC3, 0x6D, 0xA4, 0x74, 0xA2, 0x8F, 0x2E, 0x5E, 0xFD, 0xD8, 0x1C, 0x02, 0x67, 0x83, + 0xC7, 0xC7, 0x75, 0xF0, 0x80, 0xED, 0x25, 0x51, 0x3D, 0xD2, 0x47, 0x40, 0x0A, 0x92, 0x23, 0xB0, 0x80, 0x59, 0x32, 0x1B, 0xFD, 0xD5, 0x0D, 0x96, 0x0B, 0x7C, + 0xD0, 0x9D, 0x58, 0x1A, 0x34, 0xF0, 0xAE, 0x69, 0x56, 0xDB, 0xC0, 0x6A, 0x8F, 0xF5, 0xD1, 0x3F, 0x90, 0x6F, 0xEC, 0x68, 0x30, 0xDC, 0x82, 0x6F, 0xB0, 0x7E, + 0x68, 0x8F, 0x38, 0x36, 0x46, 0x01, 0x46, 0xFF, 0x92, 0x12, 0x83, 0x88, 0xFA, 0x8F, 0x6A, 0x09, 0x2F, 0x89, 0x09, 0x4C, 0xFE, 0x1F, 0xA8, 0x05, 0x40, 0x72, + 0xD3, 0x1F, 0x6E, 0xE1, 0x3C, 0x60, 0xF2, 0xE0, 0x38, 0xE0, 0xCD, 0x27, 0x9B, 0x9B, 0x28, 0xD0, 0x42, 0xB9, 0x82, 0xB8, 0x80, 0x61, 0x61, 0x73, 0x62, 0xC0, + 0xD6, 0x1F, 0x1F, 0xDF, 0x3C, 0x3E, 0xAE, 0x86, 0x00, 0xE3, 0x39, 0xC6, 0xC6, 0xA2, 0x88, 0x5F, 0x9C, 0x10, 0x8A, 0x82, 0xFD, 0xBF, 0x97, 0x30, 0x84, 0x0B, + 0xD7, 0xB5, 0x43, 0x3D, 0x6F, 0x07, 0x32, 0x61, 0x07, 0xD5, 0xA2, 0xBC, 0x44, 0x49, 0xF4, 0x34, 0x91, 0x3E, 0x1A, 0x56, 0xC8, 0xA6, 0x89, 0x72, 0x8B, 0xB6, + 0x4D, 0xD0, 0x4F, 0x53, 0x3C, 0x5A, 0x1E, 0x26, 0x77, 0xF0, 0x86, 0x43, 0x5D, 0x8A, 0x2C, 0x1B, 0xA5, 0x11, 0x05, 0xAD, 0xC6, 0x8D, 0x3E, 0x3A, 0x3E, 0x2C, + 0x4D, 0xBF, 0x9B, 0x2B, 0x63, 0x4C, 0x27, 0x0B, 0x5C, 0x12, 0x04, 0xB5, 0xF5, 0x11, 0x37, 0xD5, 0x47, 0xCF, 0xA2, 0xE3, 0x6D, 0xB4, 0xD2, 0x19, 0x6C, 0xA1, + 0x16, 0x89, 0x1C, 0xA6, 0x99, 0xCE, 0x80, 0xAB, 0x26, 0x2E, 0xB4, 0xEE, 0x56, 0x31, 0x65, 0xD4, 0x6E, 0xA3, 0x17, 0x1C, 0x2C, 0xF8, 0x46, 0x10, 0xD6, 0xD6, + 0x8A, 0x68, 0x08, 0xB9, 0x84, 0x1F, 0xED, 0x4D, 0x23, 0x11, 0x29, 0xDF, 0x80, 0x3E, 0x02, 0x23, 0x5C, 0xB2, 0xE7, 0xB6, 0x6A, 0x6B, 0x24, 0x6E, 0x0A, 0xA5, + 0x4B, 0x74, 0xBC, 0x37, 0xAD, 0x48, 0xE4, 0x7C, 0x0B, 0x7A, 0x59, 0x10, 0xD3, 0x36, 0x9C, 0x8F, 0x64, 0x32, 0x81, 0x84, 0x55, 0x5F, 0x37, 0x89, 0xE6, 0xA0, + 0x1F, 0x76, 0xAE, 0x5D, 0xD2, 0xF3, 0xDA, 0xE3, 0x88, 0x14, 0xBA, 0xCD, 0x07, 0x13, 0xE9, 0xEC, 0xCD, 0x27, 0xD4, 0x09, 0x1D, 0xBE, 0xB1, 0x23, 0x7D, 0xF4, + 0x93, 0x17, 0xD1, 0xB9, 0x79, 0x81, 0xF1, 0x13, 0x99, 0xD2, 0xF9, 0xEA, 0x6D, 0xEA, 0x9C, 0x17, 0xBE, 0xB1, 0xA6, 0x1B, 0x22, 0xB7, 0xA9, 0xBA, 0xDE, 0x41, + 0x3D, 0xFA, 0x8B, 0xED, 0x6E, 0xCE, 0xCC, 0x10, 0x09, 0x21, 0xC4, 0xDD, 0x0E, 0x0B, 0x14, 0xA3, 0xCF, 0xE0, 0x60, 0x3B, 0x24, 0xC7, 0x38, 0xB6, 0x5E, 0xD8, + 0xC6, 0xD7, 0x50, 0x6E, 0x19, 0xAB, 0x71, 0x6D, 0xB7, 0x80, 0x36, 0xFA, 0xE8, 0xE9, 0x6F, 0xCF, 0x6A, 0x07, 0x29, 0xB6, 0xEA, 0x5B, 0xC5, 0xC2, 0xE3, 0xB9, + 0x13, 0xEC, 0x2C, 0x33, 0xA9, 0xA5, 0xF6, 0x9C, 0xAA, 0x13, 0x5B, 0x0A, 0xBE, 0x04, 0x81, 0x74, 0x91, 0x4C, 0x97, 0xD8, 0xAC, 0xC6, 0xE3, 0x97, 0x8B, 0x60, + 0x40, 0xC4, 0xC7, 0xA9, 0x61, 0xD7, 0xCF, 0x2B, 0xA2, 0x21, 0xD5, 0x94, 0xF6, 0x02, 0x8E, 0x76, 0xA5, 0x2E, 0xD6, 0xED, 0xDE, 0x74, 0xC6, 0xB9, 0xDE, 0xB7, + 0xE2, 0x80, 0x90, 0xB9, 0x67, 0xD5, 0x9F, 0xB2, 0xE2, 0xED, 0xF4, 0x11, 0x68, 0xED, 0x0D, 0x1C, 0xD4, 0xCE, 0x32, 0x02, 0xC1, 0x17, 0x4E, 0x2F, 0x4F, 0x97, + 0xA1, 0xB7, 0x4D, 0x66, 0xB9, 0x5A, 0xBA, 0xEE, 0x7A, 0x9B, 0xB4, 0x72, 0xE1, 0x78, 0x4B, 0x6B, 0x73, 0x0C, 0x90, 0x53, 0x7E, 0x9E, 0x4C, 0x6C, 0x73, 0xF3, + 0xAC, 0x84, 0xB3, 0x0A, 0xDE, 0xBC, 0x62, 0xFB, 0x2F, 0x1C, 0xC5, 0x89, 0x59, 0x3F, 0x40, 0x10, 0x13, 0xB4, 0x78, 0x79, 0xA1, 0x5D, 0x5D, 0xFE, 0x74, 0xF5, + 0xF3, 0xBB, 0xDD, 0x44, 0x07, 0xE8, 0x73, 0x4F, 0x81, 0x01, 0xB9, 0xDD, 0x77, 0x4C, 0x00, 0x22, 0x06, 0x9B, 0xE8, 0x69, 0xC0, 0x14, 0xF5, 0xFC, 0xEA, 0xED, + 0xAE, 0xB4, 0x34, 0xD8, 0x9F, 0x9A, 0x06, 0x5F, 0x83, 0x9E, 0x3E, 0x3A, 0xE4, 0x9A, 0x38, 0x1B, 0xE8, 0x8A, 0x35, 0x44, 0x7D, 0x69, 0xAF, 0xF1, 0x68, 0x6F, + 0x03, 0xB9, 0x88, 0x94, 0x6F, 0x60, 0x18, 0x07, 0x56, 0xF1, 0x91, 0x12, 0xBD, 0x89, 0xF3, 0xB0, 0x96, 0xFA, 0xE8, 0xF2, 0x66, 0xE1, 0x05, 0x4B, 0xBF, 0x62, + 0x42, 0x55, 0x6B, 0xA4, 0xB7, 0x95, 0x42, 0x04, 0x29, 0x4C, 0x23, 0x3D, 0xAE, 0x10, 0x5C, 0xD0, 0x91, 0xD6, 0xFA, 0x86, 0x77, 0xAA, 0x15, 0x44, 0xFE, 0x25, + 0x15, 0x33, 0xDD, 0x20, 0xEF, 0x4C, 0x31, 0xEF, 0xBC, 0xB8, 0xD8, 0x4D, 0x28, 0x9B, 0xEE, 0x2D, 0xE1, 0x4C, 0xF7, 0x9A, 0x70, 0x34, 0xBE, 0xDE, 0x2E, 0xA4, + 0xB0, 0xE1, 0x20, 0x82, 0x37, 0x84, 0xB1, 0xF3, 0x26, 0x03, 0x08, 0xC9, 0x73, 0xFA, 0x37, 0xDB, 0xB8, 0x8E, 0x20, 0x23, 0xE9, 0x39, 0x87, 0xB1, 0xDF, 0x1C, + 0xDD, 0xA9, 0xD7, 0x1C, 0x96, 0x52, 0xBB, 0x8D, 0xD3, 0x20, 0x27, 0x26, 0xB1, 0x1D, 0xDC, 0x76, 0x5D, 0x57, 0x21, 0x52, 0x5B, 0xA6, 0x13, 0xED, 0x82, 0x9D, + 0x6D, 0xA3, 0x9B, 0xC1, 0x36, 0xBA, 0x91, 0x29, 0x4A, 0xAA, 0xE7, 0xF8, 0x0B, 0x65, 0x1A, 0x5C, 0x37, 0xFB, 0x92, 0x6B, 0x1E, 0x8B, 0xFA, 0x31, 0x0D, 0xDA, + 0xE8, 0xA3, 0x67, 0x6F, 0x77, 0x13, 0xD3, 0xB0, 0xB3, 0x8A, 0x31, 0x6D, 0xAB, 0x08, 0x46, 0x99, 0xDA, 0x77, 0x29, 0xB6, 0xDA, 0x40, 0x1B, 0x2B, 0x24, 0xFC, + 0xB7, 0x1D, 0x69, 0x63, 0x55, 0x5D, 0x1B, 0x77, 0x9C, 0x61, 0x56, 0x5F, 0x83, 0x7E, 0x7C, 0x63, 0xF5, 0x71, 0x3A, 0x37, 0x6A, 0xEB, 0x88, 0xB7, 0xD3, 0x47, + 0xEF, 0x8C, 0x95, 0xF6, 0xE2, 0xCD, 0xD3, 0x9D, 0xE8, 0x4A, 0x74, 0xBA, 0x1F, 0x7D, 0x45, 0x2C, 0xEF, 0x5B, 0x67, 0x0E, 0x71, 0xEB, 0x3B, 0x15, 0x36, 0xD2, + 0x47, 0xAF, 0x89, 0x1B, 0x68, 0x17, 0x9E, 0xCF, 0x5F, 0x91, 0xB7, 0x13, 0xAD, 0xD1, 0x9E, 0xF7, 0xA3, 0x32, 0xC6, 0xF4, 0xBE, 0xF5, 0x35, 0x9B, 0xDB, 0xBE, + 0xEF, 0xF9, 0xB5, 0x55, 0xC6, 0xDB, 0xE9, 0xA3, 0x97, 0x9D, 0x37, 0xF4, 0x68, 0x27, 0xEA, 0x12, 0xBD, 0xEE, 0x47, 0x63, 0x11, 0xCF, 0xFB, 0x56, 0xDA, 0xF5, + 0xC4, 0xB1, 0x17, 0xB5, 0x55, 0x46, 0x5B, 0xE9, 0xA3, 0xF7, 0x9D, 0x1F, 0xE1, 0xEF, 0x4E, 0xD4, 0xC5, 0x7A, 0xDC, 0x8F, 0xB2, 0x38, 0xB7, 0xFB, 0x56, 0x95, + 0x65, 0xAE, 0x6A, 0x2B, 0x0A, 0xDA, 0xE8, 0xA3, 0xE7, 0x17, 0xBF, 0x69, 0xCD, 0xE7, 0xDE, 0xCA, 0xC5, 0x87, 0x43, 0xB5, 0xCB, 0x9F, 0x5A, 0x3B, 0xD1, 0x18, + 0x76, 0xBD, 0x1F, 0x7D, 0x51, 0xA6, 0xF7, 0xAD, 0x2D, 0xBA, 0x07, 0x68, 0x6C, 0xD4, 0x0F, 0x87, 0xA2, 0x21, 0x3E, 0xFB, 0x02, 0x47, 0xDA, 0x33, 0x63, 0x37, + 0x01, 0x31, 0xEA, 0x77, 0x17, 0x45, 0x7B, 0xCC, 0xE4, 0xBE, 0xF5, 0xE4, 0x10, 0xAB, 0x82, 0x8A, 0x92, 0x25, 0x86, 0xF5, 0x11, 0xB7, 0xD3, 0xE0, 0x36, 0xD3, + 0x35, 0xD4, 0x1A, 0x97, 0xCF, 0xB5, 0x57, 0xE2, 0xB4, 0x02, 0x37, 0x1B, 0xCF, 0xD9, 0xE5, 0x0D, 0x6D, 0x93, 0xF4, 0x24, 0x07, 0xB7, 0x83, 0xA3, 0xA3, 0xED, + 0x86, 0xB7, 0x79, 0xD3, 0xA8, 0x47, 0x47, 0x5B, 0xEA, 0x44, 0xDE, 0x8C, 0xC1, 0xDF, 0x62, 0x58, 0xE6, 0x24, 0x7C, 0x53, 0x00, 0x1D, 0xCE, 0x93, 0xB0, 0x13, + 0x84, 0xB6, 0xE3, 0xE8, 0xA3, 0x17, 0x24, 0xD4, 0xAE, 0xF0, 0xB0, 0xE2, 0x2E, 0x00, 0x09, 0x8B, 0xD8, 0x02, 0x14, 0xFA, 0xC4, 0x98, 0xEB, 0xA3, 0x2B, 0x7C, + 0xBF, 0x23, 0xE0, 0xC2, 0xB3, 0x72, 0x64, 0x95, 0xF7, 0x0B, 0xD0, 0x0D, 0x42, 0xB8, 0xE3, 0x27, 0xF9, 0x3A, 0x56, 0x30, 0x7D, 0xB6, 0xFD, 0x6F, 0x74, 0x16, + 0x2C, 0x0C, 0x57, 0x80, 0xD1, 0xBD, 0x71, 0x2B, 0xBE, 0xD9, 0x69, 0xEC, 0x39, 0x16, 0x00, 0x3E, 0xB5, 0xAE, 0xF1, 0xD5, 0x34, 0x96, 0x76, 0x15, 0x6D, 0xDB, + 0xC1, 0x26, 0xE0, 0x3B, 0x02, 0x43, 0x89, 0xB0, 0x67, 0xBE, 0x40, 0xCF, 0x36, 0x58, 0xE1, 0xCB, 0x3C, 0x0A, 0xA4, 0x9D, 0xB3, 0xD3, 0xC8, 0x27, 0xD3, 0xC8, + 0x84, 0x54, 0x1B, 0xD0, 0x94, 0xFB, 0x8E, 0xDE, 0x91, 0xA9, 0x1D, 0x00, 0x8D, 0x1A, 0xE8, 0xE9, 0x80, 0xEE, 0xD5, 0x60, 0x1E, 0x52, 0x6D, 0x1F, 0x90, 0xDC, + 0x25, 0xDF, 0xC5, 0xA8, 0xDC, 0xDD, 0x55, 0x2B, 0x85, 0xA4, 0xF7, 0x62, 0x25, 0x31, 0x96, 0x59, 0xE1, 0xFD, 0x4E, 0x67, 0x36, 0xC4, 0x5D, 0x27, 0x9A, 0x60, + 0xED, 0xEC, 0x60, 0x36, 0x2C, 0x7B, 0xF4, 0xBC, 0x74, 0xCB, 0x10, 0x70, 0xBA, 0xF1, 0x8E, 0x21, 0x94, 0xD2, 0x08, 0xA8, 0x69, 0x6B, 0x6F, 0x8C, 0xE0, 0x73, + 0x5B, 0x7B, 0x8F, 0x0E, 0xBF, 0xC3, 0x8D, 0x43, 0x48, 0xBB, 0x61, 0x59, 0x7E, 0xEE, 0xE6, 0xA1, 0x61, 0x62, 0xF3, 0xD0, 0xB1, 0xD8, 0x3C, 0x24, 0x4D, 0xBD, + 0xDD, 0xF4, 0xFB, 0xFD, 0x2A, 0x9C, 0x57, 0xDC, 0x3F, 0x74, 0x27, 0x2C, 0xCD, 0x41, 0x98, 0x15, 0x59, 0x1A, 0x0A, 0x96, 0x86, 0x12, 0x4B, 0x27, 0x77, 0xB9, + 0x23, 0xEA, 0x4E, 0x38, 0xE2, 0x0B, 0x3B, 0x5F, 0x09, 0x4B, 0x95, 0x36, 0x79, 0x51, 0xDB, 0xBE, 0xAB, 0x3D, 0x5E, 0x14, 0x24, 0x1D, 0x0C, 0x8F, 0x0A, 0x63, + 0x21, 0x6D, 0xC2, 0x7C, 0xFE, 0xC5, 0x5D, 0xFA, 0xFC, 0x74, 0x0B, 0x9F, 0x9F, 0x66, 0x7C, 0x7E, 0x87, 0xCE, 0x2E, 0x08, 0xFF, 0xC6, 0x1C, 0x5E, 0xB0, 0x55, + 0xC3, 0xE9, 0x95, 0x6C, 0xED, 0xD6, 0x43, 0x22, 0x4B, 0x78, 0x71, 0x97, 0x1E, 0x92, 0x63, 0xB7, 0x1B, 0x19, 0x29, 0x8F, 0x39, 0xA3, 0xDD, 0xE4, 0x24, 0x5A, + 0x49, 0xC9, 0xEA, 0xE4, 0xBD, 0xE3, 0xCE, 0xA3, 0xC3, 0x21, 0x2F, 0x9B, 0xEE, 0x42, 0x3D, 0xD5, 0x37, 0x93, 0xE6, 0x82, 0xDC, 0x4D, 0x51, 0x86, 0x1B, 0xDF, + 0x16, 0x50, 0x07, 0xD7, 0x2E, 0xCC, 0x2E, 0x5E, 0xFF, 0xBD, 0x5E, 0x2D, 0x96, 0xEE, 0x69, 0x77, 0xF5, 0xD8, 0x66, 0xD6, 0x2A, 0x0B, 0x8C, 0xD3, 0x0E, 0x11, + 0x07, 0xB7, 0x55, 0x0F, 0x3E, 0x44, 0x9C, 0x2B, 0x06, 0xCD, 0x52, 0x50, 0x88, 0x9A, 0xE5, 0x8D, 0x89, 0x69, 0x20, 0xE4, 0x93, 0x18, 0x70, 0x2C, 0xE2, 0x9A, + 0xE6, 0x4D, 0x26, 0xF4, 0x5B, 0x3D, 0x8F, 0x30, 0x60, 0x04, 0x9F, 0xF1, 0x7A, 0xAF, 0x1F, 0x91, 0xA4, 0x1A, 0x11, 0xC7, 0x14, 0x46, 0xB4, 0x51, 0x13, 0xE3, + 0x86, 0x76, 0x67, 0x22, 0x38, 0x64, 0x22, 0x78, 0xFE, 0xEA, 0xBD, 0x4A, 0x06, 0xCC, 0xD7, 0x7A, 0x59, 0x11, 0x1C, 0x6E, 0xBE, 0x31, 0xBC, 0x5F, 0x59, 0x5A, + 0xBD, 0x58, 0x5A, 0x87, 0x93, 0x78, 0xCF, 0xD8, 0x36, 0x21, 0x4B, 0x21, 0x81, 0x23, 0xF6, 0x54, 0xA8, 0xF6, 0x56, 0xF6, 0x80, 0x4A, 0x76, 0x70, 0x54, 0xC7, + 0x0E, 0xAC, 0xC3, 0x2D, 0xCC, 0xE0, 0x28, 0xC7, 0x0C, 0xEE, 0x4A, 0x06, 0x43, 0x7D, 0xF4, 0x76, 0x13, 0x33, 0x18, 0x56, 0x34, 0x83, 0x43, 0x61, 0x06, 0xF1, + 0x86, 0xC2, 0x61, 0x55, 0x61, 0x49, 0x56, 0xF0, 0x68, 0x82, 0xEB, 0xE8, 0x8F, 0xAA, 0x79, 0xC2, 0xEE, 0x62, 0xEE, 0xCA, 0x76, 0xEB, 0xC7, 0xDB, 0xDF, 0x6C, + 0xD7, 0xF2, 0x56, 0xF5, 0x42, 0xAE, 0xDC, 0xD1, 0xD7, 0x1E, 0x6E, 0xEB, 0x8D, 0x5A, 0x71, 0xAA, 0xA5, 0x73, 0x83, 0x95, 0xBD, 0x1B, 0x78, 0xBE, 0x96, 0x7D, + 0xED, 0x41, 0x62, 0x47, 0x92, 0x80, 0xAE, 0x56, 0x04, 0x64, 0x9F, 0xC1, 0x7E, 0xF5, 0xA3, 0xB6, 0xC1, 0x8E, 0x74, 0x05, 0x32, 0xBE, 0x71, 0x5F, 0xDB, 0x60, + 0xE7, 0xBE, 0x02, 0x5B, 0xCE, 0x33, 0xEB, 0xF8, 0x16, 0x05, 0x6D, 0xB3, 0xD7, 0x28, 0x94, 0x3E, 0xBE, 0xCD, 0xA0, 0x36, 0x4F, 0x29, 0xD1, 0xB8, 0x8C, 0x79, + 0x2B, 0xD4, 0x58, 0xD5, 0xF6, 0xDC, 0x52, 0x9C, 0x92, 0x01, 0xF0, 0xE6, 0x37, 0xF4, 0xF1, 0xF6, 0x80, 0x54, 0xDB, 0xA5, 0x96, 0x26, 0xAC, 0x46, 0x65, 0xF8, + 0xE1, 0x54, 0x0A, 0x66, 0x51, 0xE7, 0x35, 0x83, 0x59, 0x5C, 0xE7, 0x83, 0x31, 0xED, 0x7D, 0xF0, 0xF2, 0x4F, 0x05, 0x4B, 0xEB, 0xCD, 0x59, 0x3A, 0xBC, 0x2B, + 0x96, 0xB6, 0x48, 0x55, 0x91, 0x75, 0x85, 0x5E, 0x68, 0x38, 0x1B, 0x1B, 0x17, 0x6B, 0x0D, 0xB6, 0xC5, 0x62, 0xAE, 0x76, 0x05, 0xAC, 0xEE, 0xD4, 0xC0, 0x04, + 0x01, 0xD5, 0x94, 0x31, 0xCC, 0x2A, 0xE3, 0xE4, 0x6B, 0xB3, 0x2F, 0xC6, 0x51, 0x55, 0xF3, 0x52, 0x70, 0x74, 0xFC, 0x35, 0x99, 0x97, 0xB7, 0x0C, 0xF1, 0xEA, + 0xC6, 0xC1, 0x8B, 0x35, 0xC7, 0xE0, 0x45, 0x8F, 0x76, 0x6F, 0x60, 0x11, 0x05, 0x1B, 0xEB, 0xE3, 0xF0, 0x4E, 0xDF, 0xD7, 0x74, 0x17, 0x11, 0x8C, 0xB1, 0xB4, + 0x85, 0x89, 0x0D, 0x86, 0x3B, 0x34, 0x31, 0x69, 0x19, 0x89, 0xE7, 0x41, 0x5E, 0xC0, 0xE8, 0x7C, 0x6D, 0x20, 0x2E, 0x68, 0xEA, 0xAC, 0x24, 0xA9, 0xB3, 0xF2, + 0xD9, 0x01, 0x14, 0x85, 0x59, 0x04, 0x39, 0x74, 0x9E, 0xB1, 0xCF, 0xBE, 0xA9, 0x3B, 0x8C, 0x5F, 0xD3, 0x46, 0xD7, 0xB9, 0xE2, 0x17, 0x82, 0x46, 0x85, 0x66, + 0xFA, 0x45, 0xA1, 0xA5, 0xAF, 0x44, 0x3B, 0x33, 0xF8, 0x9E, 0xFC, 0x6B, 0xC2, 0x17, 0xE5, 0xB4, 0x99, 0x4F, 0x26, 0xE7, 0xFA, 0x77, 0x11, 0x4E, 0x2E, 0x2D, + 0x04, 0xD1, 0x35, 0x08, 0xC9, 0xAE, 0xE3, 0x19, 0x58, 0xAC, 0x1A, 0x8B, 0x10, 0x28, 0xED, 0xFE, 0xB1, 0xC0, 0x49, 0x5E, 0x03, 0x37, 0x70, 0x1B, 0xD5, 0xD6, + 0x71, 0xE9, 0xAB, 0x45, 0xF9, 0x4A, 0x3B, 0x1E, 0x46, 0x8B, 0x78, 0xFF, 0xF3, 0xDF, 0x65, 0x53, 0x33, 0xF8, 0xF1, 0xBF, 0x58, 0x00, 0x60, 0x46, 0xBE, 0x79, + 0xAE, 0x03, 0xA5, 0xBE, 0x17, 0x40, 0x29, 0x6A, 0x4F, 0xED, 0x1C, 0x55, 0xE5, 0x49, 0xFB, 0x40, 0x25, 0xEE, 0x14, 0xB0, 0x62, 0x6C, 0x72, 0x16, 0x98, 0xBE, + 0xBD, 0x80, 0x52, 0xCD, 0xF2, 0xCC, 0xE5, 0x9C, 0xB8, 0x61, 0xD7, 0xB0, 0xAC, 0xCB, 0x6B, 0x38, 0x78, 0x8D, 0x33, 0xCC, 0x20, 0xF9, 0x66, 0xE3, 0xF9, 0xCF, + 0x6F, 0x2E, 0xD8, 0x0B, 0xF7, 0x5E, 0x83, 0xBC, 0x88, 0xD5, 0x68, 0x6B, 0x93, 0xA5, 0xCB, 0xAA, 0xF7, 0x26, 0x41, 0x58, 0xF6, 0x11, 0xC6, 0x6B, 0xC3, 0xD7, + 0xC6, 0x46, 0x40, 0x5E, 0x7A, 0x41, 0xA8, 0x9D, 0x6B, 0x11, 0x46, 0xC7, 0x33, 0xE9, 0x4B, 0x12, 0xBA, 0x8C, 0x2F, 0x0E, 0xC9, 0x18, 0xFF, 0xD5, 0x77, 0x00, + 0x34, 0x6A, 0xF5, 0x50, 0x6B, 0x9C, 0x9E, 0xF4, 0x1B, 0x68, 0x7F, 0x51, 0x17, 0x13, 0xFC, 0xAC, 0x22, 0xC0, 0x35, 0x97, 0xBE, 0xD3, 0xD6, 0xCC, 0x71, 0x8B, + 0xBD, 0x24, 0x91, 0x5E, 0xC6, 0x6B, 0xE2, 0xED, 0xB9, 0xDD, 0x70, 0x46, 0xDC, 0x66, 0x4C, 0x19, 0x38, 0xC3, 0xC2, 0x73, 0x83, 0xC4, 0x17, 0x22, 0xED, 0x49, + 0x7C, 0xBD, 0x0B, 0x05, 0x7D, 0xB8, 0x0C, 0xB4, 0xFB, 0xE7, 0xE7, 0x1A, 0x16, 0xB8, 0x89, 0x97, 0x2F, 0x9A, 0xE3, 0x34, 0x5C, 0x5B, 0x4B, 0x5D, 0xF8, 0x05, + 0x42, 0x83, 0xF4, 0x9A, 0xDF, 0x5B, 0x8D, 0x38, 0xA9, 0xF7, 0xCC, 0x46, 0x0D, 0x30, 0x8A, 0x34, 0x5B, 0x49, 0x02, 0x9B, 0x96, 0x11, 0x1A, 0xAD, 0xE4, 0x0B, + 0x1F, 0xA1, 0x57, 0xA0, 0xA4, 0xAD, 0xD1, 0x5B, 0xF2, 0xDB, 0x27, 0x6F, 0x5B, 0x5D, 0x90, 0x21, 0xF0, 0x1B, 0xB5, 0x26, 0xBE, 0x9F, 0xFE, 0xF4, 0x25, 0xB4, + 0xEE, 0xF4, 0xDB, 0x1A, 0xDE, 0x49, 0xB6, 0x95, 0x88, 0xBC, 0x27, 0xAE, 0x09, 0xA1, 0x15, 0xA3, 0x55, 0xA0, 0x64, 0xE8, 0x6E, 0x13, 0x2A, 0x82, 0xD8, 0xF3, + 0x8E, 0x4C, 0x41, 0x62, 0xD3, 0x36, 0x1F, 0x40, 0xB7, 0xE9, 0xE8, 0xB9, 0xCD, 0x82, 0xA2, 0xA4, 0xB5, 0x83, 0x03, 0x70, 0x69, 0x08, 0x4A, 0x04, 0xAC, 0x62, + 0xDA, 0x6C, 0xF0, 0x05, 0x4C, 0xB0, 0xA8, 0x46, 0xEF, 0xA6, 0xF1, 0x10, 0x10, 0x74, 0x43, 0xEF, 0x2A, 0xF4, 0x6D, 0x77, 0x0A, 0x43, 0x8F, 0x56, 0x8C, 0x8D, + 0xDE, 0x46, 0x94, 0xA9, 0xFB, 0xF4, 0x3A, 0xED, 0x24, 0x7D, 0xA3, 0xC9, 0xAF, 0x3F, 0x6C, 0xB4, 0x1A, 0x9C, 0x78, 0x7A, 0x0E, 0xE6, 0xD6, 0x64, 0x07, 0x0F, + 0x28, 0x8D, 0x2D, 0xED, 0xEC, 0x8C, 0x77, 0xC3, 0xA0, 0xF0, 0x22, 0x00, 0xD1, 0x3F, 0xA9, 0x5B, 0x91, 0x29, 0x7E, 0xFA, 0xFE, 0x2F, 0x61, 0xB3, 0xB7, 0x07, + 0x40, 0xF5, 0x13, 0x9C, 0x41, 0xF8, 0xFE, 0x2F, 0xF8, 0xFF, 0xED, 0x03, 0x3A, 0x6D, 0xF0, 0xFD, 0x5F, 0xF8, 0xE7, 0xF6, 0x01, 0xF4, 0x04, 0xC7, 0xB4, 0xBF, + 0xDB, 0x4F, 0x54, 0x0E, 0x59, 0xE9, 0x4D, 0x73, 0xA5, 0x17, 0x89, 0xAD, 0x36, 0x4D, 0xD3, 0x02, 0xA2, 0x3E, 0xC5, 0xFE, 0xDB, 0x34, 0x3D, 0x0B, 0xD4, 0x13, + 0x82, 0x25, 0x0B, 0xA5, 0x3B, 0xA0, 0x12, 0x21, 0xA8, 0xE8, 0x0D, 0xA8, 0xF6, 0x84, 0x42, 0x6A, 0xDC, 0x55, 0x62, 0x03, 0x11, 0x90, 0x0B, 0xFC, 0x2C, 0xF8, + 0x2B, 0x37, 0x6C, 0x86, 0x09, 0xA7, 0xC8, 0x91, 0xF8, 0x68, 0x94, 0x60, 0x01, 0x7F, 0xD0, 0x0E, 0xE0, 0x1A, 0x5C, 0x69, 0x91, 0xB1, 0xDD, 0x8B, 0xEC, 0x30, + 0xA6, 0x94, 0xDD, 0xCC, 0xB1, 0xC3, 0x0F, 0xA6, 0xF3, 0xB9, 0x89, 0xEF, 0x64, 0x4C, 0x87, 0x8A, 0x8C, 0x88, 0x10, 0xE8, 0x09, 0xFE, 0x0F, 0xE4, 0x82, 0x7F, + 0x72, 0xF5, 0x03, 0x58, 0x59, 0x09, 0xDF, 0xA4, 0x53, 0x00, 0x1F, 0x6F, 0xDA, 0x1A, 0x3B, 0x58, 0x83, 0x67, 0xB8, 0x16, 0x9E, 0xE3, 0x9F, 0xB5, 0xD0, 0x1E, + 0x5E, 0xE0, 0x47, 0x70, 0x8D, 0xD6, 0xAC, 0x78, 0x89, 0x1D, 0x20, 0x14, 0xAD, 0x31, 0x28, 0x14, 0x3B, 0x82, 0x6B, 0xF8, 0xCE, 0x0F, 0xB0, 0xDD, 0xB6, 0x36, + 0xB6, 0x5D, 0x97, 0x1E, 0x94, 0x50, 0x1F, 0xA7, 0xFA, 0x27, 0xC1, 0x0D, 0x70, 0xC0, 0x49, 0xBB, 0x7D, 0x10, 0xAC, 0xA3, 0xB3, 0xF5, 0xED, 0x03, 0x82, 0xF7, + 0x28, 0x91, 0x70, 0xBC, 0xE6, 0xC7, 0x70, 0x1D, 0xE8, 0xC3, 0x3B, 0x82, 0x60, 0x7A, 0x61, 0x1D, 0x5F, 0x00, 0x88, 0x10, 0xEF, 0x73, 0xE2, 0xE1, 0x6C, 0x1D, + 0x9D, 0x61, 0x6B, 0xDA, 0x96, 0xB3, 0x01, 0xA7, 0xEB, 0xF8, 0x14, 0xEE, 0xB2, 0x0F, 0xBA, 0x03, 0x11, 0x8C, 0xA7, 0xDB, 0x07, 0x9C, 0x27, 0xB8, 0xC4, 0x8F, + 0xD2, 0xA2, 0xC6, 0x98, 0x10, 0xF2, 0x28, 0xF2, 0x8C, 0x25, 0x69, 0x29, 0x7F, 0x80, 0x7F, 0x5C, 0x3A, 0x04, 0x0F, 0x9F, 0xAD, 0x5F, 0x59, 0xCD, 0x06, 0x5F, + 0x90, 0x6D, 0x60, 0x0C, 0x93, 0xDB, 0x74, 0x3D, 0xD7, 0x74, 0x6C, 0x13, 0x1D, 0xA5, 0xD9, 0xD2, 0xCE, 0x47, 0x3C, 0x8E, 0xA1, 0x41, 0x03, 0xB8, 0x6C, 0xA4, + 0xB9, 0xA8, 0xC5, 0x92, 0x62, 0xA3, 0xD5, 0xA5, 0x76, 0xC8, 0x6D, 0x0D, 0x51, 0x70, 0x17, 0xAC, 0x86, 0x03, 0x81, 0x15, 0x38, 0x32, 0xDE, 0x52, 0x88, 0x84, + 0x42, 0x4B, 0x58, 0x28, 0x1A, 0x39, 0xD4, 0xF6, 0x52, 0x51, 0xB6, 0xC0, 0xAB, 0x85, 0x03, 0xDF, 0x4F, 0x3B, 0x30, 0xA8, 0xCA, 0x0F, 0x9B, 0x8D, 0x4B, 0x7C, + 0x10, 0xF8, 0xF7, 0xC6, 0x43, 0x04, 0x7A, 0xD8, 0xF8, 0xD7, 0xA9, 0xD6, 0x78, 0x28, 0x7B, 0xF2, 0x6D, 0xDA, 0xE5, 0x98, 0xC6, 0xA6, 0x15, 0x35, 0x36, 0x95, + 0x34, 0x36, 0xBD, 0x5B, 0x8D, 0xC9, 0x0B, 0xC1, 0xDB, 0x68, 0x4D, 0x5E, 0x79, 0x2D, 0xD0, 0x5C, 0x69, 0x7B, 0xAE, 0x34, 0xAE, 0xAD, 0xA9, 0x4A, 0x5B, 0x9B, + 0xA8, 0x89, 0xA5, 0x38, 0xF0, 0x1E, 0xE2, 0xBF, 0xFC, 0xE5, 0xCD, 0x6B, 0x0C, 0x95, 0x6A, 0x95, 0x45, 0x1A, 0x4B, 0x97, 0x23, 0x0A, 0x0C, 0x98, 0x3B, 0x13, + 0x81, 0x3B, 0x91, 0x43, 0x1F, 0x36, 0xB4, 0x26, 0x45, 0x89, 0x19, 0xB4, 0xC4, 0x10, 0x78, 0xE0, 0xAD, 0xE6, 0xBB, 0x18, 0x6C, 0x85, 0xF3, 0xC6, 0xAD, 0x0A, + 0x6C, 0x01, 0x1B, 0x54, 0x52, 0x22, 0xC3, 0x9C, 0x71, 0x18, 0x29, 0x27, 0xEC, 0xDC, 0x45, 0xA8, 0xBF, 0x06, 0x55, 0x83, 0x9A, 0x88, 0xE9, 0x71, 0x6C, 0x0B, + 0x4A, 0xA5, 0xC3, 0x23, 0x7F, 0x25, 0x01, 0xF1, 0x39, 0x6C, 0x85, 0x81, 0x8B, 0x4C, 0x50, 0x09, 0x8D, 0x98, 0xBA, 0xCC, 0xC7, 0xB3, 0xAE, 0x83, 0x67, 0xAD, + 0xC0, 0xC3, 0x33, 0x4F, 0x25, 0x34, 0x7C, 0xA2, 0x2B, 0x17, 0x4B, 0x35, 0x62, 0xF8, 0xE4, 0x92, 0x8A, 0x27, 0x9E, 0xE9, 0xAA, 0xF1, 0xC4, 0x27, 0x45, 0xF2, + 0xF1, 0x54, 0x94, 0x0D, 0x9F, 0x89, 0x50, 0xD8, 0x73, 0xBA, 0x1A, 0xE9, 0x89, 0xFF, 0x36, 0xAF, 0x3F, 0x26, 0x06, 0x44, 0x8B, 0xF8, 0xCF, 0x5E, 0xBC, 0x64, + 0xFA, 0x5E, 0x54, 0x8B, 0xC4, 0x51, 0x66, 0x03, 0xE2, 0x74, 0x8D, 0x10, 0xE2, 0x13, 0x8C, 0xE3, 0x49, 0xD0, 0xC5, 0x0A, 0x37, 0x12, 0x63, 0xE6, 0x56, 0xD7, + 0x05, 0x02, 0x28, 0xC2, 0xD6, 0x69, 0x2F, 0x6D, 0x9E, 0x19, 0x5C, 0xEC, 0x72, 0x1E, 0x3A, 0x76, 0x37, 0x07, 0x23, 0xCF, 0x30, 0xC9, 0x16, 0x78, 0x31, 0x0F, + 0x1B, 0x1D, 0xC5, 0x48, 0xB8, 0x06, 0x47, 0x47, 0xD9, 0x3C, 0xC3, 0x3B, 0xE0, 0x9F, 0x7E, 0x42, 0x0C, 0x38, 0x1F, 0x15, 0x0F, 0xCD, 0x4C, 0x28, 0x04, 0xB5, + 0x86, 0x58, 0xB2, 0x6B, 0x9C, 0x66, 0x2A, 0x6E, 0x68, 0xC1, 0x17, 0xE1, 0xB4, 0x27, 0x8C, 0xC6, 0xC4, 0x07, 0x0D, 0xC6, 0x30, 0xF8, 0x8E, 0xBE, 0x5B, 0xC4, + 0x90, 0xD1, 0x07, 0xAB, 0x23, 0x4C, 0xEC, 0x1A, 0x0E, 0x5C, 0x53, 0x97, 0xD8, 0xDA, 0x4E, 0xC7, 0x73, 0x89, 0xBA, 0xD7, 0x44, 0xFD, 0xCE, 0x3B, 0xE2, 0x67, + 0xFC, 0x89, 0xEF, 0xB8, 0x99, 0x4F, 0xC2, 0xA5, 0xEF, 0xF2, 0x7A, 0x3E, 0x5B, 0xDF, 0x28, 0x87, 0x92, 0x3B, 0xB4, 0xCD, 0x83, 0x03, 0xED, 0x69, 0x18, 0x1A, + 0xA0, 0x00, 0x5C, 0xA7, 0x9C, 0xA1, 0x7C, 0x34, 0x83, 0x4F, 0x4A, 0x78, 0x3E, 0x1A, 0x25, 0x7B, 0xA8, 0x98, 0x30, 0xBF, 0xC5, 0xCF, 0x3C, 0x09, 0x77, 0xA6, + 0xA8, 0xBA, 0xFF, 0x5E, 0x12, 0x7F, 0x7D, 0x45, 0x05, 0xE6, 0xF9, 0x4F, 0x1D, 0xA7, 0xD9, 0xE8, 0xC6, 0xCB, 0xCE, 0x0D, 0x36, 0x06, 0xEF, 0x02, 0xAA, 0x4B, + 0xE8, 0x03, 0x74, 0x1C, 0xDB, 0x3C, 0xE3, 0x26, 0xD2, 0x3B, 0x8C, 0xBB, 0xCE, 0xB9, 0x32, 0xD2, 0x83, 0x7E, 0x80, 0xF0, 0xDC, 0xCF, 0x64, 0xBD, 0x5C, 0x80, + 0xF8, 0xE3, 0x61, 0x7C, 0x6A, 0x62, 0x81, 0x4B, 0x87, 0x74, 0x01, 0xF2, 0x82, 0x0F, 0xE4, 0xFA, 0x87, 0x0A, 0xA0, 0x58, 0x05, 0xD4, 0x3A, 0xD1, 0x13, 0xB3, + 0x5F, 0xC1, 0xB8, 0xBD, 0xA7, 0x3E, 0x53, 0x4C, 0x81, 0x70, 0x02, 0xB9, 0xF0, 0x44, 0xF2, 0x4A, 0xF5, 0x90, 0x9A, 0x9E, 0xB8, 0x6D, 0xDD, 0x8B, 0x23, 0xC3, + 0x72, 0x61, 0x19, 0x21, 0x49, 0x06, 0x87, 0xC8, 0x16, 0xC4, 0xCD, 0xB9, 0x17, 0x92, 0x54, 0xC4, 0xB0, 0x5D, 0x3B, 0xB4, 0x0D, 0xE7, 0x7D, 0x6C, 0x8D, 0x5F, + 0xD4, 0xFD, 0x15, 0x3E, 0x5E, 0xC3, 0xFF, 0x33, 0x73, 0x10, 0xD5, 0xC6, 0xCD, 0x19, 0x0B, 0x89, 0xE2, 0x41, 0x6C, 0x25, 0xB2, 0x1C, 0x12, 0x61, 0x81, 0xDF, + 0x17, 0x3D, 0xDD, 0xBF, 0x4F, 0x8F, 0xEE, 0x45, 0x4A, 0x13, 0xD1, 0xE3, 0x5C, 0x8B, 0x6F, 0xA4, 0x14, 0x9C, 0xC5, 0x9D, 0xC2, 0x21, 0x90, 0x4B, 0x18, 0x98, + 0x6F, 0x45, 0xEA, 0x5D, 0x40, 0xB5, 0x89, 0xB6, 0xF0, 0xFF, 0x51, 0xFF, 0x2B, 0x8A, 0xFA, 0x5F, 0x2E, 0xC4, 0x17, 0xD8, 0x76, 0xCA, 0x03, 0x58, 0x3B, 0xF5, + 0xB4, 0xE0, 0xC3, 0x06, 0x54, 0x3B, 0xCA, 0x79, 0x3F, 0x1E, 0xBA, 0x63, 0xFB, 0x9A, 0xD9, 0x16, 0x23, 0x3A, 0xB6, 0x2C, 0x94, 0x11, 0x4E, 0xDF, 0xE3, 0x04, + 0x37, 0xCE, 0x76, 0x37, 0x1B, 0x6C, 0x6D, 0x81, 0xC6, 0xE3, 0xDB, 0xB8, 0x24, 0x99, 0x79, 0xAB, 0xA2, 0x96, 0x3E, 0x44, 0x9D, 0x6B, 0x92, 0x6A, 0x1C, 0xB5, + 0xE6, 0xDF, 0x6A, 0x2F, 0xED, 0x5A, 0x7C, 0xD3, 0x9D, 0x27, 0x03, 0x00, 0x88, 0xBE, 0xF2, 0x7E, 0xAE, 0x85, 0x3E, 0xF5, 0x1A, 0x09, 0x2D, 0x71, 0xCB, 0xB0, + 0x0A, 0xB2, 0x0A, 0x11, 0xD3, 0x32, 0x2F, 0x89, 0x99, 0x85, 0xD2, 0x1A, 0x41, 0x56, 0xBE, 0x0C, 0x2D, 0x92, 0xA7, 0xE7, 0x9A, 0xBB, 0x74, 0x1C, 0xB0, 0x41, + 0x64, 0x01, 0x6C, 0x50, 0xBE, 0xAB, 0x0C, 0xD1, 0xFF, 0xB9, 0xF1, 0x2C, 0xA2, 0x3C, 0x21, 0x81, 0x07, 0x0F, 0x92, 0xD8, 0x70, 0x91, 0x81, 0x95, 0xF1, 0x51, + 0x6F, 0x0C, 0xFE, 0xC2, 0x73, 0x27, 0xF6, 0x34, 0xCE, 0xB3, 0x9C, 0x24, 0x48, 0xD6, 0xF7, 0x13, 0x82, 0x97, 0x6A, 0x1C, 0x20, 0xC4, 0xB6, 0xA8, 0x80, 0xE8, + 0x9B, 0xFA, 0x32, 0xB3, 0xB1, 0x4F, 0xA8, 0xD5, 0x37, 0x09, 0x7F, 0x87, 0x57, 0x0B, 0xE4, 0x8F, 0xC6, 0x1C, 0x5F, 0x10, 0xF5, 0x4E, 0xD4, 0x95, 0x8C, 0x71, + 0x9A, 0xC0, 0x88, 0x8C, 0xA5, 0xE8, 0xC6, 0x1F, 0xC5, 0x87, 0x6F, 0xB3, 0xE1, 0x6F, 0xD4, 0x91, 0xBF, 0x60, 0x48, 0x3B, 0x07, 0x34, 0xF8, 0xC6, 0x1D, 0x29, + 0xB5, 0x67, 0x6B, 0x03, 0x0A, 0x98, 0x83, 0x84, 0x76, 0x90, 0x45, 0x52, 0x48, 0xB9, 0x78, 0x9D, 0xA9, 0x42, 0x20, 0x14, 0xDD, 0x6A, 0x8C, 0xA2, 0xA0, 0xBD, + 0xC2, 0x61, 0x2E, 0x2A, 0x2D, 0xB5, 0x97, 0x31, 0x83, 0x6E, 0xA4, 0x75, 0xFA, 0x02, 0x27, 0x80, 0xBE, 0xC0, 0xB5, 0xFB, 0x08, 0x73, 0x74, 0x21, 0x59, 0x54, + 0x0A, 0x67, 0x8B, 0x26, 0x95, 0x65, 0xED, 0xB3, 0xD4, 0x17, 0xE7, 0xBD, 0xD8, 0xBC, 0xEE, 0x28, 0x13, 0xF4, 0x35, 0xF9, 0x93, 0x65, 0x72, 0xBC, 0xDE, 0x32, + 0x09, 0x14, 0xE0, 0x64, 0x8B, 0xAD, 0x69, 0xA4, 0xCB, 0xF1, 0xDC, 0x0E, 0x15, 0x08, 0x1B, 0xFD, 0x46, 0x9D, 0x7C, 0x22, 0xFB, 0x1E, 0x8B, 0x5F, 0xB4, 0xD4, + 0x06, 0x44, 0x89, 0x49, 0x72, 0xFA, 0xDA, 0x7C, 0xCF, 0x79, 0x72, 0x6D, 0xF8, 0x38, 0xF5, 0x8D, 0x0A, 0x4E, 0x2D, 0xC9, 0x30, 0x14, 0x6C, 0x2D, 0x91, 0xA2, + 0x48, 0xAE, 0x26, 0x8A, 0x15, 0xBC, 0x64, 0x65, 0x2E, 0x2F, 0x61, 0x7D, 0xF2, 0x09, 0xB4, 0x0B, 0x70, 0xAA, 0x41, 0xFB, 0xFE, 0x2F, 0x8A, 0xE2, 0x56, 0x9B, + 0x80, 0xEF, 0x07, 0x33, 0x62, 0xD1, 0x85, 0x82, 0x10, 0xBF, 0xF2, 0x87, 0xCB, 0x30, 0x89, 0xD5, 0xC3, 0xDB, 0x4F, 0x91, 0x85, 0x44, 0xA9, 0xA3, 0x74, 0xF0, + 0x40, 0x17, 0x99, 0x8B, 0xC7, 0x0D, 0xAC, 0xDC, 0x56, 0x4C, 0x15, 0xE1, 0x8F, 0xC5, 0x05, 0xA7, 0xCB, 0x3E, 0x4D, 0xFE, 0x13, 0x54, 0x23, 0x29, 0x33, 0x6D, + 0xF1, 0x61, 0x0F, 0x68, 0xC0, 0x12, 0x01, 0x8C, 0xE9, 0x08, 0x07, 0x37, 0x4C, 0x4C, 0x09, 0x09, 0x33, 0x66, 0x38, 0x2F, 0xE5, 0x2B, 0xB0, 0x3C, 0xAB, 0x47, + 0xB2, 0xF8, 0x23, 0x80, 0xC1, 0x4A, 0xEB, 0x5E, 0x24, 0x86, 0x2C, 0x0E, 0xEC, 0x40, 0x42, 0x90, 0x10, 0x51, 0x9E, 0x98, 0x92, 0x3B, 0x8B, 0x1B, 0x71, 0xFC, + 0xC8, 0x1D, 0x6B, 0x69, 0x72, 0xFE, 0xA3, 0xC9, 0x8F, 0xF6, 0xFB, 0x3B, 0x35, 0x99, 0x7F, 0xF1, 0x79, 0x11, 0x29, 0x0E, 0xB5, 0xEA, 0x90, 0x93, 0x19, 0xF2, + 0x95, 0x90, 0x72, 0x87, 0xC5, 0xAF, 0xF8, 0x41, 0x78, 0xA3, 0xF8, 0xA0, 0xC4, 0xCC, 0x0C, 0xFA, 0xE4, 0x0A, 0x2D, 0x62, 0x30, 0x71, 0x96, 0x1C, 0x83, 0x49, + 0xE2, 0x51, 0x56, 0x67, 0xB9, 0xE2, 0x62, 0xD6, 0xC5, 0xDC, 0xF5, 0xDA, 0x26, 0xAB, 0xC2, 0xE9, 0x50, 0xFA, 0x74, 0x00, 0x95, 0x57, 0xDC, 0xE0, 0x22, 0x7A, + 0x1A, 0xA4, 0xB4, 0x65, 0xFC, 0xE4, 0x88, 0x84, 0x83, 0x3E, 0x16, 0x52, 0x6D, 0xB5, 0x82, 0x82, 0x26, 0x9A, 0x22, 0xD6, 0xF2, 0xB6, 0x89, 0xCD, 0xDC, 0x52, + 0x7B, 0xEA, 0xB9, 0xE5, 0xCD, 0xE5, 0xA7, 0x48, 0xE4, 0xDE, 0x8D, 0xEB, 0x0A, 0x8D, 0xE3, 0x47, 0x5F, 0xA4, 0xA6, 0x22, 0x1F, 0x15, 0x35, 0x8C, 0xDE, 0x3E, + 0xD0, 0x90, 0x34, 0x14, 0x84, 0xDE, 0x82, 0xED, 0x42, 0x4F, 0x05, 0x92, 0x15, 0x9D, 0x7D, 0xEC, 0xE2, 0xFD, 0x26, 0x2F, 0x5A, 0x64, 0xE9, 0x24, 0x97, 0x18, + 0xE4, 0xDD, 0xEC, 0x8D, 0x64, 0xC5, 0x49, 0xA7, 0x2F, 0x95, 0x3D, 0xA0, 0xAE, 0xBB, 0x81, 0x6F, 0xB2, 0x70, 0x1E, 0x3D, 0x28, 0x82, 0xD1, 0x06, 0x0F, 0x3F, + 0xB1, 0x3E, 0x31, 0xFD, 0x26, 0xAC, 0xA2, 0x55, 0x4A, 0x8B, 0xB7, 0x48, 0x93, 0x12, 0x4F, 0xF1, 0x30, 0x0F, 0x0D, 0x30, 0x8E, 0xF3, 0x37, 0x01, 0xDC, 0xD3, + 0x64, 0x8B, 0xC9, 0x09, 0xAB, 0xB1, 0x98, 0x78, 0x1C, 0x4B, 0x12, 0x2F, 0xE5, 0x22, 0xF6, 0x94, 0xD1, 0x93, 0x8F, 0xE6, 0x18, 0xD2, 0xCF, 0x73, 0x70, 0x1F, + 0xF0, 0xD7, 0x55, 0xB3, 0x75, 0x5B, 0xC4, 0x0E, 0x13, 0x57, 0x6C, 0x3B, 0x55, 0x89, 0xA0, 0x81, 0x5E, 0x8D, 0x2D, 0x21, 0x1F, 0x35, 0x3A, 0xD9, 0xE4, 0x2F, + 0x5D, 0x31, 0x68, 0xC8, 0x13, 0xEC, 0x79, 0x56, 0xB4, 0xAC, 0x6E, 0x4C, 0x20, 0x88, 0x43, 0x78, 0x86, 0xD8, 0x54, 0x69, 0x28, 0xD9, 0x85, 0x00, 0x88, 0x68, + 0x8F, 0xFC, 0x20, 0x87, 0x72, 0x7C, 0xB0, 0xC8, 0x34, 0xDC, 0x6B, 0x23, 0x90, 0xED, 0xDD, 0x04, 0x5C, 0x21, 0xE1, 0x26, 0xDF, 0xD4, 0x19, 0x80, 0xCE, 0x6D, + 0x97, 0x9D, 0x75, 0xE9, 0x4E, 0x0C, 0x2C, 0xED, 0x51, 0x7D, 0xF4, 0x24, 0x71, 0x9B, 0x7D, 0xF8, 0x55, 0xDC, 0x67, 0x67, 0x0C, 0x20, 0xEA, 0x05, 0x3F, 0xB5, + 0xDA, 0x35, 0x16, 0x0B, 0xE2, 0x5A, 0x17, 0x33, 0xDB, 0xB1, 0x9A, 0xAC, 0x69, 0xF4, 0xEC, 0x89, 0xCF, 0xBE, 0xD2, 0x4E, 0x9F, 0x73, 0xE0, 0x58, 0xC1, 0x11, + 0x2F, 0xD8, 0xB5, 0x66, 0x63, 0x60, 0x89, 0xC7, 0x54, 0x38, 0x58, 0xD7, 0xF2, 0x8D, 0xD5, 0x2B, 0x7C, 0x0C, 0x8E, 0x6A, 0xB2, 0xDD, 0x6B, 0xF7, 0x38, 0x40, + 0x08, 0x95, 0x8E, 0x90, 0x16, 0xE2, 0xC5, 0xC7, 0x85, 0x7E, 0x7D, 0xF7, 0x3A, 0xC6, 0x1B, 0x7A, 0xCF, 0xD9, 0xA5, 0x66, 0x83, 0x3E, 0x47, 0x77, 0xF0, 0xC7, + 0x02, 0x57, 0x5F, 0x45, 0x8C, 0x97, 0xC4, 0x88, 0x8F, 0xC8, 0xA1, 0xA8, 0x18, 0xF8, 0x0F, 0x32, 0x52, 0xB8, 0xEC, 0x42, 0x90, 0x46, 0x4B, 0x6D, 0xAA, 0x9A, + 0x8A, 0x07, 0xE8, 0xB0, 0x39, 0x72, 0xF2, 0x23, 0x8C, 0xF8, 0xFE, 0x49, 0x0C, 0x1F, 0xF4, 0xF1, 0x50, 0x6B, 0xEA, 0x3D, 0xFD, 0x61, 0x93, 0x5E, 0x7F, 0x03, + 0xEC, 0xCC, 0x9A, 0xAD, 0x87, 0xFD, 0x56, 0x0B, 0x3F, 0xAC, 0x6E, 0x92, 0x66, 0x67, 0x20, 0x40, 0xE0, 0x0F, 0x85, 0x61, 0x9D, 0xE4, 0xDF, 0x7F, 0xE9, 0x2D, + 0xFD, 0xA0, 0x08, 0xE0, 0x8D, 0xED, 0x62, 0x1A, 0x2C, 0x02, 0xB9, 0x22, 0x20, 0x58, 0x2B, 0x03, 0xA2, 0xD3, 0xE7, 0xFE, 0xC4, 0xE0, 0x8B, 0x3E, 0x0E, 0x05, + 0xF5, 0xB6, 0x54, 0x69, 0xF3, 0x62, 0x8F, 0xE0, 0xE4, 0x71, 0x53, 0x2C, 0xE7, 0xDC, 0xCA, 0xC6, 0x11, 0x97, 0x53, 0x7C, 0xF8, 0x9D, 0xD1, 0x7F, 0x2A, 0xD6, + 0xF0, 0x02, 0x25, 0x33, 0xAD, 0x5C, 0xA5, 0x06, 0x54, 0x16, 0x37, 0x85, 0xC5, 0x60, 0x72, 0xEE, 0x35, 0x3D, 0xEC, 0x4C, 0x16, 0x7E, 0x17, 0x4B, 0x70, 0xCF, + 0xB9, 0x08, 0x86, 0xEC, 0x1A, 0x8E, 0xC1, 0xA2, 0xA8, 0x0D, 0x63, 0xB2, 0xA2, 0x54, 0x02, 0xB7, 0xA5, 0xE4, 0xC3, 0x07, 0x70, 0x25, 0x0D, 0xA4, 0xD7, 0xED, + 0x4A, 0x6D, 0xA5, 0x81, 0x61, 0x61, 0xB2, 0x4E, 0xBF, 0x20, 0x96, 0xA2, 0x00, 0xAC, 0x59, 0xCE, 0xE5, 0x79, 0x0B, 0xCE, 0x3F, 0xC0, 0xB5, 0xA2, 0x78, 0x85, + 0x8D, 0xF8, 0x98, 0x49, 0x8A, 0x56, 0x39, 0x23, 0xDD, 0xEC, 0x28, 0x37, 0x15, 0xC6, 0xF2, 0x46, 0xB7, 0xD9, 0x91, 0xED, 0xAD, 0x64, 0x20, 0xE2, 0xC5, 0xDB, + 0xB1, 0x08, 0x49, 0xB1, 0xBC, 0x89, 0x2C, 0x6F, 0x31, 0xC2, 0x2F, 0x69, 0x21, 0xBF, 0x1F, 0x9C, 0x89, 0x8B, 0x54, 0x14, 0x17, 0xE1, 0xE2, 0xC2, 0x06, 0xF1, + 0xF0, 0xB2, 0x7C, 0xBA, 0x21, 0xB2, 0xFF, 0xDF, 0x9E, 0xC5, 0x9C, 0xAD, 0xC6, 0x85, 0x74, 0xF2, 0xE1, 0xBC, 0xC4, 0x5E, 0x71, 0x83, 0xC4, 0x47, 0x44, 0x18, + 0x5B, 0xAB, 0x71, 0x35, 0xB6, 0xC4, 0x74, 0x00, 0x36, 0x88, 0xD9, 0x52, 0x4F, 0x1A, 0x08, 0x56, 0x9E, 0x93, 0x90, 0xEF, 0x60, 0x33, 0x5C, 0x4B, 0x8B, 0x3E, + 0x7B, 0x1B, 0x11, 0x1B, 0x5D, 0x29, 0xA2, 0x39, 0x02, 0x62, 0x95, 0x57, 0x74, 0x5A, 0x89, 0xEC, 0x08, 0x3A, 0x36, 0xE1, 0x18, 0x81, 0x98, 0xAA, 0x38, 0x4A, + 0x4F, 0x38, 0xB1, 0x1A, 0xDE, 0xA2, 0xD4, 0xA7, 0x2A, 0x75, 0x19, 0xC0, 0x87, 0x68, 0x39, 0x75, 0x01, 0x53, 0x02, 0x86, 0x99, 0x2A, 0x8B, 0x16, 0xEC, 0x77, + 0x76, 0x20, 0x9E, 0x20, 0x66, 0x67, 0xEC, 0x83, 0xE3, 0x67, 0x07, 0xB3, 0x70, 0xEE, 0x8C, 0xEE, 0xFD, 0x2F, 0xFC, 0x34, 0x4E, 0xF9, 0x5F, 0xA4, 0x00, 0x00 }; -//File: index_ov3660.html.gz, Size: 8887 -#define index_ov3660_html_gz_len 8887 -const uint8_t index_ov3660_html_gz[] = { - 0x1F, 0x8B, 0x08, 0x08, 0xA3, 0xFA, 0x69, 0x5E, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x6F, 0x76, 0x33, 0x36, 0x36, 0x30, 0x2E, 0x68, 0x74, 0x6D, - 0x6C, 0x00, 0xED, 0x3D, 0x69, 0x73, 0xDB, 0x46, 0xB2, 0xDF, 0xFD, 0x2B, 0x60, 0x24, 0x6B, 0x51, 0x65, 0x91, 0xE2, 0xAD, 0x23, 0x12, 0xFD, 0x6C, 0x59, 0xB1, - 0x53, 0x1B, 0x67, 0xBD, 0x71, 0xE2, 0x24, 0xB5, 0xB5, 0xE5, 0x80, 0xC4, 0x90, 0x44, 0x0C, 0x02, 0x5C, 0x00, 0xD4, 0x91, 0x94, 0x7E, 0xC7, 0xFB, 0x41, 0xEF, - 0x8F, 0xBD, 0xEE, 0x39, 0x70, 0x71, 0x00, 0x0C, 0x00, 0x11, 0x52, 0xF2, 0x1E, 0x5D, 0x65, 0xE1, 0x98, 0xEE, 0xE9, 0x7B, 0x7A, 0x7A, 0x06, 0xC0, 0xD9, 0x53, - 0xD3, 0x9D, 0x05, 0xB7, 0x6B, 0xA2, 0x2D, 0x83, 0x95, 0x3D, 0x79, 0x72, 0xC6, 0xFE, 0x68, 0xF0, 0x3B, 0x5B, 0x12, 0xC3, 0x64, 0x87, 0xF4, 0x74, 0x45, 0x02, - 0x43, 0x9B, 0x2D, 0x0D, 0xCF, 0x27, 0xC1, 0xB9, 0xBE, 0x09, 0xE6, 0xED, 0x63, 0x3D, 0x7D, 0xDB, 0x31, 0x56, 0xE4, 0x5C, 0xBF, 0xB2, 0xC8, 0xF5, 0xDA, 0xF5, - 0x02, 0x5D, 0x9B, 0xB9, 0x4E, 0x40, 0x1C, 0x68, 0x7E, 0x6D, 0x99, 0xC1, 0xF2, 0xDC, 0x24, 0x57, 0xD6, 0x8C, 0xB4, 0xE9, 0xC9, 0x81, 0xE5, 0x58, 0x81, 0x65, - 0xD8, 0x6D, 0x7F, 0x66, 0xD8, 0xE4, 0xBC, 0x17, 0xC7, 0x15, 0x58, 0x81, 0x4D, 0x26, 0x97, 0x1F, 0xDE, 0x0F, 0xFA, 0xDA, 0x3F, 0x3E, 0x0E, 0xC6, 0xE3, 0xEE, - 0xD9, 0x21, 0xBB, 0x16, 0xB5, 0xF1, 0x83, 0xDB, 0xF8, 0x39, 0xFE, 0xA6, 0xAE, 0x79, 0xAB, 0xFD, 0x91, 0xB8, 0x84, 0xBF, 0x39, 0x10, 0xD1, 0x9E, 0x1B, 0x2B, - 0xCB, 0xBE, 0x3D, 0xD5, 0x5E, 0x7A, 0xD0, 0xE7, 0xC1, 0x5B, 0x62, 0x5F, 0x91, 0xC0, 0x9A, 0x19, 0x07, 0xBE, 0xE1, 0xF8, 0x6D, 0x9F, 0x78, 0xD6, 0xFC, 0xAB, - 0x2D, 0xC0, 0xA9, 0x31, 0xFB, 0xBC, 0xF0, 0xDC, 0x8D, 0x63, 0x9E, 0x6A, 0x5F, 0xF4, 0x8E, 0xF1, 0xDF, 0x76, 0xA3, 0x99, 0x6B, 0xBB, 0x1E, 0xDC, 0xBF, 0xFC, - 0x1A, 0xFF, 0x6D, 0xDF, 0xA7, 0xBD, 0xFB, 0xD6, 0xEF, 0xE4, 0x54, 0xEB, 0x8D, 0xD7, 0x37, 0x89, 0xFB, 0x77, 0x4F, 0x12, 0xA7, 0xCB, 0x7E, 0x16, 0xF5, 0x1C, - 0xFE, 0x38, 0x1F, 0xDE, 0x27, 0xB3, 0xC0, 0x72, 0x9D, 0xCE, 0xCA, 0xB0, 0x1C, 0x09, 0x26, 0xD3, 0xF2, 0xD7, 0xB6, 0x01, 0x32, 0x98, 0xDB, 0x24, 0x17, 0xCF, - 0x17, 0x2B, 0xE2, 0x6C, 0x0E, 0x0A, 0xB0, 0x21, 0x92, 0xB6, 0x69, 0x79, 0xAC, 0xD5, 0x29, 0xCA, 0x61, 0xB3, 0x72, 0x0A, 0xD1, 0xE6, 0xD1, 0xE5, 0xB8, 0x0E, - 0x91, 0x08, 0x10, 0x3B, 0xBA, 0xF6, 0x8C, 0x35, 0x36, 0xC0, 0xBF, 0xDB, 0x4D, 0x56, 0x96, 0xC3, 0x8C, 0xEA, 0x54, 0x1B, 0x0C, 0xBB, 0xEB, 0x9B, 0x02, 0x55, - 0x0E, 0xC6, 0xF8, 0x6F, 0xBB, 0xD1, 0xDA, 0x30, 0x4D, 0xCB, 0x59, 0x9C, 0x6A, 0xC7, 0x52, 0x14, 0xAE, 0x67, 0x12, 0xAF, 0xED, 0x19, 0xA6, 0xB5, 0xF1, 0x4F, - 0xB5, 0xA1, 0xAC, 0xCD, 0xCA, 0xF0, 0x16, 0x40, 0x4B, 0xE0, 0x02, 0xB1, 0xED, 0x9E, 0x94, 0x12, 0xDE, 0xC4, 0xB3, 0x16, 0xCB, 0x00, 0x54, 0xBA, 0xD5, 0x26, - 0x2D, 0x34, 0xEE, 0x42, 0x45, 0xFA, 0xCC, 0x95, 0x9B, 0x5C, 0x6A, 0x86, 0x6D, 0x2D, 0x9C, 0xB6, 0x15, 0x90, 0x15, 0xB0, 0xE3, 0x07, 0x1E, 0x09, 0x66, 0xCB, - 0x3C, 0x52, 0xE6, 0xD6, 0x62, 0xE3, 0x11, 0x09, 0x21, 0xA1, 0xDC, 0x72, 0x18, 0x86, 0x9B, 0xDB, 0xB7, 0xDA, 0xD7, 0x64, 0xFA, 0xD9, 0x0A, 0xDA, 0x5C, 0x26, - 0x53, 0x32, 0x77, 0x3D, 0x22, 0x6D, 0x29, 0x5A, 0xD8, 0xEE, 0xEC, 0x73, 0xDB, 0x0F, 0x0C, 0x2F, 0x50, 0x41, 0x68, 0xCC, 0x03, 0xE2, 0x15, 0xE3, 0x23, 0x68, - 0x15, 0xC5, 0xD8, 0xB2, 0xBB, 0xE5, 0x0D, 0x2C, 0xC7, 0xB6, 0x1C, 0xA2, 0x4E, 0x5E, 0x56, 0xBF, 0x49, 0x74, 0xAC, 0x95, 0x82, 0x62, 0xAC, 0xD5, 0x22, 0xCF, - 0x4A, 0x28, 0xAF, 0xDB, 0x9D, 0x71, 0xBF, 0xE9, 0x75, 0xBB, 0x7F, 0xDB, 0xBE, 0xB9, 0x24, 0xCC, 0x4C, 0x8D, 0x4D, 0xE0, 0xD6, 0xF7, 0x88, 0x2D, 0xB7, 0x4A, - 0xF1, 0xF1, 0x5F, 0x2B, 0x62, 0x5A, 0x86, 0xD6, 0x8A, 0xB9, 0xF3, 0x71, 0x17, 0x6C, 0x6A, 0x5F, 0x33, 0x1C, 0x53, 0x6B, 0xB9, 0x9E, 0x05, 0x8E, 0x60, 0xD0, - 0x70, 0x63, 0xC3, 0x15, 0x18, 0x38, 0xD6, 0x64, 0x5F, 0xC2, 0x72, 0x8E, 0xCF, 0xC4, 0x25, 0x22, 0x77, 0x1B, 0xFC, 0x29, 0x84, 0x1C, 0xFC, 0x15, 0x3A, 0x90, - 0x84, 0x47, 0x8A, 0x3E, 0x4F, 0x5F, 0x71, 0x0A, 0xB3, 0x74, 0x86, 0xBF, 0x95, 0x71, 0xD3, 0xCE, 0xD5, 0x9D, 0x68, 0x24, 0x74, 0x08, 0xC3, 0xEC, 0xAC, 0x05, - 0x4D, 0xAF, 0x96, 0x5A, 0x5B, 0xC3, 0x28, 0xB9, 0x2F, 0x87, 0xE1, 0x48, 0xE5, 0x2A, 0xC7, 0x5F, 0xDC, 0x28, 0x4A, 0xB0, 0x2B, 0x67, 0x35, 0x8A, 0x1D, 0xEC, - 0x9F, 0xCC, 0x86, 0x18, 0x27, 0x99, 0x51, 0x04, 0x7F, 0xEA, 0x91, 0x24, 0x42, 0x56, 0x18, 0x4D, 0x24, 0x88, 0xB3, 0x23, 0xCA, 0x16, 0xDE, 0x2C, 0xEF, 0x96, - 0x60, 0xCD, 0x27, 0x41, 0x35, 0xBA, 0x48, 0x10, 0xE7, 0xD1, 0x50, 0x18, 0x65, 0xF0, 0x77, 0xA7, 0x90, 0x6F, 0x7C, 0x31, 0xDD, 0x04, 0x81, 0xEB, 0xF8, 0xB5, - 0x86, 0xA8, 0x2C, 0x3F, 0xFB, 0x6D, 0xE3, 0x07, 0xD6, 0xFC, 0xB6, 0xCD, 0x5D, 0x1A, 0xFC, 0x6C, 0x6D, 0x40, 0x0A, 0x39, 0x25, 0xC1, 0x35, 0x21, 0xF9, 0xE9, - 0x86, 0x63, 0x5C, 0x41, 0xDC, 0x59, 0x2C, 0x6C, 0x99, 0xED, 0xCD, 0x36, 0x9E, 0x8F, 0x79, 0xDB, 0xDA, 0xB5, 0x00, 0xB1, 0xB7, 0xDD, 0x71, 0xD2, 0x07, 0x15, - 0x3B, 0x6A, 0xCF, 0xA6, 0x92, 0xBE, 0xDC, 0x4D, 0x80, 0x32, 0x96, 0x6A, 0xC2, 0x05, 0x76, 0xAC, 0xE0, 0x56, 0x7A, 0x8F, 0x7B, 0xA2, 0xE4, 0x8E, 0x70, 0xC1, - 0xDC, 0x61, 0x21, 0x49, 0xD7, 0xE9, 0x6C, 0x49, 0x66, 0x9F, 0x89, 0xF9, 0xBC, 0x30, 0x0D, 0x2B, 0x4A, 0x0F, 0x3B, 0x96, 0xB3, 0xDE, 0x04, 0x6D, 0x4C, 0xA7, - 0xD6, 0x3B, 0xD1, 0x39, 0x35, 0x48, 0xC1, 0x62, 0xBF, 0x9F, 0x97, 0x54, 0x8C, 0xD6, 0x37, 0xF9, 0x42, 0x88, 0x13, 0x3B, 0xB1, 0x8D, 0x29, 0xB1, 0xF3, 0x48, - 0xE6, 0xCE, 0x90, 0x11, 0x76, 0x79, 0xAC, 0xCA, 0xCE, 0xDD, 0x28, 0x65, 0xD1, 0xE0, 0x35, 0x3C, 0xFA, 0x9B, 0xB2, 0x1C, 0xE9, 0xF1, 0x41, 0xE2, 0x92, 0x4F, - 0x6C, 0x70, 0xB0, 0xAC, 0xD4, 0x1B, 0xDA, 0x5C, 0x03, 0x0D, 0xB9, 0x1D, 0x78, 0x86, 0xB3, 0x20, 0x10, 0x0B, 0x6E, 0x0E, 0xC4, 0x61, 0xFE, 0xC4, 0x40, 0x89, - 0x7D, 0x0C, 0xD5, 0xA3, 0xFC, 0x89, 0x08, 0x0B, 0x08, 0x07, 0x5A, 0x87, 0x1D, 0x54, 0xC8, 0x4A, 0x62, 0xFA, 0xCD, 0x25, 0xA4, 0x27, 0xB5, 0x0E, 0x96, 0x98, - 0x48, 0x3D, 0x27, 0x69, 0x5B, 0xD2, 0x44, 0xBF, 0x30, 0x34, 0x88, 0x29, 0xDF, 0x7C, 0x5E, 0x34, 0x69, 0x9C, 0xCF, 0x07, 0xDD, 0xC1, 0xB0, 0x30, 0x73, 0x92, - 0x72, 0x99, 0x9A, 0x38, 0x4A, 0x42, 0x47, 0x18, 0x56, 0x72, 0x8D, 0xC0, 0x37, 0xAE, 0xA4, 0x49, 0xBB, 0xEB, 0x5B, 0x6C, 0xE6, 0x66, 0x4C, 0x7D, 0x98, 0xBB, - 0x05, 0x92, 0xA9, 0x17, 0x37, 0xF4, 0xBE, 0x94, 0x3E, 0x9A, 0xD2, 0x49, 0x5D, 0x40, 0x88, 0x57, 0x4E, 0x76, 0x42, 0x03, 0xF2, 0x26, 0x31, 0x05, 0x4B, 0x93, - 0xCA, 0x80, 0xDC, 0x04, 0x6D, 0x93, 0xCC, 0x5C, 0x8F, 0x65, 0x83, 0x19, 0x33, 0xC7, 0x94, 0x22, 0x8B, 0x2D, 0xF6, 0x74, 0xE9, 0x5E, 0x11, 0x4F, 0x22, 0xAC, - 0x94, 0x52, 0x87, 0x27, 0x43, 0x53, 0x01, 0x9B, 0x01, 0xC3, 0xA3, 0x54, 0xF6, 0x49, 0x74, 0xFD, 0xDE, 0xAC, 0x9F, 0xEB, 0xC7, 0x0C, 0x5D, 0x07, 0x7C, 0xC6, - 0x98, 0xDA, 0xC4, 0xCC, 0x19, 0xCD, 0x4C, 0x32, 0x37, 0x36, 0x76, 0x50, 0x60, 0x95, 0x46, 0x17, 0xFF, 0xE5, 0xF5, 0x48, 0xC3, 0xD0, 0xBF, 0xB0, 0x2E, 0x74, - 0x4E, 0x03, 0xC7, 0xBF, 0x25, 0x7D, 0x8A, 0x54, 0xC3, 0x58, 0xAF, 0x89, 0x01, 0xAD, 0x66, 0x24, 0x4B, 0x0F, 0x4A, 0x53, 0x0C, 0x79, 0x9C, 0x57, 0x9A, 0xB7, - 0x17, 0x3A, 0x6C, 0x98, 0x3C, 0x96, 0xE2, 0xF9, 0x74, 0xEE, 0xCE, 0x36, 0xB2, 0xAC, 0x46, 0xCD, 0xF1, 0xB6, 0xF1, 0x9D, 0x0A, 0x91, 0xF9, 0xB6, 0x45, 0xDD, - 0x7F, 0xE3, 0x38, 0xA8, 0xD1, 0x76, 0xE0, 0x01, 0x9B, 0x92, 0x8E, 0xD4, 0x04, 0x57, 0x29, 0x86, 0x25, 0x04, 0x9B, 0x55, 0xBB, 0x4A, 0x85, 0x29, 0x49, 0x38, - 0x0D, 0x23, 0xAD, 0x06, 0x31, 0xC4, 0x32, 0x05, 0xAA, 0x7A, 0x72, 0x09, 0x96, 0x9B, 0x95, 0x2C, 0x8F, 0x12, 0x9D, 0xF5, 0x60, 0xD0, 0x67, 0xDD, 0x79, 0x8B, - 0xA9, 0xD1, 0xEA, 0x1E, 0x74, 0x0F, 0x06, 0xF0, 0x9F, 0x64, 0x3E, 0x93, 0x6F, 0x5C, 0x5C, 0xBC, 0x19, 0x96, 0x97, 0x0A, 0xD1, 0xC5, 0x65, 0xA5, 0xAC, 0x60, - 0x5F, 0xA8, 0x0B, 0x75, 0x4F, 0x4A, 0xD6, 0x97, 0x7A, 0x9D, 0x82, 0x71, 0x38, 0xC3, 0xA4, 0xCB, 0x1B, 0xA2, 0xC4, 0x5A, 0xCA, 0xAA, 0x78, 0xE5, 0xFE, 0xDE, - 0x66, 0x49, 0xC8, 0xFF, 0x79, 0x6B, 0x8F, 0x89, 0xE2, 0x2F, 0x6D, 0xE9, 0xA5, 0xE5, 0xE2, 0x3F, 0xB4, 0x6D, 0x74, 0xB3, 0xB5, 0xDE, 0xE6, 0x59, 0x1F, 0x50, - 0xE8, 0xC0, 0x1C, 0xD4, 0x83, 0xC9, 0x68, 0x66, 0x66, 0x18, 0x6B, 0x53, 0x41, 0x06, 0x73, 0xCB, 0xB6, 0xDB, 0xB6, 0x7B, 0x5D, 0x9C, 0x89, 0xE4, 0x5B, 0xF2, - 0x96, 0x9D, 0x16, 0x9B, 0x7C, 0x55, 0x6A, 0x37, 0x10, 0xB9, 0xFE, 0x14, 0xD4, 0xFE, 0xB5, 0x1D, 0x2E, 0xD7, 0x35, 0xAA, 0x0D, 0x14, 0x15, 0xEC, 0xB1, 0x5E, - 0x47, 0x4A, 0xA6, 0xC4, 0x32, 0xC1, 0xFC, 0x69, 0xCF, 0xB5, 0x15, 0xCC, 0x96, 0x15, 0xA6, 0x9E, 0xD1, 0xC4, 0xC8, 0x23, 0xB6, 0x81, 0x19, 0x7C, 0xA5, 0x0A, - 0x45, 0xE1, 0xF4, 0x2D, 0x0E, 0xAE, 0xC2, 0x09, 0x15, 0xDD, 0xE3, 0xA9, 0x2E, 0x75, 0x58, 0xEE, 0x90, 0x1D, 0xAB, 0xE5, 0x66, 0x5D, 0x90, 0xEE, 0x27, 0x3D, - 0x43, 0xDE, 0xA8, 0x44, 0x44, 0x17, 0x41, 0x7B, 0xE1, 0x91, 0x5B, 0x05, 0x66, 0x0E, 0xF8, 0xDF, 0x53, 0x56, 0x3F, 0xAE, 0x5E, 0x2A, 0xA1, 0x03, 0x00, 0xB7, - 0xA2, 0xCE, 0xD0, 0x57, 0xE8, 0x3A, 0xBB, 0x4B, 0x15, 0x7B, 0x0C, 0xAB, 0xA3, 0xBA, 0xAE, 0x10, 0x6E, 0x72, 0x86, 0x50, 0xB9, 0xA9, 0x8A, 0xD1, 0x57, 0x3E, - 0x9F, 0x27, 0xF3, 0x20, 0x63, 0xF1, 0x87, 0xE6, 0xA9, 0x83, 0xFC, 0xE8, 0xD6, 0x8E, 0x55, 0x53, 0x0A, 0x23, 0x47, 0x58, 0xC4, 0xCC, 0xB6, 0x3E, 0x29, 0x66, - 0x8C, 0x9E, 0xA5, 0x91, 0x67, 0xAB, 0x44, 0xA4, 0xCF, 0x54, 0xCD, 0xD0, 0x66, 0xC5, 0x87, 0x7C, 0x50, 0x0F, 0xF9, 0xB9, 0xD5, 0x1F, 0x4B, 0xD7, 0x56, 0x72, - 0x1A, 0xE7, 0x91, 0x96, 0x59, 0x05, 0xDC, 0x1E, 0xB2, 0x32, 0x27, 0xC8, 0xF1, 0x58, 0x24, 0x55, 0x54, 0xBE, 0x57, 0xE6, 0x45, 0x98, 0xED, 0x4A, 0x56, 0xAE, - 0xB1, 0x5B, 0x2B, 0x03, 0xD2, 0x5E, 0x34, 0x57, 0x03, 0x30, 0xCA, 0xF4, 0xA7, 0x62, 0xEE, 0xB1, 0x1A, 0x6B, 0x6F, 0xDC, 0x2D, 0xE8, 0x72, 0x66, 0xBB, 0x7E, - 0xCD, 0x02, 0x58, 0x76, 0xFD, 0x4B, 0x7A, 0x47, 0x69, 0xE8, 0xCE, 0xF5, 0xA9, 0x7C, 0x77, 0x4C, 0xC9, 0xBC, 0xD7, 0x95, 0x46, 0xDA, 0xDC, 0x2A, 0x25, 0xAD, - 0xA0, 0xD1, 0xF5, 0xCB, 0x53, 0x6D, 0x46, 0xE4, 0x61, 0x34, 0x59, 0xA8, 0x53, 0x29, 0x95, 0xE6, 0xEA, 0x61, 0x69, 0x99, 0x26, 0xC9, 0xAD, 0x05, 0xE3, 0x9C, - 0x57, 0x31, 0x79, 0x40, 0xFA, 0x65, 0x45, 0xA9, 0x9D, 0x38, 0x45, 0xEE, 0xB6, 0x86, 0xDE, 0xAE, 0x3D, 0x86, 0x0F, 0x34, 0x59, 0x95, 0xF4, 0x64, 0x2A, 0x92, - 0x4B, 0xAA, 0xD4, 0xB9, 0xC3, 0x5A, 0x2B, 0x8A, 0x0C, 0xE4, 0x80, 0xAD, 0xB6, 0xA3, 0x79, 0x8A, 0x2A, 0xBA, 0x90, 0xD2, 0xE1, 0x6B, 0x4B, 0x7C, 0x19, 0xB0, - 0x9D, 0xB5, 0xBA, 0x72, 0x8F, 0x4B, 0x6D, 0xD4, 0x02, 0xD2, 0xFD, 0x66, 0x8A, 0xE6, 0x81, 0x32, 0xA3, 0x1C, 0x22, 0xC3, 0x21, 0x46, 0x6C, 0xAE, 0x4A, 0xB6, - 0x2A, 0xEB, 0x1C, 0xE1, 0xF9, 0xD9, 0x61, 0x6C, 0x3B, 0xDC, 0xD9, 0x61, 0xB4, 0x73, 0xEF, 0x0C, 0xF7, 0xC4, 0xC5, 0x77, 0xCD, 0xF1, 0x8E, 0x66, 0xB6, 0xE1, - 0xFB, 0xE7, 0x3A, 0xEE, 0xED, 0xD2, 0x93, 0x9B, 0xE8, 0xCE, 0x4C, 0xEB, 0x4A, 0xB3, 0xCC, 0x73, 0xDD, 0x76, 0x17, 0x6E, 0xEA, 0x1E, 0xBD, 0xCF, 0xD4, 0x0C, - 0x03, 0xD9, 0xB9, 0x9E, 0x58, 0x60, 0xD4, 0x29, 0x54, 0x74, 0x49, 0x9F, 0x3C, 0xFB, 0xE2, 0xE4, 0xE8, 0x68, 0xFC, 0xD5, 0x33, 0x67, 0xEA, 0xAF, 0xF9, 0xFF, - 0x3F, 0xB0, 0xF5, 0x58, 0xB6, 0xA9, 0x0F, 0xC6, 0xB6, 0x20, 0x00, 0xDB, 0xF3, 0xCF, 0x0E, 0x29, 0xD2, 0x14, 0x21, 0x87, 0x40, 0x49, 0x06, 0x6D, 0x3C, 0xDF, - 0x91, 0x91, 0x27, 0x9A, 0xF8, 0x30, 0x84, 0x4F, 0x0D, 0x4F, 0xD2, 0x84, 0x36, 0x63, 0xD9, 0x34, 0x8D, 0x25, 0x3A, 0x55, 0xCA, 0xD4, 0xBD, 0x49, 0x73, 0x40, - 0x99, 0xE2, 0x1A, 0xE3, 0xAD, 0x88, 0x99, 0x85, 0x10, 0xC0, 0x28, 0x38, 0xAE, 0xAE, 0x42, 0x1B, 0x69, 0xA3, 0x84, 0x0A, 0xB0, 0xF1, 0xCD, 0xCC, 0xFE, 0x2C, - 0x94, 0xAF, 0x0B, 0xA5, 0x38, 0x6E, 0xC0, 0x62, 0x65, 0x46, 0x57, 0x09, 0x56, 0x39, 0x4C, 0x6C, 0xDD, 0x90, 0x71, 0x01, 0xA2, 0x6D, 0x53, 0xEC, 0xEC, 0x5A, - 0x3E, 0x26, 0x8A, 0x2D, 0xA6, 0x57, 0x01, 0xAC, 0x4F, 0x7E, 0xBE, 0xF8, 0xF6, 0xEF, 0xDA, 0xBB, 0xB7, 0xBF, 0x4B, 0x35, 0x54, 0x44, 0x14, 0x06, 0x69, 0x85, - 0x9E, 0x29, 0x18, 0xD3, 0x87, 0x90, 0x89, 0xCE, 0x35, 0x43, 0x31, 0xE0, 0x70, 0x6F, 0x13, 0x67, 0x11, 0x2C, 0xCF, 0xF5, 0x9E, 0x8E, 0x7B, 0x5A, 0xC4, 0x59, - 0x5F, 0xD7, 0x30, 0x80, 0xD3, 0x83, 0x2B, 0xC3, 0xDE, 0xE0, 0x51, 0x57, 0x85, 0xD7, 0x6D, 0xD3, 0x92, 0x36, 0xE3, 0x91, 0x25, 0x94, 0x71, 0x2C, 0x12, 0x27, - 0xA5, 0xAC, 0x4F, 0x3E, 0x90, 0xE0, 0xEC, 0x90, 0xDD, 0x2A, 0xD0, 0x5A, 0x7E, 0xDF, 0xE0, 0xC9, 0xCC, 0x1C, 0xF2, 0x4C, 0x28, 0x4F, 0xF1, 0x73, 0xCF, 0x58, - 0x11, 0x94, 0x8A, 0x92, 0xE6, 0xE3, 0x5A, 0x0F, 0x21, 0xF5, 0xC9, 0xF7, 0x84, 0x66, 0x44, 0x40, 0x86, 0x92, 0xE2, 0xCF, 0x78, 0x92, 0x9A, 0xE8, 0x3F, 0xB4, - 0x67, 0xBE, 0x28, 0xD5, 0x36, 0x98, 0x99, 0x2B, 0xC8, 0xFD, 0x69, 0xBB, 0xAD, 0x0D, 0xDE, 0xBD, 0xD7, 0xDA, 0x6D, 0x85, 0xC6, 0xEE, 0x9A, 0xBA, 0x13, 0xD7, - 0x7F, 0xEF, 0x48, 0x9F, 0xFC, 0xF3, 0xE7, 0x37, 0x2F, 0x5B, 0xFD, 0xEE, 0xF0, 0xF8, 0xA6, 0x37, 0x1A, 0x0F, 0xF7, 0xCF, 0x0E, 0x59, 0x93, 0xF2, 0xB8, 0xC6, - 0xFA, 0xE4, 0x3D, 0x12, 0xD2, 0x3A, 0x1E, 0x0F, 0xEB, 0xE2, 0x1A, 0x21, 0xAE, 0xB7, 0xAF, 0x5B, 0x47, 0xFD, 0xEE, 0x4D, 0xAF, 0x7F, 0xDC, 0xAD, 0x81, 0x6A, - 0xA8, 0x4F, 0xBE, 0x06, 0x4C, 0xBD, 0x13, 0x44, 0xD5, 0x2D, 0x87, 0x0A, 0x45, 0xDB, 0xAF, 0x28, 0xDA, 0x81, 0x3E, 0xF9, 0x11, 0x45, 0x0B, 0x39, 0x37, 0xF2, - 0xD0, 0xAD, 0xC3, 0x43, 0x1F, 0x5C, 0x86, 0xE2, 0x02, 0x51, 0x00, 0x13, 0xFD, 0x3A, 0xA2, 0xED, 0xE9, 0x13, 0x14, 0x07, 0x62, 0x02, 0xE9, 0xD6, 0x40, 0x04, - 0xB1, 0x83, 0xD2, 0x04, 0xE4, 0xDC, 0x1C, 0x8D, 0x8F, 0xAB, 0x63, 0x3A, 0x01, 0xEE, 0x3E, 0x02, 0xA6, 0x63, 0x10, 0xD4, 0xB8, 0x8E, 0x9C, 0x8E, 0xF5, 0x09, - 0xE2, 0x19, 0x0F, 0xBB, 0x37, 0xC3, 0x3A, 0x36, 0x03, 0x5E, 0xF1, 0x16, 0x11, 0x01, 0x92, 0x9B, 0x41, 0x1D, 0x19, 0x81, 0x4B, 0x5C, 0x7C, 0xF3, 0x75, 0x6B, - 0x08, 0x8C, 0xF5, 0x4F, 0xC6, 0xD5, 0xF1, 0x80, 0x3B, 0xFC, 0x13, 0x09, 0x02, 0x62, 0x6E, 0xFA, 0xC3, 0x1A, 0x04, 0x81, 0x33, 0x00, 0x3C, 0xE2, 0xA8, 0x8C, - 0x02, 0xEC, 0xFA, 0x2D, 0x25, 0x06, 0x11, 0xF5, 0x8E, 0x6A, 0x70, 0x05, 0x56, 0xFD, 0x4F, 0x14, 0x0F, 0x20, 0xB9, 0xE9, 0x0D, 0xEB, 0xD8, 0x34, 0x20, 0xA2, - 0x24, 0x81, 0xAF, 0xA1, 0xAB, 0x55, 0xC7, 0x04, 0x36, 0x7D, 0x32, 0xBE, 0x39, 0x19, 0xAB, 0x21, 0xC0, 0xE1, 0x07, 0x43, 0x79, 0xDE, 0x00, 0x95, 0x3F, 0x7E, - 0xE5, 0x8D, 0x4D, 0xFF, 0xD9, 0xC0, 0x94, 0x33, 0xB8, 0x2D, 0x3D, 0x32, 0x71, 0x38, 0x90, 0x09, 0x3B, 0x50, 0x1B, 0x94, 0x62, 0x94, 0x84, 0xBB, 0x9F, 0xF4, - 0xC9, 0x50, 0x61, 0xF0, 0x4F, 0x64, 0x87, 0x14, 0x36, 0x41, 0x3F, 0xCD, 0x48, 0xD0, 0xF2, 0x30, 0x17, 0x01, 0x97, 0x18, 0xE8, 0xB1, 0x08, 0x52, 0x69, 0xD4, - 0x93, 0xD0, 0x6A, 0xDC, 0xE8, 0x93, 0xF1, 0xA0, 0x30, 0x5B, 0xA8, 0xAE, 0x8C, 0x29, 0x2D, 0x6E, 0x38, 0xC4, 0xF7, 0x4B, 0xEB, 0x23, 0x02, 0xD5, 0x27, 0xAF, - 0xC2, 0xE3, 0x3A, 0x5A, 0x69, 0x17, 0x71, 0x4A, 0x61, 0x33, 0xD4, 0x12, 0x23, 0x87, 0x69, 0xA6, 0x3D, 0xE0, 0xAA, 0x89, 0x34, 0x73, 0xBF, 0x8A, 0xD9, 0xA5, - 0x5E, 0x70, 0x6E, 0xE3, 0x19, 0x7E, 0x50, 0x5A, 0x2B, 0x02, 0x10, 0x22, 0x34, 0x3F, 0x7A, 0x30, 0x8D, 0x84, 0xA4, 0xFC, 0x05, 0xF4, 0xE1, 0x1B, 0xC1, 0x86, - 0xED, 0x33, 0x2B, 0xAD, 0x91, 0x08, 0x14, 0xF2, 0x81, 0xF0, 0xB8, 0x96, 0x56, 0xEA, 0x84, 0xAF, 0x18, 0x39, 0x5C, 0x2F, 0x22, 0x84, 0x0D, 0x77, 0xA4, 0x97, - 0x22, 0x6A, 0x6B, 0xE9, 0x65, 0x69, 0x78, 0xEB, 0x4A, 0xE1, 0x2B, 0x84, 0x04, 0xAD, 0x88, 0xC3, 0x07, 0x73, 0x95, 0x88, 0x98, 0xBF, 0x80, 0xAF, 0x98, 0xC4, - 0x71, 0x2D, 0xBF, 0xFC, 0xD4, 0x93, 0xC3, 0xE9, 0x93, 0xD7, 0xA4, 0xFD, 0x1D, 0x1E, 0xD5, 0x51, 0xC7, 0xCB, 0x4D, 0xE0, 0xD6, 0x50, 0x88, 0xA0, 0x85, 0xA9, - 0xA3, 0xCB, 0xB5, 0x71, 0xBC, 0x23, 0x6D, 0x1C, 0xEF, 0x50, 0x1B, 0x06, 0xF9, 0x64, 0x93, 0x2B, 0x62, 0x97, 0x56, 0x87, 0x00, 0xD4, 0x27, 0x97, 0x37, 0x6B, - 0xD7, 0xC7, 0xA7, 0x77, 0xBE, 0xC5, 0xF3, 0x5A, 0x4E, 0x32, 0xAA, 0xA1, 0x93, 0x90, 0x20, 0xEE, 0x23, 0x23, 0xAE, 0x95, 0xD1, 0x8E, 0xB4, 0x52, 0x44, 0x6B, - 0x1D, 0xAD, 0x2C, 0x0C, 0xCB, 0x99, 0x11, 0xCB, 0xC6, 0x27, 0x09, 0xCA, 0x2A, 0x26, 0x06, 0xAB, 0x4F, 0xDE, 0x44, 0x27, 0x75, 0x14, 0xD3, 0xAD, 0xA1, 0x97, - 0x38, 0x3D, 0x49, 0x7F, 0x19, 0xC1, 0xAC, 0x7C, 0x47, 0xBA, 0xE9, 0xF5, 0x76, 0x39, 0xAA, 0xAC, 0xC9, 0xCC, 0x32, 0xEC, 0x4F, 0x64, 0x3E, 0x87, 0x69, 0x50, - 0xF9, 0xA1, 0x25, 0x01, 0x0E, 0xE3, 0x0B, 0x3B, 0xD7, 0x2E, 0xE9, 0x79, 0xE9, 0x62, 0x5A, 0x0A, 0x5D, 0xF5, 0x8A, 0x5A, 0x7A, 0x4E, 0xC8, 0x97, 0x95, 0x09, - 0xAD, 0x61, 0xB2, 0x23, 0x7D, 0xF2, 0x9D, 0x1B, 0xD2, 0x59, 0x7D, 0xDA, 0xFA, 0x1D, 0x59, 0xD0, 0x55, 0xDB, 0x3A, 0x73, 0xE8, 0x37, 0x9E, 0x71, 0x4B, 0x5F, - 0x0B, 0x50, 0x67, 0x4A, 0xFF, 0x3D, 0x31, 0xB5, 0x1F, 0x2C, 0xA7, 0x3A, 0x33, 0x43, 0x24, 0x84, 0x10, 0xA7, 0x1E, 0x96, 0x11, 0x4C, 0x91, 0xE0, 0xA0, 0x1E, - 0x92, 0x31, 0x16, 0x98, 0xD7, 0x96, 0xF1, 0x18, 0x26, 0xF1, 0xC6, 0xF5, 0xB4, 0xFC, 0x80, 0x72, 0x3D, 0x85, 0x71, 0xF9, 0xA7, 0x57, 0xDA, 0x25, 0xDD, 0x67, - 0x5C, 0x3A, 0x5C, 0xB1, 0x2D, 0x50, 0x2A, 0x86, 0x1E, 0xAD, 0x23, 0x60, 0x9F, 0x5B, 0x0B, 0x3C, 0x72, 0x07, 0x52, 0x5D, 0xE4, 0x91, 0xB0, 0x27, 0x08, 0xA4, - 0x3B, 0x46, 0xF4, 0x18, 0xB7, 0x6A, 0x3C, 0xEE, 0x30, 0x15, 0x9B, 0x5D, 0x97, 0x4F, 0xC3, 0x66, 0xD7, 0xA0, 0x26, 0xF3, 0x0A, 0xB7, 0xA0, 0x9B, 0x1A, 0xE8, - 0xAB, 0x11, 0x45, 0x61, 0xAF, 0x0F, 0xA3, 0x28, 0xCA, 0xEF, 0x43, 0x2B, 0x0A, 0xAC, 0xE5, 0x13, 0x8E, 0xA3, 0x55, 0x9C, 0x8A, 0x02, 0xEA, 0x93, 0x77, 0x86, - 0xB3, 0x81, 0x41, 0xA6, 0x29, 0x85, 0x85, 0x1D, 0x3F, 0x98, 0x7B, 0x71, 0xBE, 0x1F, 0x5A, 0x75, 0x40, 0xC8, 0xCA, 0x35, 0xCB, 0x4F, 0x77, 0x38, 0x1C, 0x0B, - 0x89, 0xEF, 0xE0, 0xA8, 0x74, 0x62, 0x20, 0x30, 0xEC, 0x38, 0x23, 0x60, 0x53, 0xA9, 0xEA, 0xC9, 0xC0, 0x87, 0x8D, 0xE3, 0xDC, 0xD6, 0xC9, 0x04, 0x2E, 0x6C, - 0x77, 0x63, 0x56, 0xC7, 0x00, 0x69, 0xC0, 0x3F, 0xE6, 0x73, 0x6B, 0x56, 0x3D, 0x91, 0x80, 0x24, 0xE0, 0xAD, 0xBB, 0x52, 0x84, 0xDF, 0xF1, 0xC0, 0x4B, 0x66, - 0x15, 0x66, 0x72, 0x33, 0xD0, 0xE2, 0xE5, 0x45, 0xA3, 0x03, 0x2F, 0xF4, 0xF9, 0x40, 0x91, 0x01, 0xB9, 0x7D, 0xE8, 0xA0, 0x00, 0x44, 0x7C, 0xA2, 0xC6, 0x53, - 0x45, 0x59, 0x0C, 0x32, 0x8C, 0xE8, 0x62, 0xFA, 0xFD, 0x50, 0xF3, 0xBB, 0x88, 0xA2, 0xE4, 0xEC, 0xAE, 0x37, 0x1A, 0x8C, 0xC3, 0xE9, 0xDD, 0xA0, 0x7F, 0xBF, - 0x13, 0x3C, 0x44, 0xBE, 0x5B, 0xFD, 0xF4, 0xAB, 0xA8, 0x06, 0xA2, 0xD1, 0x77, 0xB8, 0xCE, 0x50, 0x22, 0x60, 0xD7, 0x77, 0xA4, 0xFE, 0xC3, 0x79, 0x52, 0xFF, - 0x11, 0xB8, 0xD2, 0xA2, 0x42, 0xC4, 0x5B, 0x60, 0xC4, 0x7B, 0x73, 0xD1, 0x8C, 0x86, 0x16, 0x0F, 0x16, 0xEA, 0x16, 0x0F, 0x1A, 0xEA, 0x34, 0xBE, 0x43, 0x4D, - 0x48, 0xA1, 0x62, 0x06, 0xCB, 0x01, 0x59, 0x2D, 0xAB, 0x4E, 0x90, 0xEB, 0xDD, 0xD4, 0x89, 0x72, 0x82, 0x8C, 0x64, 0x90, 0x1B, 0x47, 0xAB, 0x22, 0xA3, 0xFB, - 0x5D, 0xD6, 0x1D, 0x16, 0x51, 0x5B, 0xC7, 0x69, 0x3C, 0xE3, 0xFA, 0xD3, 0x62, 0x65, 0x94, 0x56, 0x06, 0x87, 0x03, 0x5D, 0xBC, 0x7B, 0xD9, 0x64, 0xBA, 0x20, - 0xFA, 0x7D, 0x18, 0x3F, 0x0A, 0xB9, 0x7E, 0xE8, 0x58, 0x67, 0x13, 0xA7, 0x7C, 0xB0, 0x43, 0x20, 0x7D, 0xF2, 0x2D, 0x71, 0x7C, 0xED, 0xC2, 0xF5, 0xF8, 0xBB, - 0x18, 0x1B, 0xD1, 0x1A, 0xED, 0xF9, 0x61, 0x54, 0xC6, 0x98, 0x7E, 0x68, 0x7D, 0x2D, 0x57, 0x96, 0xE7, 0xB9, 0x5E, 0x69, 0x95, 0x71, 0x38, 0x98, 0x56, 0xB4, - 0xDF, 0xD1, 0xA3, 0x46, 0xD4, 0x25, 0x7A, 0x7D, 0x18, 0x8D, 0x85, 0x3C, 0x3F, 0xB4, 0xD2, 0xAE, 0xE6, 0xB6, 0xB5, 0x2E, 0xAD, 0x32, 0x0A, 0xA5, 0x4F, 0x3E, - 0xB6, 0xBF, 0x86, 0xBF, 0x8D, 0xA8, 0x8B, 0xF5, 0xF8, 0x30, 0xCA, 0xE2, 0xDC, 0x3E, 0xB4, 0xAA, 0xA6, 0xEB, 0xF2, 0xE1, 0x10, 0x60, 0xF4, 0xC9, 0xAB, 0xF7, - 0xCD, 0xE4, 0x7E, 0xD8, 0x99, 0xA2, 0x86, 0x6A, 0xE9, 0x83, 0x32, 0xF5, 0xD0, 0xDA, 0xB8, 0xAE, 0xA0, 0x8D, 0x6B, 0x24, 0xFC, 0xA7, 0x86, 0xB4, 0x71, 0xAD, - 0xAE, 0x8D, 0x7B, 0xF6, 0x97, 0xEB, 0xC7, 0xA0, 0x1F, 0xFA, 0xB0, 0xDF, 0xD4, 0x28, 0x3F, 0x1C, 0x09, 0x40, 0xDC, 0x34, 0x06, 0x47, 0xDA, 0x2B, 0xA3, 0x99, - 0x01, 0x29, 0xEC, 0xB7, 0x09, 0x17, 0x8A, 0x98, 0x7C, 0x68, 0x3D, 0xD9, 0xC4, 0xAC, 0x90, 0xE4, 0x99, 0x9F, 0xF0, 0xC9, 0x39, 0x7C, 0xA2, 0xFC, 0x16, 0xB2, - 0xBD, 0xCB, 0xD7, 0xDA, 0x37, 0xE2, 0xF4, 0xA1, 0x0A, 0x43, 0x49, 0x9A, 0x92, 0xF3, 0xA6, 0xFE, 0x68, 0x57, 0xDB, 0x32, 0x00, 0xF3, 0x0E, 0x75, 0x33, 0x37, - 0x66, 0xE4, 0x93, 0x49, 0x82, 0x2A, 0xEB, 0xFE, 0x31, 0x58, 0x7D, 0xF2, 0x35, 0x9C, 0x68, 0xAF, 0xE9, 0x49, 0x53, 0xE9, 0x78, 0xBC, 0xFF, 0x26, 0x3C, 0x2A, - 0xC1, 0xEF, 0x43, 0x3B, 0x15, 0x25, 0x06, 0x26, 0x3F, 0xEE, 0xC2, 0xA9, 0xF4, 0xDC, 0x53, 0x02, 0x9C, 0xAB, 0xEF, 0x7B, 0x76, 0xDE, 0xAC, 0x02, 0x23, 0x22, - 0x1A, 0xD3, 0x61, 0x8C, 0xEF, 0x26, 0xD4, 0x18, 0x7F, 0xF8, 0x91, 0xBF, 0x36, 0xB8, 0x48, 0x53, 0xFC, 0x21, 0x3C, 0xBA, 0xDD, 0x88, 0x04, 0x6D, 0x3F, 0xB0, - 0x6C, 0x5B, 0x9F, 0xBC, 0x21, 0x81, 0xF6, 0x01, 0x0F, 0x15, 0x9F, 0xBA, 0x8B, 0x61, 0x11, 0xCF, 0xDC, 0x06, 0x1E, 0x31, 0x56, 0xFA, 0xE4, 0x03, 0xBE, 0x50, - 0x19, 0x70, 0xE1, 0x59, 0x79, 0x64, 0x54, 0x88, 0xC4, 0xF1, 0x5C, 0x20, 0x2A, 0x54, 0x12, 0x7F, 0x51, 0xA3, 0xAE, 0x89, 0xA3, 0xD8, 0xB5, 0xC9, 0x25, 0x6D, - 0xAC, 0xA1, 0x95, 0x15, 0x77, 0x17, 0x7F, 0x1C, 0x30, 0xDF, 0x39, 0xE8, 0x03, 0xC0, 0xF8, 0x44, 0x6F, 0xF2, 0x7D, 0xEB, 0xA0, 0x56, 0xF6, 0x7C, 0xFF, 0xE4, - 0xCC, 0x5F, 0x1B, 0x8E, 0x68, 0x46, 0x1F, 0x7E, 0xBF, 0xE6, 0x4F, 0x33, 0x4F, 0x5D, 0xDB, 0xFC, 0x2A, 0xB6, 0xF0, 0xFF, 0x21, 0x7C, 0x2C, 0x17, 0x41, 0xC0, - 0x2E, 0x04, 0x86, 0x02, 0xE5, 0x2E, 0x3D, 0x81, 0x9E, 0x3D, 0x41, 0x8D, 0x6F, 0xEB, 0xCA, 0xD1, 0x6E, 0xC6, 0x93, 0xC4, 0x1E, 0x59, 0x84, 0x92, 0x94, 0x3D, - 0x61, 0x2E, 0x7D, 0xAE, 0xF8, 0x7B, 0xB2, 0xB0, 0x7C, 0xA0, 0x51, 0x03, 0xBB, 0x38, 0xA4, 0xCF, 0x62, 0x32, 0x5B, 0x56, 0x7B, 0xCE, 0x37, 0xDE, 0x25, 0x7F, - 0x4D, 0x81, 0xF4, 0xF1, 0xED, 0x52, 0xA9, 0x63, 0xFA, 0x59, 0xEB, 0x24, 0xC6, 0x22, 0xAB, 0x7F, 0xDA, 0x6E, 0x2F, 0x87, 0xF8, 0x54, 0xA9, 0x26, 0x58, 0x3B, - 0x3B, 0x5C, 0x0E, 0x8B, 0x9E, 0xDA, 0x2B, 0x7C, 0x24, 0x18, 0x38, 0xAD, 0xFC, 0x44, 0x30, 0x4A, 0x69, 0x02, 0xD4, 0x1C, 0x68, 0xEF, 0x0C, 0xFF, 0xF3, 0x81, - 0xF6, 0x11, 0x87, 0xF8, 0x06, 0x1F, 0x0C, 0x46, 0xDA, 0x0D, 0xD3, 0xF4, 0x32, 0x1F, 0x0E, 0x1E, 0x26, 0x1E, 0x0E, 0x1E, 0x8B, 0x87, 0x83, 0xA3, 0x95, 0xAA, - 0xEE, 0xCD, 0xA0, 0xDB, 0x3D, 0x56, 0x61, 0x5D, 0xF1, 0x01, 0xE1, 0x7B, 0xE1, 0x69, 0x05, 0xD2, 0x54, 0xE4, 0x69, 0x28, 0x78, 0x8A, 0x6D, 0xD8, 0xBF, 0x99, - 0xCF, 0x1F, 0x1B, 0x47, 0x7C, 0xC9, 0xB0, 0x3A, 0x4B, 0xDD, 0x7E, 0xD3, 0x4F, 0x71, 0x53, 0xE3, 0xBE, 0xAF, 0x87, 0xB8, 0x69, 0x93, 0x74, 0x34, 0x1C, 0xE5, - 0x06, 0x43, 0x0A, 0xC2, 0x9C, 0xFE, 0xCD, 0x7D, 0x3A, 0xFD, 0xA2, 0x86, 0xD3, 0x2F, 0xB6, 0x9C, 0xBE, 0x41, 0x6F, 0x17, 0x84, 0xFF, 0xD5, 0x3C, 0x5E, 0xF0, - 0x55, 0xC2, 0xEB, 0xA5, 0x7C, 0x75, 0xBB, 0xF7, 0xEA, 0xF7, 0x85, 0x4E, 0x12, 0x1A, 0xC3, 0x9B, 0xFB, 0x74, 0x92, 0x0C, 0xD3, 0xAD, 0x64, 0xA7, 0x3C, 0xEC, - 0x4C, 0x9A, 0x19, 0x97, 0x68, 0x36, 0x15, 0x57, 0x28, 0xEF, 0x1D, 0x1F, 0xD7, 0x1D, 0x0C, 0x79, 0xEA, 0x74, 0x1F, 0xEA, 0x51, 0x7F, 0x61, 0x44, 0x66, 0x93, - 0xFB, 0x49, 0xCC, 0xD6, 0xB1, 0x14, 0x57, 0x39, 0x31, 0x7B, 0xFF, 0xED, 0xB7, 0xE5, 0x72, 0xB1, 0x78, 0x2F, 0x8F, 0x24, 0x17, 0xCB, 0x2D, 0x53, 0xDF, 0xAE, - 0xE1, 0x06, 0x52, 0x5D, 0xC9, 0x74, 0x23, 0x70, 0x7D, 0xF2, 0x8A, 0x1E, 0x6B, 0x31, 0x89, 0x95, 0x32, 0x5E, 0xE5, 0x59, 0x27, 0x05, 0x8C, 0xD5, 0xB1, 0x23, - 0x12, 0xD2, 0xBA, 0x51, 0xC4, 0x95, 0x53, 0xBB, 0x8E, 0xB1, 0xA7, 0xCE, 0x54, 0x6D, 0x9F, 0xA0, 0x4D, 0x8A, 0x52, 0xE1, 0xD5, 0xC6, 0xAE, 0xAC, 0x36, 0x0E, - 0xAB, 0x4F, 0xDE, 0xC1, 0x64, 0xDC, 0x5A, 0xDB, 0x16, 0xCC, 0x3C, 0x5A, 0x5D, 0xAD, 0xAD, 0x0D, 0x7A, 0xFB, 0x0D, 0x8E, 0x91, 0x82, 0x8C, 0x92, 0x6F, 0xCB, - 0xE9, 0x45, 0x0F, 0xB3, 0x0D, 0xEE, 0xE9, 0x75, 0x39, 0x75, 0x15, 0xE2, 0xB9, 0x6E, 0x50, 0x59, 0x1B, 0x02, 0x18, 0x12, 0x15, 0x38, 0xD2, 0x22, 0x9D, 0xA8, - 0xAB, 0x22, 0xB6, 0xB5, 0x36, 0xC2, 0xA6, 0xA6, 0x0E, 0xA5, 0x8D, 0xB4, 0xB8, 0x3F, 0x45, 0x75, 0x07, 0xAA, 0x04, 0x6B, 0x4F, 0x9F, 0xF4, 0x4B, 0x60, 0x28, - 0xDE, 0x87, 0xCA, 0x5A, 0xD5, 0x77, 0x22, 0xFF, 0xB6, 0x7A, 0xEC, 0xE3, 0xB0, 0x90, 0x76, 0xDF, 0x42, 0xAA, 0xBB, 0xD2, 0x5E, 0x43, 0x5F, 0xD4, 0x89, 0x7A, - 0xA3, 0x26, 0x9D, 0x48, 0x90, 0x51, 0xDD, 0x89, 0x7A, 0x8F, 0xC3, 0x87, 0x50, 0x1F, 0x6B, 0x8F, 0x54, 0xD6, 0x07, 0x87, 0xD5, 0x27, 0xEF, 0x3D, 0x82, 0xCA, - 0xA8, 0xE4, 0x3D, 0x21, 0x92, 0x6A, 0xCE, 0x73, 0x0F, 0x8E, 0xD2, 0xEB, 0x8C, 0xEA, 0xE1, 0xE8, 0x97, 0x73, 0x36, 0x09, 0x86, 0x81, 0x3C, 0x08, 0x0C, 0x1E, - 0xA7, 0x0B, 0x13, 0xDB, 0x1C, 0x55, 0x77, 0x62, 0x01, 0x8D, 0xB3, 0x67, 0x38, 0xAC, 0x6C, 0x38, 0x31, 0x44, 0x8F, 0x2A, 0xEE, 0xD6, 0xC4, 0x70, 0x1F, 0xC6, - 0x34, 0xE9, 0x97, 0x32, 0xE9, 0x66, 0x4C, 0x67, 0x8D, 0x2F, 0x17, 0x24, 0x6A, 0x7B, 0x3E, 0x29, 0xB2, 0x78, 0xA4, 0x61, 0xB0, 0x10, 0x69, 0xE8, 0xEB, 0x05, - 0xE9, 0x5E, 0xF7, 0x46, 0x73, 0x5E, 0x41, 0xC0, 0xF6, 0x32, 0x4B, 0xF9, 0xAD, 0x01, 0x31, 0xE6, 0x64, 0x29, 0x70, 0xC8, 0xEB, 0x63, 0xCB, 0x7F, 0x29, 0x61, - 0x95, 0xC7, 0x0A, 0x0E, 0xCC, 0x55, 0x18, 0x0E, 0xDD, 0xCD, 0xE6, 0xBF, 0x21, 0x15, 0x35, 0xC6, 0xEE, 0x06, 0x13, 0xE0, 0xD8, 0xB2, 0x11, 0x55, 0x00, 0x0B, - 0x9A, 0x01, 0x9B, 0xF1, 0x95, 0x58, 0x09, 0xCA, 0x6C, 0x72, 0x3F, 0xF3, 0xFC, 0x6B, 0xCB, 0x29, 0x3F, 0xCF, 0xFF, 0xC9, 0x72, 0x4C, 0xF7, 0xBA, 0xDC, 0x54, - 0x3F, 0xDE, 0xD1, 0x9F, 0x60, 0xAA, 0x4F, 0x07, 0x4B, 0x5C, 0x2C, 0x6C, 0x7B, 0x44, 0xED, 0xA5, 0x33, 0x69, 0x21, 0x33, 0xE8, 0x1B, 0x5C, 0x6A, 0x03, 0x14, - 0xBE, 0x46, 0x97, 0x1E, 0x77, 0xED, 0x2F, 0x3F, 0x9F, 0xC6, 0x93, 0x5D, 0x4E, 0x81, 0x9A, 0xC3, 0x0C, 0x25, 0x85, 0xC7, 0x07, 0xAF, 0xA5, 0xFE, 0xB2, 0xCD, - 0xCF, 0xED, 0x83, 0xF3, 0x73, 0x1F, 0x01, 0x99, 0x38, 0x66, 0x65, 0xCB, 0x42, 0xD8, 0xC8, 0xAE, 0x2E, 0x1D, 0xB3, 0x51, 0xAB, 0x62, 0xBD, 0x57, 0xD6, 0x41, - 0xBF, 0x7B, 0x74, 0xF2, 0xB8, 0xCC, 0x0A, 0x19, 0xAA, 0x61, 0x54, 0xBD, 0xD1, 0xF0, 0xE8, 0xF1, 0xD8, 0x95, 0x3B, 0x9F, 0xB3, 0x15, 0xAE, 0x6A, 0xA6, 0xC5, - 0xC1, 0x6F, 0xE8, 0xA3, 0xB4, 0x3E, 0x69, 0x36, 0x5E, 0x85, 0x9D, 0xAB, 0xE9, 0x62, 0x20, 0xD1, 0xC5, 0xF8, 0x71, 0x99, 0x16, 0xE7, 0x48, 0xD5, 0xBA, 0x24, - 0x1C, 0xDD, 0x13, 0x43, 0xF7, 0x61, 0x5A, 0x81, 0x1B, 0x18, 0x76, 0x65, 0xCB, 0x62, 0xD0, 0x60, 0x58, 0x3F, 0xE0, 0x81, 0xF6, 0x01, 0xF8, 0x6C, 0xD4, 0xB8, - 0x44, 0xFF, 0xD5, 0x03, 0xD7, 0xA0, 0xFB, 0xC8, 0xC6, 0x43, 0xC6, 0x52, 0xAD, 0xD0, 0x35, 0x1E, 0x3E, 0x1E, 0xFB, 0x72, 0x37, 0x01, 0x5E, 0xAD, 0x1C, 0xBA, - 0x18, 0x38, 0x86, 0x2E, 0x7A, 0xD4, 0xBC, 0x89, 0x85, 0x14, 0xD4, 0x18, 0x1C, 0x87, 0x0F, 0xBF, 0x7E, 0xFD, 0x8B, 0x84, 0xA7, 0x5A, 0x46, 0x36, 0x78, 0x2C, - 0x41, 0x6C, 0x66, 0x28, 0xBF, 0x88, 0x8D, 0x22, 0x8B, 0x67, 0xF3, 0x0C, 0x16, 0xE6, 0x70, 0xEC, 0xA0, 0xD1, 0x0A, 0x86, 0xE8, 0xFC, 0xDE, 0x97, 0xEC, 0x42, - 0xAE, 0x1E, 0x53, 0xBD, 0x62, 0x6A, 0x39, 0x4E, 0x55, 0x35, 0x71, 0x58, 0x7D, 0xF2, 0x8A, 0x1D, 0x34, 0xBB, 0xB8, 0xCA, 0x3B, 0xBF, 0xFF, 0x95, 0x55, 0xC1, - 0x55, 0xD3, 0x6A, 0x4A, 0x15, 0x31, 0xBC, 0xF0, 0x4B, 0x11, 0x3A, 0xDF, 0xAD, 0x18, 0x7D, 0x39, 0xE2, 0xF1, 0x94, 0x34, 0x16, 0xC6, 0x0A, 0x9F, 0x30, 0x2E, - 0x5B, 0xD4, 0x78, 0x83, 0x60, 0xE5, 0x6A, 0x1A, 0xC9, 0x9E, 0x1E, 0x77, 0x55, 0x63, 0x92, 0x7C, 0xB5, 0x24, 0x10, 0xDE, 0x9E, 0x5A, 0x86, 0x8F, 0x4F, 0xE3, - 0xC3, 0xB1, 0xF6, 0x0A, 0x8E, 0xB5, 0xF7, 0xF6, 0x26, 0x7C, 0x37, 0xAE, 0xCC, 0x21, 0xE2, 0x3B, 0x9B, 0x22, 0x0C, 0x59, 0xDB, 0xD7, 0xE9, 0x86, 0x2E, 0xFE, - 0x14, 0x16, 0x1C, 0xE3, 0x3E, 0xA6, 0xD1, 0xF0, 0xB8, 0xAB, 0x6B, 0x2C, 0x2B, 0xE6, 0xCF, 0x90, 0xF8, 0x9F, 0xE9, 0x06, 0xA7, 0x5E, 0x48, 0xA0, 0xCC, 0x01, - 0xE2, 0xF4, 0x86, 0x04, 0x52, 0xFB, 0xAD, 0xB3, 0xEF, 0x68, 0x5B, 0x22, 0x3D, 0x21, 0x8E, 0xAE, 0xD4, 0x10, 0x12, 0x2F, 0xC3, 0x64, 0xED, 0x55, 0x9E, 0x86, - 0x91, 0x0B, 0xA2, 0x27, 0x15, 0x04, 0xEE, 0xF3, 0xBA, 0x5F, 0x9E, 0xFA, 0x82, 0xA7, 0x9E, 0x1A, 0x4F, 0xFD, 0x1A, 0x3C, 0xF5, 0x1B, 0xE2, 0x69, 0x20, 0x78, - 0xEA, 0xAB, 0xF1, 0x34, 0xA8, 0xC1, 0xD3, 0xA0, 0x21, 0x9E, 0x86, 0x82, 0xA7, 0x81, 0x1A, 0x4F, 0xC3, 0x1A, 0x3C, 0x0D, 0x1B, 0xE2, 0x69, 0x24, 0x78, 0x1A, - 0xAA, 0xF1, 0x34, 0xAA, 0xC1, 0xD3, 0xA8, 0x21, 0x9E, 0xC6, 0x82, 0xA7, 0x91, 0x1A, 0x4F, 0xE3, 0x1A, 0x3C, 0x8D, 0x1B, 0xE2, 0xE9, 0x48, 0xF0, 0x34, 0x56, - 0xE3, 0xE9, 0xA8, 0x06, 0x4F, 0x47, 0x0D, 0xF1, 0x74, 0x2C, 0x78, 0x3A, 0x52, 0xE3, 0xE9, 0xB8, 0x06, 0x4F, 0xC7, 0x0D, 0xF1, 0x74, 0x22, 0x78, 0x3A, 0x56, - 0xE3, 0xE9, 0xA4, 0x06, 0x4F, 0x27, 0x0D, 0xF1, 0x84, 0x8B, 0x72, 0x8C, 0xA9, 0x13, 0xC5, 0x41, 0xB7, 0x5B, 0x83, 0x2B, 0xA3, 0x29, 0xAE, 0xC2, 0x54, 0xA2, - 0xA7, 0x9A, 0x4B, 0xD4, 0x49, 0x26, 0xA6, 0x4D, 0xB1, 0x15, 0x65, 0x13, 0x8A, 0xE9, 0x44, 0xAF, 0x4E, 0x3E, 0x31, 0x6B, 0x8A, 0xAD, 0x30, 0xA1, 0xE8, 0x29, - 0x66, 0x14, 0xBD, 0x3A, 0x29, 0x85, 0xD9, 0x14, 0x5B, 0x61, 0x4E, 0xD1, 0x53, 0x4C, 0x2A, 0x7A, 0x75, 0xB2, 0x0A, 0xD2, 0x14, 0x5B, 0x61, 0x5A, 0xD1, 0x53, - 0xCC, 0x2B, 0x7A, 0x75, 0x12, 0x8B, 0x79, 0x53, 0x6C, 0x85, 0x99, 0x45, 0x4F, 0x31, 0xB5, 0xE8, 0xD5, 0xC8, 0x2D, 0x4E, 0xE4, 0x13, 0xB1, 0x7B, 0x65, 0x8B, - 0x04, 0x7C, 0x8A, 0x1C, 0x4D, 0xDA, 0x94, 0x1E, 0x3D, 0xE1, 0x40, 0xF8, 0x6C, 0x14, 0x13, 0xC8, 0x85, 0xEB, 0xCC, 0xAD, 0x45, 0x58, 0x64, 0x78, 0x34, 0x4F, - 0x49, 0xF8, 0xB1, 0xB7, 0xF2, 0x2A, 0x17, 0x1A, 0x3E, 0xBC, 0xBE, 0x2C, 0x57, 0x66, 0x88, 0xF7, 0xF2, 0x27, 0x2A, 0x32, 0x00, 0xD9, 0xFD, 0xF8, 0x27, 0x02, - 0x94, 0xEA, 0x0A, 0x14, 0xA8, 0x4C, 0x45, 0x61, 0x14, 0xAF, 0x28, 0x8C, 0x95, 0x2B, 0x0A, 0x8C, 0xB8, 0xDD, 0xD4, 0x12, 0x00, 0xF7, 0x80, 0x7D, 0xD7, 0x40, - 0x9D, 0xE9, 0x41, 0x75, 0xA6, 0x47, 0x65, 0x98, 0x1E, 0x54, 0x61, 0xBA, 0xC2, 0xD3, 0x8D, 0x8A, 0x72, 0x02, 0x7A, 0xBF, 0xB6, 0x6E, 0x88, 0xA9, 0xFD, 0xA2, - 0x2E, 0xAA, 0x5E, 0x75, 0x51, 0x1D, 0x95, 0x11, 0x55, 0x6F, 0x87, 0xF6, 0x31, 0x12, 0x7C, 0xFF, 0xA8, 0xCE, 0xF7, 0xA8, 0x3A, 0xDF, 0x83, 0x32, 0x7C, 0x8F, - 0x76, 0xC8, 0xF7, 0x50, 0xF0, 0xFD, 0x51, 0x9D, 0xEF, 0x61, 0x75, 0xBE, 0x87, 0x65, 0xF8, 0x1E, 0xEE, 0x90, 0xEF, 0x3E, 0x04, 0x9B, 0x1F, 0x3F, 0x6A, 0x3F, - 0x2C, 0x3D, 0xE2, 0x2F, 0x8B, 0x2B, 0x71, 0x0C, 0xA2, 0xEA, 0xD8, 0x3E, 0x6A, 0x60, 0xEE, 0x86, 0x14, 0x0E, 0xE2, 0x3C, 0x15, 0xE6, 0xCD, 0x0C, 0x42, 0xE5, - 0xC3, 0x41, 0x72, 0x9E, 0xE4, 0x33, 0xB7, 0x9E, 0x2A, 0x53, 0xBB, 0x8B, 0x61, 0xC7, 0xFA, 0xE4, 0xED, 0xA6, 0xC4, 0xF8, 0x76, 0x5C, 0xDD, 0x9E, 0xD5, 0x2B, - 0xE6, 0x8C, 0xAE, 0x9D, 0xD9, 0xF3, 0x09, 0xE5, 0x19, 0xF2, 0x32, 0x5F, 0x41, 0xED, 0xD5, 0xAB, 0x10, 0xA3, 0x06, 0xAA, 0xE4, 0x18, 0xE9, 0x8F, 0x18, 0x3B, - 0x3F, 0x22, 0x43, 0x1A, 0x64, 0x2C, 0x25, 0x06, 0xA3, 0xA3, 0x92, 0xDA, 0x3C, 0xAE, 0x18, 0x9D, 0x90, 0xC6, 0x9D, 0xA9, 0x13, 0xA7, 0x1E, 0x28, 0x80, 0x8F, - 0x15, 0x04, 0x30, 0xAE, 0x2E, 0x80, 0x52, 0x99, 0x0B, 0xD2, 0xB8, 0x3B, 0x01, 0x74, 0x99, 0x00, 0x3E, 0x44, 0x6F, 0xA6, 0xCE, 0x31, 0xE8, 0x1A, 0x15, 0xA8, - 0x51, 0x03, 0x6B, 0x24, 0x18, 0x69, 0x7B, 0xC2, 0xA2, 0x81, 0xA3, 0x72, 0x0A, 0xED, 0x97, 0xCD, 0xAF, 0xE4, 0xC5, 0x4F, 0x85, 0xFC, 0x7B, 0x97, 0x09, 0x56, - 0xBF, 0x2B, 0x2C, 0xBA, 0xBC, 0x00, 0xBA, 0xD5, 0x05, 0xD0, 0x2B, 0x25, 0x80, 0xEE, 0xE3, 0x4A, 0xC6, 0xC7, 0xDB, 0x1F, 0x13, 0x2E, 0x96, 0x56, 0x59, 0xF7, - 0x8F, 0x8D, 0x66, 0xFD, 0x32, 0xC2, 0xDA, 0xA9, 0xF7, 0x0F, 0x22, 0xCE, 0xB5, 0x5F, 0xB4, 0xE4, 0xD6, 0xD7, 0xBC, 0x38, 0x50, 0xBD, 0x08, 0x38, 0x6A, 0x60, - 0xBD, 0x0A, 0x29, 0x3C, 0x91, 0x70, 0x56, 0x32, 0xC0, 0x9F, 0x54, 0x77, 0x87, 0x52, 0x1A, 0x46, 0x5A, 0x77, 0xA7, 0xE2, 0x51, 0x42, 0x10, 0xEC, 0x43, 0xE6, - 0x2A, 0x2A, 0xAE, 0x5E, 0x39, 0x1C, 0x35, 0xB0, 0xD4, 0x85, 0x14, 0x1E, 0x4B, 0x38, 0x2B, 0xA9, 0xE2, 0xB2, 0x29, 0xE9, 0x71, 0xC5, 0xA9, 0x65, 0x6F, 0x97, - 0x39, 0x29, 0x56, 0xBB, 0x63, 0x82, 0x88, 0x7F, 0x65, 0x22, 0x4F, 0xC1, 0xD5, 0x2B, 0xDE, 0xA3, 0x9A, 0xEB, 0xB3, 0xBB, 0x8B, 0xE4, 0x47, 0xB2, 0x4F, 0x90, - 0x17, 0xDB, 0x41, 0xD9, 0x5C, 0xB6, 0x5B, 0x71, 0xE0, 0xDB, 0x69, 0x2A, 0x0B, 0xBD, 0x43, 0xD6, 0xB3, 0xCD, 0x7D, 0x8E, 0x09, 0x54, 0x5F, 0x79, 0x1B, 0x35, - 0xB0, 0x3D, 0x04, 0x29, 0xEC, 0xEB, 0x93, 0x8F, 0x25, 0x99, 0xAA, 0x53, 0x3F, 0xA8, 0xBC, 0x3F, 0xA4, 0xB9, 0xD2, 0xFB, 0x6C, 0x75, 0x53, 0xBE, 0xF4, 0x7E, - 0xF1, 0xEE, 0xE7, 0x72, 0xA5, 0xF7, 0x78, 0x2F, 0xCD, 0x95, 0xDE, 0xAB, 0xD9, 0x4C, 0xA9, 0x8D, 0xB2, 0xC0, 0x18, 0xBE, 0x3F, 0x62, 0x66, 0xF9, 0xB4, 0x4B, - 0x10, 0x8C, 0xF6, 0x5E, 0x9C, 0x86, 0x22, 0x8A, 0x3D, 0xB1, 0x9F, 0x6C, 0x9F, 0x67, 0x3D, 0x83, 0x9C, 0xB0, 0xA0, 0xB6, 0x11, 0x76, 0xFB, 0x75, 0x28, 0x9D, - 0x31, 0xFF, 0x10, 0x57, 0x8D, 0x47, 0xEB, 0xB3, 0x5E, 0x1A, 0xD0, 0x39, 0x2A, 0x89, 0x7B, 0xE7, 0x8F, 0xDC, 0x4F, 0x52, 0x8A, 0xEA, 0x51, 0xFD, 0xF4, 0xF0, - 0x5C, 0xB9, 0x4E, 0x4E, 0xC1, 0xCA, 0x44, 0xF3, 0x41, 0xBC, 0xD4, 0xA2, 0x1E, 0xCD, 0x19, 0x79, 0xBB, 0x89, 0xE6, 0x88, 0x3B, 0xC1, 0x7B, 0x89, 0xAC, 0x86, - 0xC1, 0x96, 0x13, 0x80, 0x7C, 0x13, 0x85, 0x82, 0x00, 0xB2, 0x24, 0x70, 0x2F, 0x22, 0xE8, 0x53, 0x09, 0xF4, 0x53, 0xDA, 0xCF, 0x08, 0xFC, 0xB4, 0x7D, 0xD5, - 0xB8, 0x3F, 0x68, 0xA0, 0x36, 0x81, 0xE2, 0x4A, 0x70, 0x54, 0x52, 0xA7, 0xE5, 0x16, 0x07, 0x13, 0x3A, 0x2D, 0x67, 0xD4, 0x3B, 0x5B, 0x1D, 0x04, 0xE4, 0x03, - 0x2A, 0x80, 0x81, 0xB2, 0x4A, 0xAB, 0x4F, 0x33, 0x07, 0x0D, 0xE4, 0x27, 0x28, 0xAD, 0x04, 0x47, 0x25, 0x55, 0x5A, 0x6E, 0xE9, 0x33, 0xA1, 0x52, 0xF5, 0xF9, - 0x25, 0x27, 0x72, 0x67, 0x2A, 0x1D, 0x52, 0x01, 0x0C, 0x95, 0x55, 0x5A, 0x7D, 0xD6, 0x31, 0x68, 0x60, 0xF7, 0x2E, 0x4A, 0x2B, 0xC1, 0x51, 0x49, 0x95, 0x96, - 0x5B, 0xB2, 0x4B, 0xA8, 0x54, 0x7D, 0x3E, 0xC9, 0x89, 0xDC, 0x99, 0x4A, 0x47, 0x54, 0x00, 0x23, 0x65, 0x95, 0x56, 0xAF, 0x14, 0x0C, 0x1A, 0x28, 0x06, 0xA1, - 0xB4, 0x12, 0x1C, 0x95, 0x54, 0x69, 0xB9, 0xD5, 0xE7, 0x84, 0x4A, 0xD5, 0xD7, 0x39, 0x38, 0x91, 0x3B, 0x53, 0xE9, 0x98, 0x0A, 0x60, 0xAC, 0xAC, 0xD2, 0xEA, - 0xFB, 0xAB, 0x06, 0x0D, 0xEC, 0xDD, 0x46, 0x69, 0x25, 0x38, 0x2A, 0xA9, 0xD2, 0x72, 0xA5, 0xDB, 0x84, 0x4A, 0xD5, 0x57, 0x6E, 0x38, 0x91, 0x3B, 0x53, 0xE9, - 0x11, 0x15, 0xC0, 0x91, 0xB2, 0x4A, 0xAB, 0x6F, 0x5D, 0x1F, 0x34, 0x50, 0xCF, 0x43, 0x69, 0x25, 0x38, 0x2A, 0xA9, 0xD2, 0x72, 0x15, 0x9C, 0x84, 0x4A, 0xD5, - 0xF7, 0x4E, 0x71, 0x22, 0x77, 0xA6, 0xD2, 0x63, 0x2A, 0x80, 0x63, 0x65, 0x95, 0x56, 0xDF, 0xB9, 0x3F, 0x68, 0x60, 0xE7, 0x3E, 0x4A, 0x2B, 0xC1, 0x51, 0x49, - 0x95, 0x96, 0xAB, 0xCD, 0x26, 0x54, 0xAA, 0xBE, 0xDD, 0x89, 0x13, 0xB9, 0x33, 0x95, 0x9E, 0x50, 0x01, 0x9C, 0x28, 0xAB, 0xB4, 0xFA, 0x96, 0x81, 0x41, 0x03, - 0x9B, 0x5F, 0x50, 0x5A, 0xDD, 0x38, 0x47, 0x25, 0x55, 0x5A, 0x6E, 0x81, 0x71, 0x90, 0xB1, 0xF5, 0x45, 0x41, 0xA5, 0x59, 0x0B, 0x8C, 0x8F, 0xA0, 0x7E, 0x67, - 0x5C, 0x4F, 0x2B, 0x7C, 0xFA, 0xE5, 0xE5, 0x4F, 0xAF, 0xB2, 0x0B, 0xFB, 0x99, 0x55, 0xBC, 0x44, 0x5F, 0x8F, 0xBD, 0x8C, 0x17, 0x97, 0x17, 0x12, 0x0E, 0x5A, - 0x66, 0x2F, 0x4D, 0xD4, 0xB6, 0x98, 0xCF, 0xB7, 0x34, 0x06, 0x5C, 0xC2, 0xD2, 0x06, 0xC3, 0xAE, 0x3C, 0x69, 0x29, 0xB0, 0x34, 0x4E, 0xE5, 0x6E, 0x82, 0x07, - 0x22, 0x87, 0xB9, 0x38, 0xF2, 0xFE, 0xBD, 0xD2, 0x9A, 0x0E, 0x03, 0x48, 0x86, 0x8F, 0x61, 0xF7, 0x44, 0x31, 0x7E, 0x80, 0x0C, 0xB2, 0x36, 0xC6, 0xDF, 0x63, - 0x00, 0x41, 0x1A, 0x07, 0x8C, 0xA9, 0x37, 0xCA, 0x4C, 0xA5, 0xAB, 0x00, 0xA5, 0x98, 0xCA, 0xAA, 0xEC, 0xDC, 0x33, 0x53, 0x43, 0xC6, 0x54, 0x8E, 0x93, 0xA6, - 0x98, 0x4A, 0xCF, 0x83, 0x4B, 0x31, 0x95, 0x35, 0x11, 0x8E, 0x98, 0x7A, 0x0C, 0x81, 0x8E, 0xCC, 0x8C, 0xC5, 0xAC, 0x42, 0xA8, 0xBB, 0xBC, 0x38, 0x7C, 0xF9, - 0xE6, 0x42, 0xA3, 0x4B, 0x9A, 0xAE, 0x5D, 0x32, 0xE2, 0x25, 0x3B, 0xFD, 0x53, 0xC5, 0x3C, 0x4A, 0x7A, 0x2C, 0xEA, 0xBD, 0xB9, 0x50, 0x0D, 0x78, 0x1C, 0xB2, - 0x4C, 0xC8, 0x1B, 0x75, 0x07, 0x55, 0x2A, 0x84, 0x21, 0x91, 0x3B, 0x0A, 0x7A, 0x14, 0x7D, 0x3F, 0x92, 0xC1, 0x65, 0x39, 0x19, 0x94, 0xAA, 0x92, 0x26, 0x65, - 0x50, 0x22, 0xEC, 0x0B, 0x22, 0x77, 0x29, 0x03, 0x8C, 0x92, 0x97, 0x17, 0xDA, 0xFB, 0xBF, 0x6B, 0x97, 0x37, 0x6B, 0xD7, 0xDF, 0x78, 0xA4, 0x30, 0xAA, 0x70, - 0xB8, 0x64, 0x5C, 0x19, 0x8F, 0x46, 0x03, 0xD5, 0xC0, 0x32, 0xCA, 0x1E, 0x02, 0xE6, 0xDD, 0x7B, 0x8C, 0x97, 0x94, 0xD0, 0x61, 0xC8, 0xE0, 0xF7, 0x04, 0x34, - 0xAD, 0x14, 0x37, 0x39, 0x60, 0x92, 0xC3, 0x5E, 0x17, 0xB7, 0x57, 0x2B, 0x32, 0x28, 0xCF, 0x28, 0x07, 0xF7, 0x3A, 0x1C, 0x50, 0x2A, 0x47, 0x21, 0x7B, 0x1F, - 0x7F, 0xF8, 0xA0, 0xC6, 0x58, 0xBA, 0x8E, 0x56, 0x4E, 0x75, 0x59, 0x8F, 0x8C, 0xDE, 0xD3, 0xA0, 0x20, 0xBD, 0x71, 0x76, 0x08, 0xA1, 0x77, 0x1B, 0x26, 0x43, - 0x92, 0x67, 0x73, 0x6B, 0x01, 0x76, 0x2C, 0xEF, 0x83, 0x8A, 0x96, 0xBD, 0xEC, 0x14, 0xBF, 0x51, 0xD9, 0x9E, 0x41, 0xF4, 0x07, 0x93, 0x40, 0xA7, 0x13, 0x02, - 0x5F, 0x19, 0x0B, 0x12, 0x5D, 0xD7, 0x58, 0x6C, 0xCF, 0x8B, 0xD9, 0x06, 0x43, 0x68, 0x5C, 0x11, 0xFE, 0x41, 0x4D, 0x6D, 0xE9, 0x91, 0xF9, 0xB9, 0xFE, 0x45, - 0x88, 0x93, 0x3F, 0x95, 0x87, 0x4D, 0x74, 0xCD, 0x74, 0xAF, 0x1D, 0xDB, 0x35, 0x70, 0x3C, 0x30, 0xD6, 0x01, 0x50, 0xDA, 0xF9, 0x6D, 0x8D, 0x2F, 0xBE, 0x32, - 0xF0, 0x21, 0x2E, 0x23, 0xA7, 0x9F, 0x98, 0x55, 0xCC, 0x6C, 0xD7, 0x17, 0xB3, 0x39, 0x3C, 0x0C, 0x3F, 0xC0, 0xF9, 0x3F, 0xFF, 0x5D, 0xB4, 0x83, 0xC0, 0x5A, - 0x2D, 0x62, 0x02, 0xD0, 0x35, 0xDF, 0x9B, 0x9D, 0xEB, 0x40, 0xA9, 0xE7, 0xFA, 0xBE, 0xEB, 0x59, 0x0B, 0x2B, 0x43, 0x3B, 0x59, 0xD2, 0x3E, 0x94, 0x89, 0x3B, - 0xD5, 0x58, 0xA2, 0xF8, 0x33, 0x7F, 0xE6, 0x59, 0xEB, 0x60, 0xF2, 0xC4, 0x74, 0x67, 0x9B, 0x15, 0x71, 0x82, 0x8E, 0x61, 0x9A, 0x97, 0x57, 0x70, 0xF0, 0x2D, - 0x7E, 0xAC, 0x0D, 0x24, 0xDF, 0xDA, 0x7B, 0xFD, 0x8F, 0x77, 0x38, 0x3A, 0xE3, 0x35, 0x90, 0x17, 0x31, 0xF7, 0x0E, 0xB4, 0xF9, 0xC6, 0x61, 0x03, 0x64, 0x8B, - 0x60, 0xDB, 0x7D, 0xED, 0x0F, 0xC0, 0x78, 0x65, 0x78, 0xDA, 0xD4, 0xF0, 0xC9, 0x5B, 0xD7, 0x0F, 0xB4, 0x73, 0x2D, 0xC4, 0x68, 0xBB, 0x33, 0xBA, 0x9D, 0xA3, - 0xC3, 0xF8, 0xE2, 0x2D, 0x19, 0xE3, 0x3F, 0x7A, 0x36, 0x34, 0x0D, 0xA1, 0x9E, 0x6B, 0x7B, 0xA7, 0xC7, 0xBD, 0x3D, 0xB4, 0xDD, 0xB0, 0x8B, 0x39, 0x81, 0xE8, - 0x0F, 0xED, 0x5A, 0x1B, 0xCF, 0x3E, 0xD0, 0x66, 0xD3, 0xFD, 0x3F, 0x28, 0xF5, 0xF4, 0x32, 0x5E, 0xDB, 0xE7, 0xCC, 0x74, 0x82, 0x25, 0x71, 0x5A, 0x11, 0x65, - 0x1E, 0xF1, 0xD7, 0xAE, 0xE3, 0x13, 0x46, 0x1C, 0xFB, 0x59, 0xF3, 0xE8, 0x7A, 0xC7, 0x0F, 0x8C, 0x60, 0xE3, 0x6B, 0x4F, 0xCF, 0xCF, 0xB5, 0x7E, 0xB7, 0x1B, - 0x6F, 0xA6, 0x41, 0x37, 0xE9, 0x76, 0x07, 0x5A, 0xEA, 0xC2, 0x0F, 0xE4, 0x26, 0xD8, 0xFF, 0x2A, 0x84, 0xB9, 0xD3, 0x88, 0xED, 0x93, 0x04, 0x92, 0x10, 0x00, - 0x5F, 0x27, 0xD7, 0xDA, 0x4F, 0x12, 0xD8, 0x32, 0x8D, 0xC0, 0xD8, 0xFF, 0x23, 0xA1, 0x2F, 0xE8, 0x15, 0x28, 0x39, 0xD0, 0xE8, 0xAD, 0xAF, 0x62, 0xB7, 0xEE, - 0xF6, 0x3B, 0x20, 0x43, 0xE0, 0x37, 0x84, 0x26, 0x9E, 0x97, 0xA4, 0x98, 0x42, 0xB7, 0x7B, 0x07, 0x1A, 0xDE, 0x49, 0xC2, 0xC6, 0x88, 0x7C, 0x22, 0xAE, 0x09, - 0xA1, 0xE5, 0xA3, 0x95, 0xA0, 0x64, 0xE8, 0xEE, 0x12, 0x2A, 0x82, 0x38, 0xF4, 0x3D, 0x59, 0x80, 0xC4, 0x16, 0x07, 0x3C, 0x2C, 0x1D, 0xD0, 0x98, 0x74, 0xC0, - 0xC2, 0x59, 0x4C, 0x6B, 0xE0, 0xD0, 0xBE, 0x6B, 0x13, 0xB0, 0x89, 0x45, 0x6B, 0x8F, 0x7F, 0x0A, 0x14, 0xEC, 0x69, 0xAF, 0x7B, 0xB3, 0xF7, 0x1C, 0xC0, 0x3B, - 0x81, 0xFB, 0x21, 0xF0, 0x2C, 0x67, 0xD1, 0xEA, 0x8D, 0xF7, 0x23, 0x5C, 0xF4, 0x36, 0x22, 0x4C, 0xDD, 0xA7, 0xD7, 0x69, 0x17, 0xE9, 0x1B, 0x2D, 0x7E, 0xFD, - 0xF9, 0xDE, 0xFE, 0x1E, 0x27, 0x9D, 0x9E, 0x83, 0xB1, 0xB5, 0xD8, 0xC1, 0x33, 0x4A, 0xE1, 0xBE, 0x76, 0x76, 0xC6, 0xBB, 0x61, 0xAD, 0xF0, 0x22, 0x34, 0xA2, - 0x7F, 0x52, 0xB7, 0x42, 0x43, 0xFC, 0xF5, 0xCB, 0x3F, 0x84, 0xC5, 0xDE, 0x1D, 0x02, 0xD5, 0x2F, 0x30, 0x2E, 0x7F, 0xF9, 0x07, 0xFC, 0x7F, 0xF7, 0x8C, 0x86, - 0xE2, 0x2F, 0xFF, 0xC0, 0x3F, 0x77, 0xCF, 0xA0, 0x27, 0x38, 0xA6, 0xFD, 0xDD, 0xFD, 0x4A, 0xA5, 0xB0, 0x2D, 0xBB, 0x45, 0xA6, 0xEC, 0x42, 0xA1, 0x95, 0xA6, - 0x69, 0x91, 0x43, 0xD4, 0xAF, 0x91, 0xF7, 0xB6, 0x66, 0xAE, 0x09, 0xCA, 0x09, 0xC0, 0x8E, 0x85, 0xCA, 0x6D, 0x50, 0x89, 0x10, 0x54, 0x57, 0xA8, 0xDC, 0x9A, - 0xD3, 0x96, 0x1A, 0x77, 0x94, 0xC8, 0x3C, 0x44, 0xCB, 0xB5, 0xE1, 0xF9, 0xE4, 0x1B, 0x27, 0x68, 0x05, 0x09, 0x97, 0xC8, 0x90, 0xF8, 0x64, 0x92, 0x60, 0x01, - 0x7F, 0x00, 0x07, 0xED, 0xF6, 0xB8, 0xD2, 0x42, 0x53, 0x7B, 0x12, 0x5A, 0x61, 0x44, 0x29, 0xBB, 0x99, 0x61, 0x85, 0x3F, 0xCF, 0xEC, 0xCF, 0xAD, 0x1B, 0xF8, - 0x2F, 0x1D, 0x28, 0xB6, 0x44, 0x84, 0x8D, 0x5E, 0xE0, 0x7F, 0x20, 0x17, 0xFC, 0x93, 0xA9, 0x1F, 0xC0, 0xFA, 0xDE, 0xB6, 0x5B, 0xEC, 0xB3, 0x5F, 0xA0, 0x9A, - 0x0D, 0x04, 0x21, 0xFF, 0x16, 0xC3, 0x81, 0xEB, 0x06, 0x9F, 0x0E, 0xB4, 0xB5, 0x07, 0x84, 0xD1, 0x2F, 0x7D, 0xC0, 0x31, 0x20, 0x22, 0x0E, 0xFB, 0x5B, 0x48, - 0xC1, 0xDA, 0xB6, 0x5F, 0x30, 0xAC, 0x40, 0x02, 0x3B, 0x00, 0x4D, 0x6D, 0xD0, 0x62, 0xE0, 0xFF, 0xBB, 0x67, 0xD0, 0x09, 0x1C, 0xC2, 0xFF, 0x77, 0xCF, 0xB0, - 0x2B, 0xD4, 0x25, 0xF6, 0x78, 0xF7, 0x0C, 0x7A, 0x84, 0x13, 0xF8, 0x1F, 0xDA, 0x60, 0xBF, 0xD8, 0x0A, 0xFF, 0xC2, 0x1D, 0xDA, 0x3F, 0xDE, 0xA4, 0x07, 0xEC, - 0x02, 0x3F, 0xCD, 0x63, 0x90, 0xBD, 0xE9, 0xBE, 0x45, 0xDF, 0x3C, 0xFE, 0xE9, 0x06, 0xD8, 0xA1, 0x07, 0xB7, 0xE0, 0xF8, 0x8E, 0x89, 0xE7, 0xF8, 0xE7, 0x56, - 0x98, 0x27, 0x5E, 0xE0, 0x47, 0x70, 0x8D, 0xBE, 0x9D, 0x15, 0x2F, 0xB1, 0x03, 0x6C, 0x45, 0xDF, 0xA5, 0x49, 0x5B, 0xB1, 0x23, 0xB8, 0xC6, 0xDF, 0xC0, 0x78, - 0xA0, 0xF1, 0x77, 0xFC, 0x15, 0x0A, 0x27, 0x7A, 0x07, 0xDF, 0x0B, 0xFF, 0x06, 0x19, 0x64, 0xA4, 0xA1, 0x54, 0xC2, 0xB3, 0xDB, 0xBB, 0x67, 0x04, 0xEF, 0x51, - 0x22, 0xE1, 0xF8, 0x96, 0x1F, 0xC3, 0x75, 0xA0, 0x0F, 0xEF, 0x08, 0x82, 0xE9, 0x85, 0xDB, 0xE8, 0x02, 0xB4, 0x08, 0xF0, 0x3E, 0x27, 0x1E, 0xCE, 0x6E, 0xC3, - 0x33, 0x84, 0xA6, 0xB0, 0x9C, 0x0D, 0x38, 0xBD, 0x8D, 0x4E, 0xE1, 0x2E, 0xF2, 0x82, 0x0A, 0xE0, 0x3C, 0xDD, 0x3D, 0xE3, 0x3C, 0xA1, 0x16, 0xD9, 0x51, 0x5A, - 0xD4, 0x18, 0xF4, 0x02, 0x1E, 0x24, 0x5F, 0xB1, 0x1C, 0x24, 0x36, 0x3C, 0x42, 0x00, 0xB8, 0xB4, 0x09, 0x1E, 0xBE, 0xBA, 0xFD, 0xC6, 0x6C, 0xED, 0xF1, 0x4F, - 0xB7, 0xEE, 0x61, 0x88, 0x8E, 0xC3, 0x74, 0x5C, 0x67, 0x66, 0x5B, 0x33, 0x8C, 0x04, 0xAD, 0x7D, 0xED, 0x7C, 0xC2, 0xC3, 0x34, 0x7A, 0x2C, 0x34, 0x8F, 0x7B, - 0x61, 0x26, 0x6A, 0x8F, 0x7F, 0x7C, 0x74, 0x6F, 0xBF, 0x43, 0x1D, 0x8D, 0x3B, 0x13, 0xA2, 0xE0, 0x31, 0x46, 0x0D, 0x07, 0x36, 0x96, 0xE0, 0xD8, 0x0A, 0x07, - 0xB9, 0x48, 0x68, 0xEB, 0x18, 0x16, 0x8A, 0x26, 0x3E, 0x92, 0x74, 0x53, 0x83, 0x48, 0x4E, 0xD8, 0x12, 0x11, 0xEA, 0x69, 0x3A, 0x42, 0x81, 0xAA, 0xBC, 0xA0, - 0xB5, 0x77, 0xE9, 0x79, 0xAE, 0xF7, 0xAF, 0xBD, 0xE7, 0xD8, 0xE8, 0xF9, 0xDE, 0xBF, 0x4F, 0xB5, 0xBD, 0xE7, 0xF1, 0x50, 0x75, 0x97, 0x8E, 0x29, 0x4C, 0x63, - 0x0B, 0x45, 0x8D, 0x2D, 0x62, 0x1A, 0x5B, 0xDC, 0xAF, 0xC6, 0xE2, 0x9F, 0x8C, 0xAD, 0xA3, 0xB5, 0xF8, 0x27, 0x5A, 0x73, 0x34, 0x57, 0x08, 0xCF, 0x95, 0xC6, - 0xB5, 0xB5, 0x90, 0x69, 0xAB, 0x8A, 0x9A, 0xD8, 0x18, 0x0E, 0xDE, 0x43, 0xBC, 0xB7, 0x3F, 0xBC, 0xFB, 0x16, 0xC7, 0x02, 0xB9, 0xCA, 0x42, 0x8D, 0xA5, 0xB3, - 0x2D, 0x09, 0x06, 0x4C, 0x0E, 0x12, 0x23, 0x53, 0x22, 0x49, 0x78, 0xBE, 0xA7, 0xB5, 0x28, 0x4A, 0x4C, 0x11, 0x0A, 0x0C, 0x81, 0x8F, 0x2C, 0x6A, 0xBE, 0x8B, - 0xA3, 0x89, 0x70, 0xDE, 0x08, 0x2A, 0xC7, 0x16, 0x10, 0x40, 0x49, 0x89, 0x0C, 0xF3, 0x96, 0xC3, 0xC4, 0x06, 0xBD, 0xC6, 0x5D, 0x84, 0xFA, 0xAB, 0xAF, 0x1A, - 0xD4, 0x44, 0x4C, 0x8F, 0x62, 0x9B, 0x5F, 0x28, 0x1D, 0x1E, 0xF9, 0x95, 0x04, 0xC4, 0x3F, 0x05, 0x22, 0x31, 0x70, 0x3E, 0x62, 0x94, 0xC0, 0x72, 0x2B, 0xC1, - 0x42, 0x47, 0x1A, 0x25, 0x1C, 0xF4, 0xF3, 0x11, 0x19, 0x18, 0xD4, 0xA8, 0xA0, 0xDF, 0x6B, 0x90, 0x60, 0x10, 0x63, 0x9A, 0x12, 0x12, 0xF1, 0xAD, 0x81, 0x6C, - 0x3C, 0x6A, 0xC4, 0x88, 0x37, 0xFC, 0x4B, 0xF0, 0xF0, 0x31, 0x54, 0x09, 0x0D, 0x7F, 0x3B, 0x7D, 0x26, 0x16, 0x35, 0x62, 0xF8, 0x0B, 0xE1, 0x65, 0x3C, 0xF1, - 0x31, 0x5B, 0x8D, 0x27, 0xFE, 0x1E, 0xF3, 0x6C, 0x3C, 0x8A, 0xB2, 0xE1, 0xEF, 0x0E, 0x97, 0x59, 0x1D, 0x4B, 0x11, 0x72, 0x1D, 0x83, 0x35, 0x01, 0x60, 0x5E, - 0x96, 0x7E, 0xD1, 0x3B, 0xED, 0x46, 0x18, 0x78, 0x46, 0x91, 0x87, 0x81, 0x37, 0x49, 0x63, 0x10, 0xD1, 0xE1, 0x01, 0x72, 0xBB, 0x87, 0x88, 0x42, 0x90, 0xA3, - 0xAB, 0x45, 0x21, 0x48, 0xBB, 0x45, 0xF8, 0x09, 0x61, 0x32, 0xC2, 0x0F, 0x2D, 0x68, 0xB0, 0x2F, 0x18, 0xE7, 0xC9, 0x3F, 0xFC, 0x20, 0xB0, 0x4C, 0x89, 0x88, - 0x03, 0xD2, 0x79, 0x25, 0x4B, 0xE2, 0xDF, 0xBE, 0x4D, 0x19, 0x12, 0x2D, 0x96, 0xDC, 0xFA, 0x6A, 0xA1, 0xEB, 0xD6, 0xCF, 0xC0, 0x40, 0xE7, 0x0E, 0x6A, 0xB9, - 0x19, 0xFF, 0x58, 0xAC, 0x04, 0x09, 0xCC, 0x39, 0x94, 0x50, 0xF0, 0x2F, 0x66, 0xCA, 0x18, 0xA1, 0x1F, 0x54, 0x54, 0x62, 0x45, 0x7C, 0x3C, 0x51, 0x46, 0x07, - 0x9D, 0xDE, 0xE4, 0x29, 0x85, 0x7F, 0xA2, 0x2E, 0x4B, 0x23, 0x6B, 0xD5, 0x21, 0x57, 0x7C, 0x8E, 0x4D, 0x32, 0xEC, 0x56, 0x9C, 0x15, 0x3E, 0xCC, 0x10, 0xBD, - 0xF8, 0x28, 0xE6, 0xE2, 0xC4, 0x96, 0xA6, 0xA2, 0xC4, 0xEE, 0x18, 0x01, 0x24, 0x47, 0xD3, 0x4D, 0x40, 0xFC, 0x0E, 0xD6, 0x0F, 0x42, 0xE1, 0x6C, 0xDD, 0xEA, - 0x38, 0x40, 0x00, 0x45, 0xB8, 0x1F, 0x8F, 0x55, 0x2C, 0x70, 0x6C, 0xE1, 0x62, 0x97, 0xB3, 0xD0, 0xB1, 0xBB, 0x19, 0x18, 0x79, 0x7A, 0x9B, 0x84, 0xC0, 0x8B, - 0x59, 0xD8, 0x68, 0x8D, 0x28, 0x86, 0xAB, 0x3F, 0x1A, 0x6D, 0x27, 0xB9, 0xBC, 0x03, 0xB6, 0xAC, 0x84, 0x02, 0xE9, 0x60, 0x89, 0x3E, 0x2A, 0x7B, 0xCD, 0x60, - 0x16, 0xAA, 0xED, 0x89, 0x35, 0xA5, 0xBD, 0xD3, 0xAD, 0x7A, 0x06, 0x40, 0x70, 0xAB, 0xD2, 0x5E, 0x30, 0x1A, 0x4F, 0xA3, 0x62, 0x89, 0xA6, 0x4D, 0x3D, 0x62, - 0x7C, 0xFE, 0x2A, 0x81, 0x8C, 0x56, 0xFF, 0x43, 0x4C, 0xEC, 0x1A, 0x16, 0x05, 0x53, 0x97, 0xD8, 0x13, 0x37, 0x6D, 0xD7, 0x21, 0xF2, 0x5E, 0x13, 0xD5, 0x11, - 0xDE, 0x11, 0x3F, 0x33, 0xC9, 0xDC, 0xD8, 0xD8, 0x41, 0x04, 0xE6, 0x91, 0x60, 0xE3, 0x39, 0xBC, 0x5A, 0xB2, 0x3D, 0xB9, 0x92, 0x96, 0xE9, 0x1A, 0xB4, 0xCD, - 0xC3, 0x43, 0xED, 0x65, 0x10, 0x18, 0xA0, 0x00, 0x5C, 0x66, 0x5D, 0xA2, 0x7C, 0x34, 0x83, 0x17, 0x7C, 0x5D, 0x0F, 0x8D, 0x12, 0xEB, 0xCF, 0x1E, 0x70, 0x4D, - 0xBD, 0xD1, 0x07, 0x10, 0xE1, 0xA4, 0x14, 0x55, 0xE7, 0x3F, 0x1B, 0xE2, 0xDD, 0x7E, 0xA0, 0x02, 0x73, 0xBD, 0x97, 0xE0, 0x8B, 0x7B, 0x9D, 0x68, 0xA9, 0x64, - 0x8F, 0xD5, 0x37, 0x3B, 0x80, 0xEA, 0x12, 0xFA, 0x00, 0x1D, 0x47, 0x36, 0xCF, 0xB8, 0x09, 0xF5, 0xAE, 0x9D, 0x9F, 0x9F, 0x73, 0x65, 0xA4, 0x0B, 0xAA, 0xD0, - 0xC2, 0x75, 0x3E, 0x93, 0xDB, 0xCD, 0x1A, 0xC4, 0x1F, 0x95, 0x48, 0x53, 0x45, 0x5B, 0x2E, 0x1D, 0xD2, 0x81, 0x96, 0x17, 0xBC, 0x4C, 0xD6, 0x1B, 0x48, 0x1A, - 0x45, 0x2A, 0xA0, 0xD6, 0x89, 0x9E, 0xF8, 0xD5, 0x56, 0xA3, 0xBB, 0x27, 0xF2, 0x33, 0x49, 0x79, 0x99, 0x13, 0xC8, 0x85, 0x27, 0x86, 0xAE, 0x54, 0x0F, 0x4F, - 0x92, 0xA8, 0xEE, 0xF6, 0x9F, 0x44, 0x91, 0x61, 0xB3, 0x36, 0x8D, 0x80, 0x24, 0x83, 0x43, 0x68, 0x0B, 0xE2, 0xE6, 0xCA, 0x0D, 0x48, 0x2A, 0x62, 0x58, 0x8E, - 0x15, 0x58, 0x86, 0xFD, 0x31, 0xB2, 0xC6, 0x9D, 0xBA, 0xBF, 0xC4, 0xC7, 0x4B, 0xF8, 0xFF, 0x56, 0x85, 0x57, 0xAD, 0x2A, 0xB9, 0x65, 0x21, 0x61, 0x3C, 0x88, - 0xAC, 0x24, 0x2E, 0x87, 0x44, 0x58, 0xE0, 0xF7, 0x45, 0x4F, 0x4F, 0x9F, 0xD2, 0xA3, 0x27, 0xA1, 0xD2, 0x44, 0xF4, 0x38, 0xD7, 0xA2, 0x1B, 0x29, 0x05, 0x6F, - 0xE3, 0x4E, 0xE1, 0x10, 0xC8, 0x63, 0x18, 0x98, 0x6F, 0x85, 0xEA, 0x5D, 0xC3, 0x54, 0x17, 0x6D, 0xE1, 0xFF, 0xA3, 0xFE, 0x23, 0x8A, 0xFA, 0xBB, 0x0B, 0xF1, - 0x39, 0xB6, 0x9D, 0xF2, 0x00, 0x06, 0x27, 0x5F, 0x74, 0x79, 0xBE, 0x77, 0xA0, 0xC9, 0x57, 0x55, 0x52, 0x69, 0xC5, 0xD2, 0x32, 0x19, 0xC9, 0x91, 0x5D, 0xA1, - 0x84, 0x70, 0x61, 0x14, 0x97, 0x0E, 0x71, 0x1D, 0xB1, 0xB5, 0xC7, 0x56, 0x6D, 0x69, 0x34, 0xBE, 0x8B, 0x12, 0x92, 0xA5, 0x7B, 0x9D, 0x07, 0xE9, 0x41, 0xCC, - 0xB9, 0x22, 0x29, 0xE0, 0x10, 0xDA, 0xB4, 0x7C, 0x63, 0x6A, 0x17, 0x77, 0xCD, 0xDB, 0x99, 0x7C, 0x28, 0x80, 0x06, 0xE2, 0x0A, 0x80, 0x06, 0x1E, 0xF5, 0x99, - 0x18, 0x5A, 0xE2, 0x14, 0x61, 0x15, 0x64, 0xE5, 0x22, 0x9E, 0x1B, 0xE0, 0xC4, 0x49, 0xCC, 0x2C, 0x90, 0x96, 0x08, 0xB1, 0xF1, 0xCB, 0x00, 0x91, 0x3C, 0x3D, - 0xD7, 0x9C, 0x8D, 0x6D, 0x83, 0x05, 0x22, 0x0B, 0x60, 0x81, 0xF1, 0xBB, 0xD2, 0x00, 0xFD, 0xE7, 0x8D, 0x66, 0x21, 0xE5, 0x09, 0x09, 0x3C, 0x7B, 0x96, 0xC4, - 0x86, 0xCB, 0xB7, 0x2C, 0x35, 0x0F, 0x7B, 0x63, 0xED, 0xD9, 0xDB, 0x74, 0xA3, 0x51, 0x96, 0x93, 0x04, 0x43, 0xF5, 0xD3, 0x84, 0xE0, 0x63, 0x19, 0x0E, 0x10, - 0x62, 0x99, 0x54, 0x40, 0xB8, 0x49, 0x43, 0xDF, 0x5A, 0xE9, 0x7A, 0x41, 0xAD, 0xBE, 0x45, 0xF8, 0x1E, 0x9D, 0x7D, 0x90, 0x3F, 0x1A, 0x73, 0x74, 0x41, 0x64, - 0x3B, 0x61, 0x57, 0x71, 0x8C, 0x8B, 0x04, 0x46, 0x64, 0x2C, 0x45, 0x37, 0xFE, 0x68, 0x07, 0xD0, 0x14, 0x77, 0xC8, 0xC4, 0x06, 0xEF, 0xED, 0xD1, 0x9F, 0x76, - 0xBC, 0xDD, 0x30, 0x97, 0x82, 0xEB, 0xE9, 0xA7, 0x05, 0x34, 0x97, 0x31, 0x46, 0xD1, 0x5D, 0x4F, 0x91, 0x25, 0x4A, 0x02, 0x1C, 0xE6, 0xA1, 0x9A, 0x1B, 0x33, - 0xF2, 0xC9, 0x23, 0x33, 0x77, 0xE1, 0x58, 0xBF, 0x13, 0x19, 0x42, 0xE6, 0x50, 0x2D, 0xE2, 0x78, 0xAE, 0x98, 0x66, 0x23, 0x72, 0xEE, 0x2B, 0xC9, 0xEB, 0x99, - 0x3D, 0x69, 0x3A, 0xB8, 0xD5, 0x27, 0x0B, 0x37, 0x1F, 0xF8, 0x56, 0x70, 0xBB, 0xDD, 0xCF, 0x44, 0x6B, 0xF7, 0x04, 0xF5, 0xD0, 0xF4, 0x0D, 0x6E, 0xD0, 0x09, - 0x79, 0x08, 0x2F, 0x24, 0x93, 0x50, 0xE1, 0x9E, 0xE1, 0x0A, 0x58, 0xDC, 0x5E, 0xD8, 0x50, 0x19, 0x8D, 0x93, 0x91, 0x41, 0xDE, 0xD3, 0xC8, 0xD1, 0xC3, 0x61, - 0x43, 0x1A, 0xDF, 0x6B, 0x0E, 0x1A, 0x39, 0x38, 0xD9, 0xC6, 0x97, 0x34, 0xD2, 0xCD, 0x74, 0x65, 0x05, 0x12, 0x84, 0x7B, 0xBD, 0xBD, 0x32, 0xE3, 0x4F, 0xDC, - 0x5B, 0x59, 0xC4, 0xA3, 0xA9, 0x39, 0x20, 0x4A, 0xAC, 0xE8, 0xCD, 0xD8, 0x06, 0xCF, 0x17, 0x30, 0xFD, 0xC6, 0x75, 0x3A, 0x54, 0x70, 0x6A, 0x81, 0x9C, 0xA1, - 0x60, 0xFB, 0x3A, 0x28, 0x8A, 0xE4, 0xCE, 0x0E, 0xB1, 0x9B, 0x22, 0x99, 0xC9, 0xC7, 0x37, 0x14, 0xFC, 0xEA, 0x11, 0x80, 0xF3, 0xB1, 0x9A, 0xA8, 0x7D, 0xF9, - 0x07, 0x45, 0x71, 0xA7, 0xCD, 0x21, 0x5A, 0xF8, 0x4B, 0x62, 0xD2, 0xCA, 0x57, 0xB0, 0xF1, 0x4F, 0x35, 0x5C, 0x14, 0x4F, 0xEC, 0xE4, 0xB8, 0xFB, 0x35, 0xB4, - 0x90, 0x70, 0xB0, 0x29, 0x9C, 0x6C, 0xD0, 0x0D, 0x3F, 0xF9, 0xF3, 0x0C, 0x96, 0x9E, 0x4B, 0x0A, 0x4B, 0xF8, 0x63, 0x91, 0xC4, 0xEE, 0x40, 0x4E, 0x03, 0xDD, - 0x7C, 0x07, 0xD9, 0x4B, 0xCA, 0x4C, 0xF7, 0xF9, 0x34, 0x09, 0x34, 0x60, 0x8A, 0x90, 0xC7, 0x74, 0x84, 0x93, 0x21, 0x26, 0xA6, 0x84, 0x84, 0x19, 0x33, 0x9C, - 0x97, 0xE2, 0xDD, 0x30, 0x3C, 0x0B, 0x08, 0x65, 0xF1, 0x9B, 0x0F, 0x93, 0x9B, 0xFD, 0x27, 0xA1, 0x18, 0xB6, 0x71, 0x60, 0x07, 0x31, 0x04, 0x09, 0x11, 0x65, - 0x89, 0x89, 0x1B, 0x4D, 0x72, 0x5E, 0x96, 0x23, 0x33, 0xF6, 0x8B, 0x8D, 0x99, 0x74, 0xC0, 0xA4, 0x3D, 0xFF, 0x8B, 0x1A, 0xCD, 0xBF, 0x0F, 0xD8, 0x20, 0x1B, - 0x8B, 0x79, 0xFB, 0x65, 0x08, 0xDA, 0x9A, 0x24, 0x16, 0x12, 0x73, 0x6F, 0xE9, 0xB2, 0xF8, 0x41, 0x80, 0xA3, 0xF8, 0x20, 0x29, 0xDD, 0x9A, 0x26, 0xC6, 0x73, - 0xBA, 0x90, 0x41, 0x89, 0x6C, 0x62, 0x73, 0x3C, 0x21, 0x1E, 0x69, 0x3E, 0x97, 0x29, 0x2E, 0x66, 0x5F, 0xCC, 0x61, 0xAF, 0x2C, 0x72, 0x9D, 0x5B, 0x37, 0xA5, - 0x7B, 0xB5, 0xA8, 0xBC, 0x22, 0x80, 0x8B, 0x70, 0x6F, 0x5E, 0x21, 0x64, 0xB4, 0x8F, 0x2F, 0x86, 0x83, 0x6E, 0xD2, 0x53, 0x5B, 0x5C, 0xA5, 0x4D, 0x13, 0xA0, - 0x88, 0xB5, 0x18, 0x56, 0xEC, 0xDB, 0x4E, 0x93, 0x1F, 0x1F, 0x7C, 0xF2, 0xE0, 0xE9, 0x28, 0xC7, 0x1A, 0xC7, 0xA0, 0xA9, 0xE7, 0x17, 0x03, 0xC7, 0x77, 0x04, - 0xC6, 0x69, 0x37, 0xAE, 0x14, 0x80, 0xA3, 0x6D, 0x8C, 0x31, 0x50, 0x31, 0x9E, 0xE5, 0x01, 0x42, 0x1B, 0xB6, 0x4D, 0x75, 0x2F, 0xA6, 0x5F, 0x3F, 0x70, 0xD7, - 0x1F, 0x28, 0x21, 0xA9, 0x40, 0x74, 0x4D, 0x17, 0x07, 0x3A, 0x78, 0xBF, 0xC5, 0xD3, 0xA4, 0xB8, 0x6C, 0x93, 0xEB, 0xA9, 0x1F, 0x70, 0xE5, 0x40, 0x63, 0x78, - 0xF6, 0x92, 0x39, 0x2E, 0x5D, 0x54, 0x90, 0xF6, 0x80, 0x96, 0xD2, 0xF1, 0xBD, 0x19, 0x1B, 0x0E, 0xC2, 0x4D, 0x7F, 0x18, 0xAD, 0xF0, 0xF0, 0x57, 0xD6, 0x27, - 0x0E, 0xDF, 0x09, 0x9B, 0xDA, 0x2F, 0xA4, 0xC5, 0x5D, 0xA7, 0x49, 0x89, 0x4A, 0x4A, 0xCC, 0xBF, 0x7D, 0x1C, 0x07, 0xD8, 0xD0, 0x87, 0x11, 0x33, 0x66, 0x6F, - 0x19, 0x61, 0x39, 0x12, 0x13, 0x8F, 0x83, 0x49, 0xE2, 0x63, 0x63, 0x19, 0xDB, 0x31, 0xFA, 0xE2, 0xD3, 0x6C, 0x0A, 0xC3, 0xD7, 0x6B, 0x70, 0x3E, 0xF0, 0xF6, - 0xEB, 0xD6, 0xFE, 0x5D, 0x1E, 0x3B, 0x4C, 0x5C, 0x91, 0xED, 0xA8, 0x12, 0x41, 0x07, 0x0A, 0x39, 0xB6, 0x84, 0x7C, 0xE4, 0xE8, 0xE2, 0x0E, 0x73, 0xE9, 0x88, - 0x69, 0x4A, 0x96, 0x60, 0xCF, 0xB7, 0x45, 0xCB, 0x32, 0xD5, 0x04, 0x82, 0x68, 0x08, 0xD8, 0x22, 0x36, 0x95, 0xA8, 0xC6, 0xEC, 0x42, 0x34, 0x10, 0xB4, 0xC7, - 0x7D, 0x30, 0x83, 0xF6, 0x64, 0x1E, 0x9F, 0x4A, 0x18, 0x99, 0x00, 0x42, 0x67, 0xCA, 0x59, 0xC2, 0x99, 0x19, 0xCE, 0x95, 0x91, 0x58, 0xC2, 0x99, 0x01, 0x41, - 0x01, 0xE1, 0x7E, 0xD3, 0xD2, 0x59, 0x03, 0x9D, 0x3B, 0x00, 0x3B, 0xEB, 0xD0, 0x87, 0x61, 0x70, 0x46, 0x82, 0x36, 0x40, 0x4F, 0x12, 0xB7, 0x97, 0x84, 0xBE, - 0x80, 0x90, 0xDF, 0x67, 0x67, 0xAC, 0x41, 0xD8, 0xCB, 0xD4, 0x35, 0x6F, 0x3B, 0xC6, 0x7A, 0x4D, 0x1C, 0xF3, 0x62, 0x69, 0xD9, 0x66, 0x8B, 0x81, 0xC6, 0xD6, - 0x31, 0x30, 0x2C, 0x12, 0xBA, 0xF5, 0x8D, 0x63, 0x05, 0x6F, 0xBE, 0x60, 0xD7, 0x5A, 0x7B, 0x7D, 0x53, 0xEC, 0x5C, 0xE4, 0xCD, 0x3A, 0xA6, 0x67, 0x5C, 0x7F, - 0x83, 0xFB, 0xA2, 0xA9, 0x39, 0x1C, 0x74, 0x0F, 0xBA, 0xBC, 0x41, 0x00, 0xE9, 0x96, 0x10, 0x39, 0xE2, 0xC5, 0xFD, 0xA3, 0x3F, 0x7E, 0xFF, 0x6D, 0x84, 0x37, - 0x70, 0x5F, 0xB3, 0x4B, 0xAD, 0x3D, 0xBA, 0xB1, 0xFA, 0xF0, 0xB7, 0x35, 0xEE, 0x57, 0x11, 0xC3, 0x4C, 0x4C, 0x8C, 0xB8, 0x67, 0x1A, 0x45, 0xC5, 0x9A, 0x7F, - 0x15, 0x47, 0x0A, 0x97, 0x1D, 0x18, 0x27, 0xD0, 0xDC, 0x5B, 0x32, 0x50, 0xB1, 0xA3, 0x1A, 0xC1, 0x91, 0x93, 0xAF, 0x61, 0xA2, 0xFA, 0x0B, 0x31, 0x3C, 0xD0, - 0xC7, 0x73, 0xAD, 0xA5, 0x77, 0xF5, 0xE7, 0x2D, 0x7A, 0xFD, 0x1D, 0xB0, 0xB3, 0x6C, 0xED, 0x3F, 0xEF, 0xED, 0xEF, 0x77, 0x7C, 0xD0, 0x19, 0x69, 0xB5, 0xFB, - 0xA2, 0x09, 0xFC, 0xA1, 0x6D, 0x58, 0x27, 0xD9, 0xF7, 0xDF, 0xBA, 0x1B, 0xCF, 0xCF, 0x6B, 0xF0, 0xCE, 0x72, 0x70, 0x24, 0xCE, 0x6B, 0xF2, 0x01, 0xA6, 0x2F, - 0x8E, 0xB9, 0xD5, 0x44, 0xA7, 0x1B, 0xC1, 0xC5, 0x9C, 0x91, 0xEE, 0x8F, 0x85, 0xA4, 0x3F, 0x96, 0xEE, 0xF3, 0x8C, 0x93, 0x60, 0xC5, 0xBB, 0x25, 0x96, 0xA5, - 0xEE, 0xE2, 0xC6, 0x11, 0xE5, 0x74, 0xBC, 0x6A, 0xB0, 0xA5, 0xFF, 0x54, 0xC0, 0xE2, 0x59, 0xD2, 0x56, 0x2D, 0x5C, 0x25, 0x11, 0x95, 0x66, 0x58, 0xB9, 0x19, - 0x69, 0xB2, 0x60, 0x9C, 0x9E, 0x2D, 0x27, 0xB3, 0xCF, 0x8B, 0x0D, 0xF8, 0xF8, 0x4A, 0x44, 0x54, 0x76, 0x0D, 0xA7, 0x9C, 0x61, 0xE8, 0x87, 0x29, 0x68, 0xDE, - 0x78, 0x04, 0xB7, 0x63, 0x23, 0x18, 0x9F, 0xAF, 0x16, 0x00, 0xD0, 0x49, 0x6A, 0x38, 0x8A, 0x69, 0x08, 0xB5, 0x4D, 0xB6, 0x24, 0x44, 0x40, 0xBB, 0xFD, 0x30, - 0x62, 0x21, 0x10, 0x9F, 0x75, 0x45, 0xBA, 0xDB, 0x9E, 0x5D, 0xA7, 0x03, 0xD6, 0xD6, 0xAC, 0xFA, 0x2E, 0xA6, 0x2D, 0xF1, 0x70, 0x4E, 0xC4, 0x0F, 0xC9, 0x67, - 0x9E, 0xC4, 0x99, 0x17, 0x55, 0x82, 0x02, 0x88, 0x4F, 0x34, 0xC7, 0x8F, 0xB3, 0x4F, 0x14, 0xD9, 0x27, 0x9C, 0x7D, 0x04, 0x88, 0x26, 0x9C, 0xC5, 0x25, 0x8B, - 0xD0, 0x18, 0x7F, 0x7A, 0x15, 0x71, 0x76, 0x3D, 0xCD, 0xA5, 0x93, 0x97, 0x12, 0x62, 0xEC, 0xE5, 0x03, 0x40, 0xFB, 0x15, 0xF8, 0x43, 0x9C, 0xAD, 0xEB, 0xA9, - 0x1A, 0x5B, 0xA2, 0x14, 0x81, 0x00, 0x11, 0x5B, 0xF2, 0x82, 0x85, 0x60, 0xE5, 0x35, 0x09, 0xF8, 0x23, 0x7C, 0x86, 0x63, 0x6A, 0x73, 0xCF, 0x58, 0x11, 0xFC, - 0x62, 0x7B, 0x48, 0xAC, 0x49, 0xEF, 0x17, 0xE6, 0x7E, 0xAC, 0x59, 0x8C, 0xC9, 0xB0, 0xE4, 0x51, 0x08, 0x1A, 0xB6, 0x8C, 0x41, 0x87, 0x74, 0xE4, 0x42, 0x8B, - 0x46, 0x2C, 0x83, 0x0B, 0x4F, 0x95, 0x84, 0x15, 0xB6, 0x8E, 0x1C, 0x21, 0x42, 0x20, 0x4A, 0x26, 0xA3, 0x74, 0xA9, 0x8C, 0xCD, 0x24, 0x18, 0xB3, 0xA9, 0xF9, - 0x42, 0xBC, 0x41, 0xC8, 0x52, 0xA2, 0x4D, 0xE8, 0x20, 0x0C, 0x3E, 0x8B, 0xCC, 0x42, 0x52, 0xD8, 0x42, 0xA2, 0xFE, 0xDE, 0x26, 0x58, 0x9E, 0xE0, 0x2F, 0x65, - 0xBC, 0xF8, 0xE6, 0x6B, 0xCD, 0xF5, 0x34, 0xDB, 0xBD, 0x26, 0xB8, 0x28, 0x28, 0x36, 0x90, 0x69, 0x53, 0x02, 0x01, 0x8E, 0xB0, 0x22, 0x13, 0xEE, 0x8F, 0x09, - 0x96, 0x96, 0x0F, 0x73, 0x62, 0x7C, 0x75, 0x28, 0x79, 0xAA, 0x87, 0xA3, 0x53, 0x21, 0x7B, 0xDB, 0x25, 0xF4, 0x84, 0x38, 0x19, 0x4C, 0x24, 0xCB, 0xA7, 0x9C, - 0xC7, 0xAD, 0xC0, 0x92, 0x57, 0xD6, 0x2A, 0x21, 0xC2, 0xF0, 0xF6, 0xA3, 0x95, 0xA2, 0x9C, 0x81, 0x42, 0x41, 0x86, 0x60, 0x91, 0x2C, 0x23, 0x5E, 0xB7, 0xA4, - 0x29, 0xAB, 0x1D, 0xE6, 0x68, 0x14, 0x4B, 0xE1, 0xD2, 0x68, 0x9E, 0xAD, 0x15, 0x26, 0x71, 0x36, 0xCA, 0xB1, 0xDF, 0xD9, 0xA1, 0x78, 0x14, 0x8A, 0x9D, 0x61, - 0xF6, 0x36, 0x79, 0x72, 0x76, 0xB8, 0x0C, 0x56, 0xF6, 0xE4, 0xC9, 0xFF, 0x02, 0x49, 0x60, 0xC8, 0xA8, 0x55, 0x0C, 0x01, 0x00 +//File: index_ov3660.html.gz, Size: 8636 +#define index_ov3660_html_gz_len 8636 +const unsigned char index_ov3660_html_gz[] = { + 0x1F, 0x8B, 0x08, 0x08, 0xD3, 0xA3, 0x7B, 0x67, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x6F, 0x76, 0x33, 0x36, 0x36, 0x30, 0x2E, 0x68, 0x74, 0x6D, + 0x6C, 0x00, 0xED, 0x3D, 0x69, 0x73, 0xDB, 0x46, 0xB2, 0xDF, 0xFD, 0x2B, 0x60, 0x66, 0xD7, 0xA2, 0xCA, 0x22, 0x45, 0xF0, 0xD2, 0x61, 0x89, 0x7E, 0xB6, 0xAC, + 0xD8, 0xA9, 0xB5, 0xB3, 0xDE, 0x28, 0x71, 0x92, 0xDA, 0xDA, 0x72, 0x40, 0x62, 0x48, 0x22, 0x06, 0x01, 0x2E, 0x00, 0xEA, 0x58, 0x97, 0x7E, 0xC7, 0xFB, 0x41, + 0xEF, 0x8F, 0xBD, 0xEE, 0x39, 0x70, 0x71, 0x00, 0x0C, 0x00, 0x92, 0x52, 0xF2, 0x1E, 0x5D, 0x65, 0xE1, 0x98, 0xEE, 0xE9, 0x7B, 0x7A, 0x7A, 0x06, 0xC0, 0xD9, + 0x53, 0xD3, 0x9D, 0x04, 0x77, 0x4B, 0xA2, 0xCD, 0x83, 0x85, 0x3D, 0x7A, 0x72, 0xC6, 0xFE, 0x68, 0xF0, 0x3B, 0x9B, 0x13, 0xC3, 0x64, 0x87, 0xF4, 0x74, 0x41, + 0x02, 0x43, 0x9B, 0xCC, 0x0D, 0xCF, 0x27, 0xC1, 0x79, 0x63, 0x15, 0x4C, 0x5B, 0xC7, 0x8D, 0xF4, 0x6D, 0xC7, 0x58, 0x90, 0xF3, 0xC6, 0xB5, 0x45, 0x6E, 0x96, + 0xAE, 0x17, 0x34, 0xB4, 0x89, 0xEB, 0x04, 0xC4, 0x81, 0xE6, 0x37, 0x96, 0x19, 0xCC, 0xCF, 0x4D, 0x72, 0x6D, 0x4D, 0x48, 0x8B, 0x9E, 0x1C, 0x58, 0x8E, 0x15, + 0x58, 0x86, 0xDD, 0xF2, 0x27, 0x86, 0x4D, 0xCE, 0xF5, 0x38, 0xAE, 0xC0, 0x0A, 0x6C, 0x32, 0xBA, 0xBC, 0xFA, 0xD8, 0xEB, 0x6A, 0x7F, 0xFF, 0xD4, 0x1B, 0x0E, + 0x3B, 0x67, 0x87, 0xEC, 0x5A, 0xD4, 0xC6, 0x0F, 0xEE, 0xE2, 0xE7, 0xF8, 0x1B, 0xBB, 0xE6, 0x9D, 0xF6, 0x35, 0x71, 0x09, 0x7F, 0x53, 0x20, 0xA2, 0x35, 0x35, + 0x16, 0x96, 0x7D, 0x77, 0xAA, 0xBD, 0xF2, 0xA0, 0xCF, 0x83, 0x77, 0xC4, 0xBE, 0x26, 0x81, 0x35, 0x31, 0x0E, 0x7C, 0xC3, 0xF1, 0x5B, 0x3E, 0xF1, 0xAC, 0xE9, + 0x8B, 0x35, 0xC0, 0xB1, 0x31, 0xF9, 0x32, 0xF3, 0xDC, 0x95, 0x63, 0x9E, 0x6A, 0xDF, 0xE8, 0xC7, 0xF8, 0x6F, 0xBD, 0xD1, 0xC4, 0xB5, 0x5D, 0x0F, 0xEE, 0x5F, + 0x7E, 0x8B, 0xFF, 0xD6, 0xEF, 0xD3, 0xDE, 0x7D, 0xEB, 0x3F, 0xE4, 0x54, 0xD3, 0x87, 0xCB, 0xDB, 0xC4, 0xFD, 0xFB, 0x27, 0x89, 0xD3, 0x79, 0x37, 0x8B, 0x7A, + 0x0E, 0x7F, 0x9C, 0x0F, 0xEF, 0x93, 0x49, 0x60, 0xB9, 0x4E, 0x7B, 0x61, 0x58, 0x8E, 0x04, 0x93, 0x69, 0xF9, 0x4B, 0xDB, 0x00, 0x19, 0x4C, 0x6D, 0x92, 0x8B, + 0xE7, 0x9B, 0x05, 0x71, 0x56, 0x07, 0x05, 0xD8, 0x10, 0x49, 0xCB, 0xB4, 0x3C, 0xD6, 0xEA, 0x14, 0xE5, 0xB0, 0x5A, 0x38, 0x85, 0x68, 0xF3, 0xE8, 0x72, 0x5C, + 0x87, 0x48, 0x04, 0x88, 0x1D, 0xDD, 0x78, 0xC6, 0x12, 0x1B, 0xE0, 0xDF, 0xF5, 0x26, 0x0B, 0xCB, 0x61, 0x46, 0x75, 0xAA, 0xF5, 0xFA, 0x9D, 0xE5, 0x6D, 0x81, + 0x2A, 0x7B, 0x43, 0xFC, 0xB7, 0xDE, 0x68, 0x69, 0x98, 0xA6, 0xE5, 0xCC, 0x4E, 0xB5, 0x63, 0x29, 0x0A, 0xD7, 0x33, 0x89, 0xD7, 0xF2, 0x0C, 0xD3, 0x5A, 0xF9, + 0xA7, 0x5A, 0x5F, 0xD6, 0x66, 0x61, 0x78, 0x33, 0xA0, 0x25, 0x70, 0x81, 0xD8, 0x96, 0x2E, 0xA5, 0x84, 0x37, 0xF1, 0xAC, 0xD9, 0x3C, 0x00, 0x95, 0xAE, 0xB5, + 0x49, 0x0B, 0x8D, 0xBB, 0x50, 0x91, 0x3E, 0x73, 0xE5, 0x26, 0x97, 0x9A, 0x61, 0x5B, 0x33, 0xA7, 0x65, 0x05, 0x64, 0x01, 0xEC, 0xF8, 0x81, 0x47, 0x82, 0xC9, + 0x3C, 0x8F, 0x94, 0xA9, 0x35, 0x5B, 0x79, 0x44, 0x42, 0x48, 0x28, 0xB7, 0x1C, 0x86, 0xE1, 0xE6, 0xFA, 0xAD, 0xD6, 0x0D, 0x19, 0x7F, 0xB1, 0x82, 0x16, 0x97, + 0xC9, 0x98, 0x4C, 0x5D, 0x8F, 0x48, 0x5B, 0x8A, 0x16, 0xB6, 0x3B, 0xF9, 0xD2, 0xF2, 0x03, 0xC3, 0x0B, 0x54, 0x10, 0x1A, 0xD3, 0x80, 0x78, 0xC5, 0xF8, 0x08, + 0x5A, 0x45, 0x31, 0xB6, 0xEC, 0x6E, 0x79, 0x03, 0xCB, 0xB1, 0x2D, 0x87, 0xA8, 0x93, 0x97, 0xD5, 0x6F, 0x12, 0x1D, 0x6B, 0xA5, 0xA0, 0x18, 0x6B, 0x31, 0xCB, + 0xB3, 0x12, 0xCA, 0xEB, 0x7A, 0x67, 0xDC, 0x6F, 0xF4, 0x4E, 0xE7, 0xAF, 0xEB, 0x37, 0xE7, 0x84, 0x99, 0xA9, 0xB1, 0x0A, 0xDC, 0xFA, 0x1E, 0xB1, 0xE6, 0x56, + 0x29, 0x3E, 0xFE, 0x6B, 0x41, 0x4C, 0xCB, 0xD0, 0x9A, 0x31, 0x77, 0x3E, 0xEE, 0x80, 0x4D, 0xED, 0x6B, 0x86, 0x63, 0x6A, 0x4D, 0xD7, 0xB3, 0xC0, 0x11, 0x0C, + 0x1A, 0x6E, 0x6C, 0xB8, 0x02, 0x03, 0xC7, 0x92, 0xEC, 0x4B, 0x58, 0xCE, 0xF1, 0x99, 0xB8, 0x44, 0xE4, 0x6E, 0x83, 0x3F, 0x85, 0x90, 0x83, 0xBF, 0x42, 0x07, + 0x92, 0xF0, 0x48, 0xD1, 0xE7, 0xE9, 0x2B, 0x4E, 0x61, 0x96, 0xCE, 0xF0, 0xB7, 0x30, 0x6E, 0x5B, 0xB9, 0xBA, 0x13, 0x8D, 0x84, 0x0E, 0x61, 0x98, 0x9D, 0x34, + 0xA1, 0xE9, 0xF5, 0x5C, 0x6B, 0x69, 0x18, 0x25, 0xF7, 0xE5, 0x30, 0x1C, 0xA9, 0x5C, 0xE5, 0xF8, 0x8B, 0x1B, 0x45, 0x09, 0x76, 0xE5, 0xAC, 0x46, 0xB1, 0x83, + 0xFD, 0x93, 0xD9, 0x10, 0xE3, 0x24, 0x33, 0x8A, 0xE0, 0x4F, 0x3D, 0x92, 0x44, 0xC8, 0x0A, 0xA3, 0x89, 0x04, 0x71, 0x76, 0x44, 0x59, 0xC3, 0x9B, 0xE5, 0xDD, + 0x12, 0xAC, 0xF9, 0x24, 0xA8, 0x46, 0x17, 0x09, 0xE2, 0x3C, 0x1A, 0x0A, 0xA3, 0x0C, 0xFE, 0xEE, 0x15, 0xF2, 0x8D, 0x6F, 0xC6, 0xAB, 0x20, 0x70, 0x1D, 0xBF, + 0xD6, 0x10, 0x95, 0xE5, 0x67, 0xBF, 0xAF, 0xFC, 0xC0, 0x9A, 0xDE, 0xB5, 0xB8, 0x4B, 0x83, 0x9F, 0x2D, 0x0D, 0x48, 0x21, 0xC7, 0x24, 0xB8, 0x21, 0x24, 0x3F, + 0xDD, 0x70, 0x8C, 0x6B, 0x88, 0x3B, 0xB3, 0x99, 0x2D, 0xB3, 0xBD, 0xC9, 0xCA, 0xF3, 0x31, 0x6F, 0x5B, 0xBA, 0x16, 0x20, 0xF6, 0xD6, 0x3B, 0x4E, 0xFA, 0xA0, + 0x62, 0x47, 0xAD, 0xC9, 0x58, 0xD2, 0x97, 0xBB, 0x0A, 0x50, 0xC6, 0x52, 0x4D, 0xB8, 0xC0, 0x8E, 0x15, 0xDC, 0x49, 0xEF, 0x71, 0x4F, 0x94, 0xDC, 0x11, 0x2E, + 0x98, 0x3B, 0x2C, 0x24, 0xE9, 0x3A, 0x9D, 0xCC, 0xC9, 0xE4, 0x0B, 0x31, 0x9F, 0x17, 0xA6, 0x61, 0x45, 0xE9, 0x61, 0xDB, 0x72, 0x96, 0xAB, 0xA0, 0x85, 0xE9, + 0xD4, 0x72, 0x2B, 0x3A, 0xA7, 0x06, 0x29, 0x58, 0xEC, 0x76, 0xF3, 0x92, 0x8A, 0xC1, 0xF2, 0x36, 0x5F, 0x08, 0x71, 0x62, 0x47, 0xB6, 0x31, 0x26, 0x76, 0x1E, + 0xC9, 0xDC, 0x19, 0x32, 0xC2, 0x2E, 0x8F, 0x55, 0xD9, 0xB9, 0x1B, 0xA5, 0x2C, 0x1A, 0xBC, 0xFA, 0x47, 0x7F, 0x55, 0x96, 0x23, 0x3D, 0x3E, 0x48, 0x5C, 0xF2, + 0x89, 0x0D, 0x0E, 0x96, 0x95, 0x7A, 0x43, 0x9B, 0x1B, 0xA0, 0x21, 0xB7, 0x03, 0xCF, 0x70, 0x66, 0x04, 0x62, 0xC1, 0xED, 0x81, 0x38, 0xCC, 0x9F, 0x18, 0x28, + 0xB1, 0x8F, 0xA1, 0x7A, 0x90, 0x3F, 0x11, 0x61, 0x01, 0xE1, 0x40, 0x6B, 0xB3, 0x83, 0x0A, 0x59, 0x49, 0x4C, 0xBF, 0xB9, 0x84, 0xE8, 0x52, 0xEB, 0x60, 0x89, + 0x89, 0xD4, 0x73, 0x92, 0xB6, 0x25, 0x4D, 0xF4, 0x0B, 0x43, 0x83, 0x98, 0xF2, 0x4D, 0xA7, 0x45, 0x93, 0xC6, 0xE9, 0xB4, 0xD7, 0xE9, 0xF5, 0x0B, 0x33, 0x27, + 0x29, 0x97, 0xA9, 0x89, 0xA3, 0x24, 0x74, 0x84, 0x61, 0x25, 0xD7, 0x08, 0x7C, 0xE3, 0x5A, 0x9A, 0xB4, 0xBB, 0xBE, 0xC5, 0x66, 0x6E, 0xC6, 0xD8, 0x87, 0xB9, + 0x5B, 0x20, 0x99, 0x7A, 0x71, 0x43, 0xEF, 0x4A, 0xE9, 0xA3, 0x29, 0x9D, 0xD4, 0x05, 0x84, 0x78, 0xE5, 0x64, 0x27, 0x34, 0x20, 0x6F, 0x12, 0x53, 0xB0, 0x34, + 0xA9, 0x0C, 0xC8, 0x6D, 0xD0, 0x32, 0xC9, 0xC4, 0xF5, 0x58, 0x36, 0x98, 0x31, 0x73, 0x4C, 0x29, 0xB2, 0xD8, 0x62, 0x4F, 0xE7, 0xEE, 0x35, 0xF1, 0x24, 0xC2, + 0x4A, 0x29, 0xB5, 0x7F, 0xD2, 0x37, 0x15, 0xB0, 0x19, 0x30, 0x3C, 0x4A, 0x65, 0x9F, 0x44, 0xD7, 0xD5, 0x27, 0xDD, 0x5C, 0x3F, 0x66, 0xE8, 0xDA, 0xE0, 0x33, + 0xC6, 0xD8, 0x26, 0x66, 0xCE, 0x68, 0x66, 0x92, 0xA9, 0xB1, 0xB2, 0x83, 0x02, 0xAB, 0x34, 0x3A, 0xF8, 0x2F, 0xAF, 0x47, 0x1A, 0x86, 0xFE, 0x89, 0x75, 0xA1, + 0x73, 0x1A, 0x38, 0xFE, 0x25, 0xE9, 0x53, 0xA4, 0x1A, 0xC6, 0x72, 0x49, 0x0C, 0x68, 0x35, 0x21, 0x59, 0x7A, 0x50, 0x9A, 0x62, 0xC8, 0xE3, 0xBC, 0xD2, 0xBC, + 0xBD, 0xD0, 0x61, 0xC3, 0xE4, 0xB1, 0x14, 0xCF, 0xA7, 0x53, 0x77, 0xB2, 0x92, 0x65, 0x35, 0x6A, 0x8E, 0xB7, 0x8E, 0xEF, 0x54, 0x88, 0xCC, 0xB7, 0x2D, 0xEA, + 0xFE, 0x2B, 0xC7, 0x41, 0x8D, 0xB6, 0x02, 0x0F, 0xD8, 0x94, 0x74, 0xA4, 0x26, 0xB8, 0x4A, 0x31, 0x2C, 0x21, 0xD8, 0xAC, 0xDA, 0x55, 0x2A, 0x4C, 0x49, 0xC2, + 0x69, 0x18, 0x69, 0x35, 0x88, 0x21, 0x96, 0x29, 0x50, 0xD5, 0x93, 0x4B, 0x30, 0x5F, 0x2D, 0x64, 0x79, 0x94, 0xE8, 0x4C, 0x87, 0x41, 0x9F, 0x75, 0xE7, 0xCD, + 0xC6, 0x46, 0xB3, 0x73, 0xD0, 0x39, 0xE8, 0xC1, 0x7F, 0x92, 0xF9, 0x4C, 0xBE, 0x71, 0x71, 0xF1, 0x66, 0x58, 0x5E, 0x2A, 0x44, 0x17, 0x97, 0x95, 0xB2, 0x82, + 0x7D, 0xA1, 0x2E, 0xD4, 0x3D, 0x29, 0x59, 0x5F, 0xD2, 0xDB, 0x05, 0xE3, 0x70, 0x86, 0x49, 0x97, 0x37, 0x44, 0x89, 0xB5, 0x94, 0x55, 0xF1, 0xC2, 0xFD, 0x4F, + 0x8B, 0x25, 0x21, 0xFF, 0xE7, 0xAD, 0x3D, 0x26, 0x8A, 0x3F, 0xB5, 0xA5, 0x97, 0x96, 0x8B, 0xFF, 0xD0, 0xB6, 0xD1, 0xC9, 0xD6, 0x7A, 0x8B, 0x67, 0x7D, 0x40, + 0xA1, 0x03, 0x73, 0x50, 0x0F, 0x26, 0xA3, 0x99, 0x99, 0x61, 0xAC, 0x4D, 0x05, 0x19, 0x4C, 0x2D, 0xDB, 0x6E, 0xD9, 0xEE, 0x4D, 0x71, 0x26, 0x92, 0x6F, 0xC9, + 0x6B, 0x76, 0x5A, 0x6C, 0xF2, 0x55, 0xA9, 0x5D, 0x41, 0xE4, 0xFA, 0x43, 0x50, 0xFB, 0xE7, 0x76, 0xB8, 0x5C, 0xD7, 0xA8, 0x36, 0x50, 0x54, 0xB0, 0xC7, 0x7A, + 0x1D, 0x29, 0x99, 0x12, 0xCB, 0x04, 0xF3, 0xA7, 0x3D, 0x37, 0x56, 0x30, 0x99, 0x57, 0x98, 0x7A, 0x46, 0x13, 0x23, 0x8F, 0xD8, 0x06, 0x66, 0xF0, 0x95, 0x2A, + 0x14, 0x85, 0xD3, 0xB7, 0x38, 0xB8, 0x0A, 0x27, 0x54, 0x74, 0x8F, 0xA7, 0xBA, 0xD4, 0x66, 0xB9, 0x43, 0x76, 0xAC, 0x96, 0x9B, 0x75, 0x41, 0xBA, 0x9F, 0xF4, + 0x0C, 0x79, 0xA3, 0x12, 0x11, 0x5D, 0x04, 0xED, 0x99, 0x47, 0xEE, 0x14, 0x98, 0x39, 0xE0, 0x7F, 0x4F, 0x59, 0xFD, 0xB8, 0x7A, 0xA9, 0x84, 0x0E, 0x00, 0xDC, + 0x8A, 0xDA, 0x7D, 0x5F, 0xA1, 0xEB, 0xEC, 0x2E, 0x55, 0xEC, 0x31, 0xAC, 0x8E, 0x36, 0x1A, 0x0A, 0xE1, 0x26, 0x67, 0x08, 0x95, 0x9B, 0xAA, 0x18, 0x7D, 0xE5, + 0xF3, 0x79, 0x32, 0x0D, 0x32, 0x16, 0x7F, 0x68, 0x9E, 0xDA, 0xCB, 0x8F, 0x6E, 0xAD, 0x58, 0x35, 0xA5, 0x30, 0x72, 0x84, 0x45, 0xCC, 0x6C, 0xEB, 0x93, 0x62, + 0xC6, 0xE8, 0x59, 0x1A, 0x79, 0xB6, 0x4A, 0x44, 0xFA, 0x4C, 0xD5, 0x0C, 0x6D, 0x16, 0x7C, 0xC8, 0x07, 0xF5, 0x90, 0x5F, 0x9A, 0xDD, 0xA1, 0x74, 0x6D, 0x25, + 0xA7, 0x71, 0x1E, 0x69, 0x99, 0x55, 0xC0, 0xF5, 0x21, 0x2B, 0x73, 0x82, 0x1C, 0x8F, 0x45, 0x52, 0x45, 0xE5, 0x7B, 0x65, 0x5E, 0x84, 0x59, 0xAF, 0x64, 0xE5, + 0x1A, 0xBB, 0xB5, 0x30, 0x20, 0xED, 0x45, 0x73, 0x35, 0x00, 0xA3, 0x4C, 0x7F, 0x2A, 0xE6, 0x1E, 0xAB, 0xB1, 0xEA, 0xC3, 0x4E, 0x41, 0x97, 0x13, 0xDB, 0xF5, + 0x6B, 0x16, 0xC0, 0xB2, 0xEB, 0x5F, 0xD2, 0x3B, 0x4A, 0x43, 0x77, 0xAE, 0x4F, 0xE5, 0xBB, 0x63, 0x4A, 0xE6, 0x7A, 0x47, 0x1A, 0x69, 0x73, 0xAB, 0x94, 0xB4, + 0x82, 0x46, 0xD7, 0x2F, 0x4F, 0xB5, 0x09, 0x91, 0x87, 0xD1, 0x64, 0xA1, 0x4E, 0xA5, 0x54, 0x9A, 0xAB, 0x87, 0xB9, 0x65, 0x9A, 0x24, 0xB7, 0x16, 0x8C, 0x73, + 0x5E, 0xC5, 0xE4, 0x01, 0xE9, 0x97, 0x15, 0xA5, 0xB6, 0xE2, 0x14, 0xB9, 0xDB, 0x1A, 0xF4, 0x6D, 0x7B, 0x0C, 0x1F, 0x68, 0xB2, 0x2A, 0xE9, 0xC9, 0x54, 0x24, + 0x97, 0x54, 0xA9, 0x73, 0x87, 0xB5, 0x56, 0x14, 0x19, 0xC8, 0x01, 0x5B, 0xAD, 0x47, 0xF3, 0x14, 0x55, 0x74, 0x21, 0xA5, 0xCD, 0xD7, 0x96, 0xF8, 0x32, 0x60, + 0x2B, 0x6B, 0x75, 0x65, 0x83, 0x4B, 0x6D, 0xD4, 0x02, 0xD2, 0xFD, 0x66, 0x8A, 0xE6, 0x81, 0x32, 0xA3, 0x1C, 0x22, 0xC3, 0x21, 0x46, 0x6C, 0xAE, 0x4A, 0xB6, + 0x2A, 0xEB, 0x1C, 0xE1, 0xF9, 0xD9, 0x61, 0x6C, 0x3B, 0xDC, 0xD9, 0x61, 0xB4, 0x73, 0xEF, 0x0C, 0xF7, 0xC4, 0xC5, 0x77, 0xCD, 0xF1, 0x8E, 0x26, 0xB6, 0xE1, + 0xFB, 0xE7, 0x0D, 0xDC, 0xDB, 0xD5, 0x48, 0x6E, 0xA2, 0x3B, 0x33, 0xAD, 0x6B, 0xCD, 0x32, 0xCF, 0x1B, 0xB6, 0x3B, 0x73, 0x53, 0xF7, 0xE8, 0x7D, 0xA6, 0x66, + 0x18, 0xC8, 0xCE, 0x1B, 0x89, 0x05, 0xC6, 0x06, 0x85, 0x8A, 0x2E, 0x35, 0x46, 0xCF, 0xBE, 0x39, 0x39, 0x3A, 0x1A, 0xBE, 0x78, 0xE6, 0x8C, 0xFD, 0x25, 0xFF, + 0xFF, 0x47, 0xB6, 0x1E, 0xCB, 0x36, 0xF5, 0xC1, 0xD8, 0x16, 0x04, 0x60, 0x7B, 0xFE, 0xD9, 0x21, 0x45, 0x9A, 0x22, 0xE4, 0x10, 0x28, 0xC9, 0xA0, 0x8D, 0xE7, + 0x3B, 0x32, 0xF2, 0x44, 0x13, 0x1F, 0x86, 0xF0, 0xB1, 0xE1, 0x49, 0x9A, 0xD0, 0x66, 0x2C, 0x9B, 0xA6, 0xB1, 0xA4, 0x41, 0x95, 0x32, 0x76, 0x6F, 0xD3, 0x1C, + 0x50, 0xA6, 0xB8, 0xC6, 0x78, 0x2B, 0x62, 0x66, 0x21, 0x04, 0x30, 0x0A, 0x8E, 0xAB, 0xAB, 0xD0, 0x46, 0xDA, 0x28, 0xA1, 0x02, 0x6C, 0x7C, 0x3B, 0xB1, 0xBF, + 0x08, 0xE5, 0x37, 0x84, 0x52, 0x1C, 0x37, 0x60, 0xB1, 0x32, 0xA3, 0xAB, 0x04, 0xAB, 0x1C, 0x26, 0xB6, 0x6E, 0xC8, 0xB8, 0x00, 0xD1, 0xB6, 0x28, 0x76, 0x76, + 0x2D, 0x1F, 0x13, 0xC5, 0x16, 0xD3, 0xAB, 0x00, 0x6E, 0x8C, 0x7E, 0xB9, 0x78, 0xFF, 0x37, 0xED, 0xC3, 0xBB, 0xFF, 0x48, 0x35, 0x54, 0x44, 0x14, 0x06, 0x69, + 0x85, 0x9E, 0x29, 0x18, 0xD3, 0x87, 0x90, 0x49, 0x83, 0x6B, 0x86, 0x62, 0xC0, 0xE1, 0xDE, 0x26, 0xCE, 0x2C, 0x98, 0x9F, 0x37, 0xF4, 0x06, 0xEE, 0x69, 0x11, + 0x67, 0xDD, 0x86, 0x86, 0x01, 0x9C, 0x1E, 0x5C, 0x1B, 0xF6, 0x0A, 0x8F, 0x3A, 0x2A, 0xBC, 0xAE, 0x9B, 0x96, 0xB4, 0x19, 0x8F, 0x2C, 0xA1, 0x8C, 0x63, 0x91, + 0x38, 0x29, 0xE5, 0xC6, 0xE8, 0x8A, 0x04, 0x67, 0x87, 0xEC, 0x56, 0x81, 0xD6, 0xF2, 0xFB, 0x06, 0x4F, 0x66, 0xE6, 0x90, 0x67, 0x42, 0x79, 0x8A, 0x9F, 0x7A, + 0xC6, 0x82, 0xA0, 0x54, 0x94, 0x34, 0x1F, 0xD7, 0x7A, 0x08, 0xD9, 0x18, 0xFD, 0x40, 0x68, 0x46, 0x04, 0x64, 0x28, 0x29, 0xFE, 0x8C, 0x27, 0xA9, 0x89, 0xFE, + 0x43, 0x7B, 0xE6, 0x8B, 0x52, 0x2D, 0x83, 0x99, 0xB9, 0x82, 0xDC, 0x9F, 0xB6, 0x5A, 0x5A, 0xEF, 0xC3, 0x47, 0xAD, 0xD5, 0x52, 0x68, 0xEC, 0x2E, 0xA9, 0x3B, + 0x71, 0xFD, 0xEB, 0x27, 0x8D, 0xD1, 0x3F, 0x7E, 0x79, 0xFB, 0xAA, 0xD9, 0xED, 0xF4, 0x8F, 0x6F, 0xF5, 0xC1, 0xB0, 0xBF, 0x7F, 0x76, 0xC8, 0x9A, 0x94, 0xC7, + 0x75, 0xDC, 0x18, 0x7D, 0x44, 0x42, 0x9A, 0xC7, 0xC3, 0x7E, 0x5D, 0x5C, 0x47, 0x88, 0xEB, 0xDD, 0x9B, 0xE6, 0x51, 0xB7, 0x73, 0xAB, 0x77, 0x8F, 0x3B, 0x35, + 0x50, 0x0D, 0x1B, 0xA3, 0x6F, 0x01, 0x93, 0x7E, 0x82, 0xA8, 0x3A, 0xE5, 0x50, 0xA1, 0x68, 0xBB, 0x15, 0x45, 0x3B, 0x68, 0x8C, 0x7E, 0x42, 0xD1, 0x42, 0xCE, + 0x8D, 0x3C, 0x74, 0xEA, 0xF0, 0xD0, 0x07, 0x97, 0xA1, 0xB8, 0x40, 0x14, 0xC0, 0x44, 0xB7, 0x8E, 0x68, 0x7B, 0x8D, 0x11, 0x8A, 0x03, 0x31, 0x81, 0x74, 0x6B, + 0x20, 0xEA, 0x42, 0xC0, 0x43, 0x9A, 0x80, 0x9C, 0xDB, 0xA3, 0xE1, 0x71, 0x0D, 0x4C, 0x3A, 0xB0, 0xF7, 0x09, 0x50, 0x1D, 0x83, 0xA4, 0x86, 0xB5, 0x04, 0x05, + 0xF1, 0x0C, 0x11, 0x0D, 0xFB, 0x9D, 0xDB, 0x7E, 0x1D, 0xAB, 0x01, 0xBF, 0x78, 0x87, 0x88, 0x00, 0xC9, 0x6D, 0xAF, 0x8E, 0x94, 0xC0, 0x29, 0x2E, 0xBE, 0xFB, + 0xB6, 0xD9, 0x07, 0xCE, 0xBA, 0x27, 0xC3, 0xEA, 0x78, 0xC0, 0x21, 0x80, 0x0E, 0xA4, 0xA5, 0x32, 0x0A, 0x70, 0x84, 0x7F, 0x20, 0x4F, 0x88, 0xA7, 0xDB, 0xAF, + 0xC1, 0x13, 0x58, 0x36, 0xC0, 0x23, 0x8E, 0xCA, 0x28, 0xC0, 0xA0, 0xDF, 0x51, 0x62, 0x10, 0x91, 0x7E, 0x54, 0x43, 0x30, 0x60, 0xCE, 0xFF, 0x40, 0x09, 0x03, + 0x92, 0x5B, 0xBD, 0x5F, 0xC3, 0x31, 0xC0, 0x9C, 0xC1, 0x29, 0x30, 0xDA, 0x54, 0x37, 0x3F, 0xA0, 0x85, 0x72, 0x05, 0x3E, 0x8F, 0x2E, 0x5F, 0x9D, 0x18, 0xB0, + 0xE3, 0x93, 0xE1, 0xED, 0xC9, 0x50, 0x0D, 0x01, 0x0E, 0x83, 0x38, 0xA4, 0xE4, 0x0D, 0x94, 0xF9, 0xE3, 0x68, 0xDE, 0x18, 0xF9, 0xEF, 0x15, 0x4C, 0x7D, 0x83, + 0xBB, 0xD2, 0x23, 0x24, 0x87, 0x03, 0x99, 0xB0, 0x03, 0xB5, 0xC1, 0x31, 0x46, 0x49, 0xB8, 0x0B, 0xAB, 0x31, 0xEA, 0x2B, 0x24, 0x21, 0x89, 0x2C, 0x95, 0xC2, + 0x26, 0xE8, 0xA7, 0x99, 0x11, 0x5A, 0x1E, 0xE6, 0x44, 0xE0, 0x0D, 0xBD, 0x46, 0x2C, 0x6A, 0x54, 0x1A, 0x7D, 0x25, 0xB4, 0x1A, 0xB7, 0x8D, 0xD1, 0xB0, 0x57, + 0x98, 0xB5, 0x54, 0x57, 0xC6, 0x98, 0x16, 0x59, 0x1C, 0xE2, 0xFB, 0xA5, 0xF5, 0x11, 0x81, 0x36, 0x46, 0xAF, 0xC3, 0xE3, 0x3A, 0x5A, 0x69, 0x15, 0x71, 0x4A, + 0x61, 0x33, 0xD4, 0x12, 0x23, 0x87, 0x69, 0xA6, 0xD5, 0xE3, 0xAA, 0x89, 0x34, 0xB3, 0x59, 0xC5, 0x6C, 0x53, 0x2F, 0x38, 0xC7, 0xF2, 0x0C, 0x3F, 0x28, 0xAD, + 0x15, 0x01, 0x08, 0xE3, 0x04, 0x3F, 0x7A, 0x30, 0x8D, 0x84, 0xA4, 0xFC, 0x09, 0xF4, 0xE1, 0x1B, 0xC1, 0x8A, 0xED, 0x77, 0x2B, 0xAD, 0x91, 0x08, 0x14, 0xD2, + 0x92, 0xF0, 0xB8, 0x96, 0x56, 0xEA, 0x84, 0xAF, 0x18, 0x39, 0x5C, 0x2F, 0x22, 0x84, 0xF5, 0xB7, 0xA4, 0x97, 0x22, 0x6A, 0x6B, 0xE9, 0x65, 0x6E, 0x78, 0xCB, + 0x4A, 0xE1, 0x2B, 0x84, 0x04, 0xAD, 0x88, 0xC3, 0x07, 0x73, 0x95, 0x88, 0x98, 0x3F, 0x81, 0xAF, 0x98, 0xC4, 0x71, 0x2D, 0xBF, 0xFC, 0x14, 0x98, 0xC3, 0x35, + 0x46, 0x6F, 0x48, 0xEB, 0x7B, 0x3C, 0xAA, 0xA3, 0x8E, 0x57, 0xAB, 0xC0, 0xAD, 0xA1, 0x10, 0x41, 0x0B, 0x53, 0x47, 0x87, 0x6B, 0xE3, 0x78, 0x4B, 0xDA, 0x38, + 0xDE, 0xA2, 0x36, 0x0C, 0xF2, 0xD9, 0x26, 0xD7, 0xC4, 0x2E, 0xAD, 0x0E, 0x01, 0xD8, 0x18, 0x5D, 0xDE, 0x2E, 0x5D, 0x1F, 0x9F, 0x22, 0x7A, 0x8F, 0xE7, 0xB5, + 0x9C, 0x64, 0x50, 0x43, 0x27, 0x21, 0x41, 0xDC, 0x47, 0x06, 0x5C, 0x2B, 0x83, 0x2D, 0x69, 0xA5, 0x88, 0xD6, 0x3A, 0x5A, 0x99, 0x19, 0x96, 0x33, 0x21, 0x96, + 0x8D, 0x4F, 0x34, 0x94, 0x55, 0x4C, 0x0C, 0xB6, 0x31, 0x7A, 0x1B, 0x9D, 0xD4, 0x51, 0x4C, 0xA7, 0x86, 0x5E, 0xE2, 0xF4, 0x24, 0xFD, 0x65, 0x00, 0x53, 0xF1, + 0x2D, 0xE9, 0x46, 0xD7, 0xB7, 0x39, 0xAA, 0x2C, 0xC9, 0xC4, 0x32, 0xEC, 0xCF, 0x64, 0x3A, 0x85, 0x69, 0x50, 0xF9, 0xA1, 0x25, 0x01, 0x0E, 0xE3, 0x0B, 0x3B, + 0xD7, 0x2E, 0xE9, 0x79, 0xE9, 0xA2, 0x5E, 0x0A, 0x5D, 0xF5, 0xCA, 0x5E, 0x7A, 0x4E, 0xC8, 0x97, 0xB7, 0x09, 0xAD, 0xA5, 0xB2, 0xA3, 0xC6, 0xE8, 0x7B, 0x37, + 0xA4, 0xB3, 0xFA, 0xB4, 0xF5, 0x7B, 0x32, 0xA3, 0xAB, 0xC7, 0x75, 0x66, 0xCF, 0x6F, 0x3D, 0xE3, 0x8E, 0xBE, 0x9E, 0xA0, 0xCE, 0x5C, 0xFE, 0x07, 0x62, 0x6A, + 0x3F, 0x5A, 0x4E, 0x75, 0x66, 0xFA, 0x48, 0x08, 0x21, 0x4E, 0x3D, 0x2C, 0x03, 0x98, 0x22, 0xC1, 0x41, 0x3D, 0x24, 0x43, 0x2C, 0x74, 0x2F, 0x2D, 0xE3, 0x31, + 0x4C, 0xE2, 0x8D, 0x9B, 0x71, 0xF9, 0x01, 0xE5, 0x66, 0x0C, 0xE3, 0xF2, 0xCF, 0xAF, 0xB5, 0x4B, 0xBA, 0xDF, 0xB9, 0x74, 0xB8, 0x62, 0x5B, 0xB1, 0x54, 0x0C, + 0x3D, 0x5A, 0xCF, 0xC0, 0x3E, 0xD7, 0x16, 0x9A, 0xE4, 0x0E, 0xA4, 0xBA, 0xD8, 0x24, 0x61, 0x4F, 0x10, 0x48, 0x77, 0xAE, 0x34, 0x62, 0xDC, 0xAA, 0xF1, 0xB8, + 0xC5, 0x54, 0x6C, 0x72, 0x53, 0x3E, 0x0D, 0x9B, 0xDC, 0x80, 0x9A, 0xCC, 0x6B, 0xDC, 0x0A, 0x6F, 0x6A, 0xA0, 0xAF, 0x9D, 0x28, 0x0A, 0x7B, 0x7D, 0x18, 0x45, + 0x51, 0x7E, 0x1F, 0x5A, 0x51, 0x60, 0x2D, 0x9F, 0x71, 0x1C, 0xAD, 0xE2, 0x54, 0x14, 0xB0, 0x31, 0xFA, 0x60, 0x38, 0x2B, 0x18, 0x64, 0x76, 0xA5, 0xB0, 0xB0, + 0xE3, 0x07, 0x73, 0x2F, 0xCE, 0xF7, 0x43, 0xAB, 0x0E, 0x08, 0x59, 0xB8, 0x66, 0xF9, 0xE9, 0x0E, 0x87, 0x63, 0x21, 0xF1, 0x03, 0x1C, 0x95, 0x4E, 0x0C, 0x04, + 0x86, 0x2D, 0x67, 0x04, 0x6C, 0x2A, 0x55, 0x3D, 0x19, 0xB8, 0x5A, 0x39, 0xCE, 0x5D, 0x9D, 0x4C, 0xE0, 0xC2, 0x76, 0x57, 0x66, 0x75, 0x0C, 0x90, 0x06, 0xFC, + 0x7D, 0x3A, 0xB5, 0x26, 0xD5, 0x13, 0x09, 0x5C, 0x5E, 0x70, 0x17, 0x8A, 0xF0, 0x5B, 0x1E, 0x78, 0xC9, 0xA4, 0xC2, 0x4C, 0x6E, 0x02, 0x5A, 0xBC, 0xBC, 0xD8, + 0xE9, 0xC0, 0x0B, 0x7D, 0x3E, 0x50, 0x64, 0x40, 0x6E, 0x1F, 0x3A, 0x28, 0x00, 0x11, 0x9F, 0xA9, 0xF1, 0x54, 0x51, 0x16, 0x83, 0x0C, 0x23, 0xBA, 0x98, 0x7E, + 0x3F, 0xD4, 0xFC, 0x2E, 0xA2, 0x28, 0x39, 0xBB, 0xD3, 0x07, 0xBD, 0x61, 0x38, 0xBD, 0xEB, 0x75, 0x37, 0x3B, 0xC1, 0x43, 0xE4, 0xDB, 0xD5, 0x4F, 0xB7, 0x8A, + 0x6A, 0x20, 0x1A, 0x7D, 0x8F, 0xEB, 0x0C, 0x25, 0x02, 0x76, 0x7D, 0x47, 0xEA, 0x3E, 0x9C, 0x27, 0x75, 0x1F, 0x81, 0x2B, 0xCD, 0x2A, 0x44, 0xBC, 0x19, 0x46, + 0xBC, 0xB7, 0x17, 0xBB, 0xD1, 0xD0, 0xEC, 0xC1, 0x42, 0xDD, 0xEC, 0x41, 0x43, 0x9D, 0xC6, 0x77, 0xCA, 0x09, 0x29, 0x54, 0xCC, 0x60, 0x39, 0x20, 0xAB, 0x65, + 0xD5, 0x09, 0x72, 0xFA, 0x6D, 0x9D, 0x28, 0x27, 0xC8, 0x48, 0x06, 0xB9, 0x61, 0xB4, 0x2A, 0x32, 0xD8, 0xEC, 0xB2, 0x6E, 0xBF, 0x88, 0xDA, 0x3A, 0x4E, 0xE3, + 0x19, 0x37, 0x9F, 0x67, 0x0B, 0xA3, 0xB4, 0x32, 0x38, 0x1C, 0xE8, 0xE2, 0xC3, 0xAB, 0x5D, 0xA6, 0x0B, 0xA2, 0xDF, 0x87, 0xF1, 0xA3, 0x90, 0xEB, 0x87, 0x8E, + 0x75, 0x36, 0x71, 0xCA, 0x07, 0x3B, 0x04, 0x6A, 0x8C, 0xDE, 0x13, 0xC7, 0xD7, 0x2E, 0x5C, 0x8F, 0xBF, 0x13, 0x72, 0x27, 0x5A, 0xA3, 0x3D, 0x3F, 0x8C, 0xCA, + 0x18, 0xD3, 0x0F, 0xAD, 0xAF, 0xF9, 0xC2, 0xF2, 0x3C, 0xD7, 0x2B, 0xAD, 0x32, 0x0E, 0x07, 0xD3, 0x8A, 0xD6, 0x07, 0x7A, 0xB4, 0x13, 0x75, 0x89, 0x5E, 0x1F, + 0x46, 0x63, 0x21, 0xCF, 0x0F, 0xAD, 0xB4, 0xEB, 0xA9, 0x6D, 0x2D, 0x4B, 0xAB, 0x8C, 0x42, 0x35, 0x46, 0x9F, 0x5A, 0xDF, 0xC2, 0xDF, 0x9D, 0xA8, 0x8B, 0xF5, + 0xF8, 0x30, 0xCA, 0xE2, 0xDC, 0x3E, 0xB4, 0xAA, 0xC6, 0xCB, 0xF2, 0xE1, 0x10, 0x60, 0x1A, 0xA3, 0xD7, 0x1F, 0x77, 0x93, 0xFB, 0x61, 0x67, 0x8A, 0x1A, 0xAA, + 0xA5, 0x0F, 0xCA, 0xD4, 0x43, 0x6B, 0xE3, 0xA6, 0x82, 0x36, 0x6E, 0x90, 0xF0, 0x9F, 0x77, 0xA4, 0x8D, 0x1B, 0x75, 0x6D, 0x6C, 0xD8, 0x5F, 0x6E, 0x1E, 0x83, + 0x7E, 0xE8, 0x43, 0x87, 0x63, 0xA3, 0xFC, 0x70, 0x24, 0x00, 0x71, 0xD3, 0x18, 0x1C, 0x69, 0xAF, 0x8D, 0xDD, 0x0C, 0x48, 0x61, 0xBF, 0xBB, 0x70, 0xA1, 0x88, + 0xC9, 0x87, 0xD6, 0x93, 0x4D, 0xCC, 0x0A, 0x49, 0x9E, 0xF9, 0x19, 0x9F, 0xE0, 0xC3, 0x27, 0xDB, 0xEF, 0x20, 0xDB, 0xBB, 0x7C, 0xA3, 0x7D, 0x27, 0x4E, 0x1F, + 0xAA, 0x30, 0x94, 0xA4, 0x29, 0x39, 0x6F, 0xEA, 0x0E, 0xB6, 0xB5, 0x2D, 0x03, 0x30, 0xD7, 0xD4, 0x4D, 0xFC, 0x21, 0x30, 0xFE, 0xFA, 0xD4, 0x22, 0x52, 0xF8, + 0xC3, 0x48, 0x74, 0xBB, 0x03, 0x09, 0x5A, 0x7E, 0x60, 0xD9, 0x36, 0x4C, 0x92, 0x48, 0xA0, 0x5D, 0xE1, 0xA1, 0xE2, 0xD3, 0x47, 0x31, 0x2C, 0xE2, 0xD9, 0xC3, + 0xC0, 0x23, 0xC6, 0xA2, 0x31, 0xBA, 0xC2, 0x17, 0xCB, 0x02, 0x2E, 0x3C, 0x2B, 0x46, 0x16, 0x7F, 0x4E, 0x29, 0xDF, 0x04, 0xE9, 0x93, 0x89, 0xF8, 0xA8, 0x61, + 0xF2, 0x45, 0xD0, 0xE0, 0x03, 0xEC, 0xC1, 0xE3, 0xD1, 0x99, 0xBF, 0x34, 0x1C, 0xD1, 0x8C, 0x3E, 0x95, 0x7B, 0xC3, 0x1F, 0xB3, 0x1C, 0xBB, 0xB6, 0xF9, 0x22, + 0xB6, 0x12, 0x78, 0x15, 0x3E, 0x2F, 0x88, 0x20, 0xE0, 0x44, 0x02, 0x43, 0x81, 0xB4, 0xE7, 0x9E, 0x40, 0xCF, 0x1E, 0xED, 0xC4, 0xD7, 0x08, 0xE5, 0x88, 0x3B, + 0xE3, 0x11, 0x47, 0x8F, 0xCC, 0x42, 0x2B, 0x92, 0x3D, 0xFA, 0x2A, 0x7D, 0xE0, 0xF1, 0x07, 0x32, 0xB3, 0x7C, 0xA0, 0x51, 0x03, 0x45, 0x1D, 0xD2, 0x87, 0xC4, + 0x98, 0xA3, 0xA8, 0x3D, 0x80, 0x18, 0xEF, 0x92, 0x3F, 0x3F, 0x2D, 0x7D, 0xAE, 0xB4, 0xD4, 0x58, 0x92, 0x7E, 0x08, 0x34, 0x89, 0xB1, 0xC8, 0x0C, 0x9F, 0xB6, + 0x5A, 0xF3, 0x3E, 0x3E, 0xEE, 0xA6, 0x09, 0xD6, 0xCE, 0x0E, 0xE7, 0xFD, 0xA2, 0xC7, 0x89, 0x0A, 0x9F, 0x55, 0x04, 0x4E, 0x2B, 0x3F, 0xAA, 0x88, 0x52, 0x1A, + 0x01, 0x35, 0x07, 0xDA, 0x07, 0xC3, 0xFF, 0x72, 0xA0, 0x7D, 0x42, 0x9F, 0xDF, 0xE1, 0x13, 0x8B, 0x48, 0xBB, 0x61, 0x9A, 0x5E, 0xE6, 0x53, 0x8B, 0xFD, 0xC4, + 0x53, 0x8B, 0x43, 0xF1, 0xD4, 0x62, 0x54, 0xBA, 0xEE, 0xDC, 0xF6, 0x3A, 0x9D, 0x63, 0x15, 0xD6, 0x15, 0x9F, 0x5C, 0xDC, 0x08, 0x4F, 0x0B, 0x90, 0xA6, 0x22, + 0x4F, 0x7D, 0xC1, 0x53, 0x6C, 0x07, 0xEF, 0xED, 0x74, 0xFA, 0xD8, 0x38, 0xE2, 0x6B, 0x08, 0xD5, 0x59, 0xEA, 0x74, 0x77, 0xFD, 0x78, 0x29, 0x35, 0xEE, 0x4D, + 0x3D, 0x5D, 0x4A, 0x9B, 0xA4, 0xA3, 0xE1, 0x20, 0x37, 0x18, 0x52, 0x10, 0xE6, 0xF4, 0x6F, 0x37, 0xE9, 0xF4, 0xB3, 0x1A, 0x4E, 0x3F, 0x5B, 0x73, 0xFA, 0x1D, + 0x7A, 0xBB, 0x20, 0xFC, 0xCF, 0xE6, 0xF1, 0x82, 0xAF, 0x12, 0x5E, 0x2F, 0xE5, 0xAB, 0xD3, 0xD9, 0xA8, 0xDF, 0x17, 0x3A, 0x49, 0x68, 0x0C, 0x6F, 0x37, 0xE9, + 0x24, 0x19, 0xA6, 0x5B, 0xC9, 0x4E, 0x79, 0xD8, 0x19, 0xED, 0x66, 0x5C, 0xA2, 0xD9, 0x54, 0x5C, 0xA1, 0xBC, 0x77, 0x7C, 0x7E, 0xAF, 0xD7, 0xE7, 0xA9, 0xD3, + 0x26, 0xD4, 0xA3, 0xFE, 0x24, 0x7B, 0x66, 0x93, 0xCD, 0x24, 0x66, 0x4B, 0x48, 0x84, 0x4B, 0x27, 0x66, 0x1F, 0xDF, 0xBF, 0x2F, 0x97, 0x8B, 0xC5, 0x7B, 0x79, + 0x24, 0xB9, 0x58, 0x6E, 0xDD, 0xEA, 0x6E, 0x09, 0x37, 0x90, 0xEA, 0x4A, 0xA6, 0x1B, 0x81, 0x37, 0x46, 0xAF, 0xE9, 0xB1, 0x16, 0x93, 0x58, 0x29, 0xE3, 0x55, + 0x9E, 0x96, 0x53, 0xC0, 0x58, 0x61, 0x2B, 0x22, 0x21, 0xAD, 0x1B, 0x45, 0x5C, 0x39, 0xC5, 0xAC, 0x18, 0x7B, 0xEA, 0x4C, 0xD5, 0xF6, 0x09, 0xDA, 0xA4, 0x28, + 0x15, 0x5E, 0xAC, 0xEC, 0xCA, 0x6A, 0xE3, 0xB0, 0x8D, 0xD1, 0x07, 0x98, 0xE3, 0x5A, 0x4B, 0xDB, 0x82, 0x99, 0x47, 0xB3, 0xA3, 0xB5, 0xB4, 0x9E, 0xBE, 0xBF, + 0xC3, 0x31, 0x52, 0x90, 0x51, 0xF2, 0x35, 0x1E, 0x7A, 0xF4, 0x74, 0x4B, 0x6F, 0x43, 0xEF, 0xF1, 0xA8, 0xAB, 0x10, 0xCF, 0x75, 0x83, 0xCA, 0xDA, 0x10, 0xC0, + 0x90, 0xA8, 0xC0, 0x91, 0x16, 0xE9, 0x44, 0x5D, 0x15, 0xB1, 0xBD, 0x76, 0x11, 0x36, 0x35, 0x75, 0x28, 0xED, 0xAC, 0xC3, 0x05, 0x6B, 0xD5, 0x2D, 0x69, 0x12, + 0xAC, 0x7A, 0x63, 0xD4, 0x2D, 0x81, 0xA1, 0x78, 0x63, 0x1A, 0x6B, 0x55, 0xDF, 0x89, 0xFC, 0xBB, 0xEA, 0xB1, 0x8F, 0xC3, 0x42, 0xDA, 0x7D, 0x07, 0xA9, 0xEE, + 0x42, 0x7B, 0x03, 0x7D, 0x51, 0x27, 0xD2, 0x07, 0xBB, 0x74, 0x22, 0x41, 0x46, 0x75, 0x27, 0xD2, 0x1F, 0x87, 0x0F, 0xA1, 0x3E, 0x96, 0x1E, 0xA9, 0xAC, 0x0F, + 0x0E, 0xDB, 0x18, 0x7D, 0xF4, 0x08, 0x2A, 0xA3, 0x92, 0xF7, 0x84, 0x48, 0xAA, 0x39, 0xCF, 0x06, 0x1C, 0x45, 0x6F, 0x0F, 0xEA, 0xE1, 0xE8, 0x96, 0x73, 0x36, + 0x09, 0x86, 0x9E, 0x3C, 0x08, 0xF4, 0x1E, 0xA7, 0x0B, 0x13, 0xDB, 0x1C, 0x54, 0x77, 0x62, 0x01, 0x8D, 0xB3, 0x67, 0x38, 0xAC, 0x6C, 0x38, 0x31, 0x44, 0x8F, + 0x2A, 0xEE, 0xD6, 0xC4, 0xB0, 0x09, 0x63, 0x1A, 0x75, 0x4B, 0x99, 0xF4, 0x6E, 0x4C, 0x67, 0x89, 0x6F, 0x3D, 0x23, 0x6A, 0x9B, 0xC0, 0x28, 0xB2, 0x78, 0xA4, + 0x61, 0xB0, 0x10, 0x69, 0xE8, 0x7B, 0xCF, 0xE8, 0xE6, 0xD7, 0x9D, 0xE6, 0xBC, 0x82, 0x80, 0xF5, 0xD5, 0xA8, 0xF2, 0x6B, 0x85, 0x31, 0xE6, 0x64, 0x29, 0x70, + 0xC8, 0xEB, 0x63, 0xCB, 0x7F, 0x29, 0x61, 0x95, 0xC7, 0x0A, 0x0E, 0xCC, 0x55, 0x18, 0x0E, 0xDD, 0xBB, 0xCD, 0x7F, 0x43, 0x2A, 0x6A, 0x8C, 0xDD, 0x3B, 0x4C, + 0x80, 0x63, 0x8B, 0x42, 0x54, 0x01, 0x2C, 0x68, 0x06, 0x6C, 0xC6, 0x57, 0x62, 0x25, 0x28, 0xB3, 0xC9, 0x66, 0xE6, 0xF9, 0x37, 0x96, 0x53, 0x7E, 0x9E, 0xFF, + 0xB3, 0xE5, 0x98, 0xEE, 0x4D, 0xB9, 0xA9, 0x7E, 0xBC, 0xA3, 0x3F, 0xC0, 0x54, 0x9F, 0x0E, 0x96, 0xB8, 0x7A, 0xD7, 0xF2, 0x88, 0xDA, 0x5B, 0x28, 0xD2, 0x42, + 0x66, 0xD0, 0xB7, 0xB8, 0xD4, 0x06, 0x28, 0x7C, 0x8D, 0xAE, 0x05, 0x6E, 0xDB, 0x5F, 0x7E, 0x39, 0x8D, 0x27, 0xBB, 0x9C, 0x02, 0x35, 0x87, 0xE9, 0x4B, 0x0A, + 0x8F, 0x0F, 0x5E, 0x4B, 0xFD, 0x75, 0x9D, 0x9F, 0xBB, 0x07, 0xE7, 0x67, 0x13, 0x01, 0x99, 0x38, 0x66, 0x65, 0xCB, 0x42, 0xD8, 0xC8, 0xAE, 0x2E, 0x1D, 0x73, + 0xA7, 0x56, 0xC5, 0x7A, 0xAF, 0xAC, 0x83, 0x6E, 0xE7, 0xE8, 0xE4, 0x71, 0x99, 0x15, 0x32, 0x54, 0xC3, 0xA8, 0xF4, 0x41, 0xFF, 0xE8, 0xF1, 0xD8, 0x95, 0x3B, + 0x9D, 0xB2, 0x15, 0xAE, 0x6A, 0xA6, 0xC5, 0xC1, 0x6F, 0xE9, 0xB3, 0x75, 0x3E, 0xD9, 0x6D, 0xBC, 0x0A, 0x3B, 0x57, 0xD3, 0x45, 0x4F, 0xA2, 0x8B, 0xE1, 0xE3, + 0x32, 0x2D, 0xCE, 0x91, 0xAA, 0x75, 0x49, 0x38, 0xDA, 0x10, 0x43, 0x9B, 0x30, 0xAD, 0xC0, 0x0D, 0x0C, 0xBB, 0xB2, 0x65, 0x31, 0x68, 0x30, 0xAC, 0x1F, 0xF1, + 0x40, 0xBB, 0x02, 0x3E, 0x77, 0x6A, 0x5C, 0xA2, 0xFF, 0xEA, 0x81, 0xAB, 0xD7, 0x79, 0x64, 0xE3, 0x21, 0x63, 0xA9, 0x56, 0xE8, 0x1A, 0xF6, 0x1F, 0x8F, 0x7D, + 0xB9, 0xAB, 0x00, 0xAF, 0x56, 0x0E, 0x5D, 0x0C, 0x1C, 0x43, 0x17, 0x3D, 0xDA, 0xBD, 0x89, 0x85, 0x14, 0xD4, 0x18, 0x1C, 0xFB, 0x0F, 0xBF, 0x7E, 0xFD, 0xAB, + 0x84, 0xA7, 0x5A, 0x46, 0xD6, 0x7B, 0x2C, 0x41, 0x6C, 0x62, 0x28, 0xBF, 0x99, 0x89, 0x22, 0x8B, 0x67, 0xF3, 0x0C, 0x16, 0xE6, 0x70, 0xEC, 0x60, 0xA7, 0x15, + 0x0C, 0xD1, 0xF9, 0xC6, 0x97, 0xEC, 0x42, 0xAE, 0x1E, 0x53, 0xBD, 0x62, 0x6C, 0x39, 0x4E, 0x55, 0x35, 0x71, 0xD8, 0xC6, 0xE8, 0x35, 0x3B, 0xD8, 0xED, 0xE2, + 0x2A, 0xEF, 0x7C, 0xF3, 0x2B, 0xAB, 0x82, 0xAB, 0x5D, 0xAB, 0x29, 0x55, 0xC4, 0xF0, 0xC2, 0x57, 0xD8, 0x37, 0xF8, 0x6E, 0xC5, 0xE8, 0x95, 0xF6, 0x8F, 0xA7, + 0xA4, 0x31, 0x33, 0x16, 0xF8, 0xC8, 0x61, 0xD9, 0xA2, 0xC6, 0x5B, 0x04, 0x2B, 0x57, 0xD3, 0x48, 0xF6, 0xF4, 0xB8, 0xAB, 0x1A, 0xA3, 0xE4, 0xBB, 0xE6, 0x80, + 0xF0, 0xD6, 0xD8, 0x32, 0x7C, 0x7C, 0x3C, 0x17, 0x8E, 0xB5, 0xD7, 0x70, 0xAC, 0x7D, 0xB4, 0x57, 0xE1, 0xCB, 0x32, 0x65, 0x0E, 0x11, 0xDF, 0xD9, 0x14, 0x61, + 0xC8, 0xDA, 0xE5, 0x4F, 0x37, 0x74, 0xF1, 0xC7, 0x32, 0xE0, 0x18, 0xF7, 0x31, 0x0D, 0xFA, 0xC7, 0x9D, 0x86, 0xC6, 0xB2, 0x62, 0xBE, 0xA9, 0xDC, 0xFF, 0x42, + 0x37, 0x38, 0xE9, 0x21, 0x81, 0x32, 0x07, 0x88, 0xD3, 0x1B, 0x12, 0x48, 0xED, 0xB7, 0xCE, 0xBE, 0xA3, 0x75, 0x89, 0xE8, 0x42, 0x1C, 0x1D, 0xA9, 0x21, 0x24, + 0xDE, 0x8E, 0xC7, 0xDA, 0xAB, 0x6C, 0x8F, 0x97, 0x0B, 0x42, 0x97, 0x0A, 0x02, 0xF7, 0x79, 0x6D, 0x96, 0xA7, 0xAE, 0xE0, 0x49, 0x57, 0xE3, 0xA9, 0x5B, 0x83, + 0xA7, 0xEE, 0x8E, 0x78, 0xEA, 0x09, 0x9E, 0xBA, 0x6A, 0x3C, 0xF5, 0x6A, 0xF0, 0xD4, 0xDB, 0x11, 0x4F, 0x7D, 0xC1, 0x53, 0x4F, 0x8D, 0xA7, 0x7E, 0x0D, 0x9E, + 0xFA, 0x3B, 0xE2, 0x69, 0x20, 0x78, 0xEA, 0xAB, 0xF1, 0x34, 0xA8, 0xC1, 0xD3, 0x60, 0x47, 0x3C, 0x0D, 0x05, 0x4F, 0x03, 0x35, 0x9E, 0x86, 0x35, 0x78, 0x1A, + 0xEE, 0x88, 0xA7, 0x23, 0xC1, 0xD3, 0x50, 0x8D, 0xA7, 0xA3, 0x1A, 0x3C, 0x1D, 0xED, 0x88, 0xA7, 0x63, 0xC1, 0xD3, 0x91, 0x1A, 0x4F, 0xC7, 0x35, 0x78, 0x3A, + 0xDE, 0x11, 0x4F, 0x27, 0x82, 0xA7, 0x63, 0x35, 0x9E, 0x4E, 0x6A, 0xF0, 0x74, 0xB2, 0x23, 0x9E, 0x70, 0x51, 0x8E, 0x31, 0x75, 0xA2, 0x38, 0xE8, 0x76, 0x6A, + 0x70, 0x65, 0xEC, 0x8A, 0xAB, 0x30, 0x95, 0xD0, 0x55, 0x73, 0x89, 0x3A, 0xC9, 0xC4, 0x78, 0x57, 0x6C, 0x45, 0xD9, 0x84, 0x62, 0x3A, 0xA1, 0xD7, 0xC9, 0x27, + 0x26, 0xBB, 0x62, 0x2B, 0x4C, 0x28, 0x74, 0xC5, 0x8C, 0x42, 0xAF, 0x93, 0x52, 0x98, 0xBB, 0x62, 0x2B, 0xCC, 0x29, 0x74, 0xC5, 0xA4, 0x42, 0xAF, 0x93, 0x55, + 0x90, 0x5D, 0xB1, 0x15, 0xA6, 0x15, 0xBA, 0x62, 0x5E, 0xA1, 0xD7, 0x49, 0x2C, 0xA6, 0xBB, 0x62, 0x2B, 0xCC, 0x2C, 0x74, 0xC5, 0xD4, 0x42, 0xAF, 0x91, 0x5B, + 0x9C, 0xC8, 0x27, 0x62, 0x1B, 0x65, 0x8B, 0x04, 0x7C, 0x8A, 0x1C, 0x4D, 0xDA, 0x94, 0x1E, 0x3D, 0xE1, 0x40, 0xF8, 0x6C, 0x14, 0x13, 0xC8, 0x85, 0xEB, 0x4C, + 0xAD, 0x59, 0x58, 0x64, 0x78, 0x34, 0x4F, 0x49, 0xF8, 0xB1, 0xD7, 0x74, 0x2A, 0x17, 0x1A, 0xAE, 0xDE, 0x5C, 0x96, 0x2B, 0x33, 0xC4, 0x7B, 0xF9, 0x03, 0x15, + 0x19, 0x80, 0xEC, 0x6E, 0xFC, 0x9D, 0xE1, 0x4A, 0x75, 0x05, 0x0A, 0x54, 0xA6, 0xA2, 0x30, 0x88, 0x57, 0x14, 0x86, 0xCA, 0x15, 0x05, 0x46, 0xDC, 0x76, 0x6A, + 0x09, 0x80, 0xBB, 0xC7, 0x5E, 0x74, 0xAE, 0xCE, 0x74, 0xAF, 0x3A, 0xD3, 0x83, 0x32, 0x4C, 0xF7, 0xAA, 0x30, 0x5D, 0xE1, 0xE9, 0x46, 0x45, 0x39, 0x01, 0xBD, + 0xDF, 0x5A, 0xB7, 0xC4, 0xD4, 0x7E, 0x55, 0x17, 0x95, 0x5E, 0x5D, 0x54, 0x47, 0x65, 0x44, 0xA5, 0x6F, 0xD1, 0x3E, 0x06, 0x82, 0xEF, 0x9F, 0xD4, 0xF9, 0x1E, + 0x54, 0xE7, 0xBB, 0x57, 0x86, 0xEF, 0xC1, 0x16, 0xF9, 0xEE, 0x0B, 0xBE, 0x3F, 0xA9, 0xF3, 0xDD, 0xAF, 0xCE, 0x77, 0xBF, 0x0C, 0xDF, 0xFD, 0x2D, 0xF2, 0xDD, + 0x85, 0x60, 0xF3, 0xD3, 0x27, 0xED, 0xC7, 0xB9, 0x47, 0xFC, 0x79, 0x71, 0x25, 0x8E, 0x41, 0x54, 0x1D, 0xDB, 0x07, 0x3B, 0x98, 0xBB, 0x21, 0x85, 0xBD, 0x38, + 0x4F, 0x85, 0x79, 0x33, 0x83, 0x50, 0xF9, 0x92, 0x88, 0x9C, 0x27, 0xF9, 0xCC, 0x4D, 0x57, 0x65, 0x6A, 0x7B, 0x31, 0xEC, 0xB8, 0x31, 0x7A, 0xB7, 0x2A, 0x31, + 0xBE, 0x1D, 0x57, 0xB7, 0x67, 0xF5, 0x8A, 0x39, 0xA3, 0x6B, 0x6B, 0xF6, 0x7C, 0x42, 0x79, 0x86, 0xBC, 0xCC, 0x57, 0x50, 0x7B, 0xF5, 0x2A, 0xC4, 0x60, 0x07, + 0x55, 0x72, 0x8C, 0xF4, 0x47, 0x8C, 0x9D, 0x9F, 0x90, 0x21, 0x0D, 0x32, 0x96, 0x12, 0x83, 0xD1, 0x51, 0x49, 0x6D, 0x1E, 0x57, 0x8C, 0x4E, 0x48, 0xE3, 0xD6, + 0xD4, 0x89, 0x53, 0x0F, 0x14, 0xC0, 0xA7, 0x0A, 0x02, 0x18, 0x56, 0x17, 0x40, 0xA9, 0xCC, 0x05, 0x69, 0xDC, 0x9E, 0x00, 0x3A, 0x4C, 0x00, 0x57, 0xD1, 0xAB, + 0x6A, 0x73, 0x0C, 0xBA, 0x46, 0x05, 0x6A, 0xB0, 0x83, 0x35, 0x12, 0x8C, 0xB4, 0xBA, 0xB0, 0x68, 0xE0, 0xA8, 0x9C, 0x42, 0xBB, 0x65, 0xF3, 0x2B, 0x79, 0xF1, + 0x53, 0x21, 0xFF, 0xDE, 0x66, 0x82, 0xD5, 0xED, 0x08, 0x8B, 0x2E, 0x2F, 0x80, 0x4E, 0x75, 0x01, 0xE8, 0xA5, 0x04, 0xD0, 0x79, 0x5C, 0xC9, 0xF8, 0x70, 0xFD, + 0xEB, 0xA2, 0xC5, 0xD2, 0x2A, 0xEB, 0xFE, 0xB1, 0xD1, 0xAC, 0x5B, 0x46, 0x58, 0x5B, 0xF5, 0xFE, 0x5E, 0xC4, 0xB9, 0xF6, 0xAB, 0x96, 0xDC, 0xFA, 0x9A, 0x17, + 0x07, 0xAA, 0x17, 0x01, 0x07, 0x3B, 0x58, 0xAF, 0x42, 0x0A, 0x4F, 0x24, 0x9C, 0x95, 0x0C, 0xF0, 0x27, 0xD5, 0xDD, 0xA1, 0x94, 0x86, 0x91, 0xD6, 0xED, 0xA9, + 0x78, 0x90, 0x10, 0x04, 0xFB, 0xB2, 0xB1, 0x8A, 0x8A, 0xAB, 0x57, 0x0E, 0x07, 0x3B, 0x58, 0xEA, 0x42, 0x0A, 0x8F, 0x25, 0x9C, 0x95, 0x54, 0x71, 0xD9, 0x94, + 0xF4, 0xB8, 0xE2, 0xD4, 0x52, 0xDF, 0x66, 0x4E, 0x8A, 0xD5, 0xEE, 0x98, 0x20, 0xE2, 0xAF, 0x9D, 0xCF, 0x53, 0x70, 0xF5, 0x8A, 0xF7, 0xA0, 0xE6, 0xFA, 0xEC, + 0xF6, 0x22, 0xF9, 0x91, 0xEC, 0x9B, 0xC4, 0xC5, 0x76, 0x50, 0x36, 0x97, 0xED, 0x54, 0x1C, 0xF8, 0xB6, 0x9A, 0xCA, 0x42, 0xEF, 0x90, 0xF5, 0xAC, 0x73, 0x9F, + 0x63, 0x02, 0xD5, 0x57, 0xDE, 0x06, 0x3B, 0xD8, 0x1E, 0x82, 0x14, 0x76, 0x1B, 0xA3, 0x4F, 0x25, 0x99, 0xAA, 0x53, 0x3F, 0xA8, 0xBC, 0x3F, 0x64, 0x77, 0xA5, + 0xF7, 0xC9, 0xE2, 0xB6, 0x7C, 0xE9, 0xFD, 0xE2, 0xC3, 0x2F, 0xE5, 0x4A, 0xEF, 0xF1, 0x5E, 0x76, 0x57, 0x7A, 0xAF, 0x66, 0x33, 0xA5, 0x36, 0xCA, 0x02, 0x63, + 0xF8, 0xFE, 0x88, 0x89, 0xE5, 0xD3, 0x2E, 0x41, 0x30, 0xDA, 0x47, 0x71, 0x1A, 0x8A, 0x28, 0xF6, 0xC4, 0x7E, 0xB2, 0x7D, 0x9E, 0xF5, 0xF4, 0x72, 0xC2, 0x82, + 0xDA, 0x46, 0xD8, 0xF5, 0xD7, 0xA1, 0xB4, 0x87, 0xFC, 0xCB, 0x3C, 0x35, 0x1E, 0xAD, 0xCF, 0x7A, 0x69, 0x40, 0xFB, 0xA8, 0x24, 0xEE, 0xAD, 0x3F, 0x72, 0x3F, + 0x4A, 0x29, 0x4A, 0xA7, 0xFA, 0xD1, 0xF1, 0x5C, 0xB9, 0x4E, 0x4E, 0xC1, 0xCA, 0x44, 0xF3, 0x5E, 0xBC, 0xD4, 0xA2, 0x1E, 0xCD, 0x19, 0x79, 0xDB, 0x89, 0xE6, + 0x88, 0x3B, 0xC1, 0x7B, 0x89, 0xAC, 0x86, 0xC1, 0x96, 0x13, 0x80, 0x7C, 0x13, 0x85, 0x82, 0x00, 0xB2, 0x24, 0xB0, 0x11, 0x11, 0x74, 0xA9, 0x04, 0xBA, 0x29, + 0xED, 0x67, 0x04, 0x7E, 0xDA, 0xBE, 0x6A, 0xDC, 0xEF, 0xED, 0xA0, 0x36, 0x81, 0xE2, 0x4A, 0x70, 0x54, 0x52, 0xA7, 0xE5, 0x16, 0x07, 0x13, 0x3A, 0x2D, 0x67, + 0xD4, 0x5B, 0x5B, 0x1D, 0x04, 0xE4, 0x3D, 0x2A, 0x80, 0x9E, 0xB2, 0x4A, 0xAB, 0x4F, 0x33, 0x7B, 0x3B, 0xC8, 0x4F, 0x50, 0x5A, 0x09, 0x8E, 0x4A, 0xAA, 0xB4, + 0xDC, 0xD2, 0x67, 0x42, 0xA5, 0xEA, 0xF3, 0x4B, 0x4E, 0xE4, 0xD6, 0x54, 0xDA, 0xA7, 0x02, 0xE8, 0x2B, 0xAB, 0xB4, 0xFA, 0xAC, 0xA3, 0xB7, 0x83, 0xDD, 0xBB, + 0x28, 0xAD, 0x04, 0x47, 0x25, 0x55, 0x5A, 0x6E, 0xC9, 0x2E, 0xA1, 0x52, 0xF5, 0xF9, 0x24, 0x27, 0x72, 0x6B, 0x2A, 0x1D, 0x50, 0x01, 0x0C, 0x94, 0x55, 0x5A, + 0xBD, 0x52, 0xD0, 0xDB, 0x41, 0x31, 0x08, 0xA5, 0x95, 0xE0, 0xA8, 0xA4, 0x4A, 0xCB, 0xAD, 0x3E, 0x27, 0x54, 0xAA, 0xBE, 0xCE, 0xC1, 0x89, 0xDC, 0x9A, 0x4A, + 0x87, 0x54, 0x00, 0x43, 0x65, 0x95, 0x56, 0xDF, 0x5F, 0xD5, 0xDB, 0xC1, 0xDE, 0x6D, 0x94, 0x56, 0x82, 0xA3, 0x92, 0x2A, 0x2D, 0x57, 0xBA, 0x4D, 0xA8, 0x54, + 0x7D, 0xE5, 0x86, 0x13, 0xB9, 0x35, 0x95, 0x1E, 0x51, 0x01, 0x1C, 0x29, 0xAB, 0xB4, 0xFA, 0xD6, 0xF5, 0xDE, 0x0E, 0xEA, 0x79, 0x28, 0xAD, 0x04, 0x47, 0x25, + 0x55, 0x5A, 0xAE, 0x82, 0x93, 0x50, 0xA9, 0xFA, 0xDE, 0x29, 0x4E, 0xE4, 0xD6, 0x54, 0x7A, 0x4C, 0x05, 0x70, 0xAC, 0xAC, 0xD2, 0xEA, 0x3B, 0xF7, 0x7B, 0x3B, + 0xD8, 0xB9, 0x8F, 0xD2, 0x4A, 0x70, 0x54, 0x52, 0xA5, 0xE5, 0x6A, 0xB3, 0x09, 0x95, 0xAA, 0x6F, 0x77, 0xE2, 0x44, 0x6E, 0x4D, 0xA5, 0x27, 0x54, 0x00, 0x27, + 0xCA, 0x2A, 0xAD, 0xBE, 0x65, 0xA0, 0xB7, 0x83, 0xCD, 0x2F, 0x28, 0xAD, 0x4E, 0x9C, 0xA3, 0x92, 0x2A, 0x2D, 0xB7, 0xC0, 0xD8, 0xCB, 0xD8, 0xFA, 0xA2, 0xA0, + 0xD2, 0xAC, 0x05, 0xC6, 0x47, 0x50, 0xBF, 0x33, 0x6E, 0xC6, 0x15, 0x3E, 0xFD, 0xF2, 0xEA, 0xE7, 0xD7, 0xD9, 0x85, 0xFD, 0xCC, 0x2A, 0x5E, 0xA2, 0xAF, 0xC7, + 0x5E, 0xC6, 0x8B, 0xCB, 0x0B, 0x09, 0xD7, 0xC3, 0x2F, 0x86, 0xAF, 0x31, 0x9F, 0x6F, 0x69, 0x0C, 0xB8, 0x84, 0xA5, 0xF5, 0xFA, 0x1D, 0x79, 0xD2, 0x52, 0x60, + 0x69, 0x9C, 0xCA, 0xED, 0x04, 0x0F, 0x44, 0x0E, 0x73, 0x71, 0xE4, 0xFD, 0x07, 0xA5, 0x35, 0x1D, 0x06, 0x90, 0x0C, 0x1F, 0xFD, 0xCE, 0x89, 0x62, 0xFC, 0x00, + 0x19, 0x64, 0x6D, 0x8C, 0xDF, 0x60, 0x00, 0x41, 0x1A, 0x7B, 0x8C, 0xA9, 0xB7, 0xCA, 0x4C, 0xA5, 0xAB, 0x00, 0xA5, 0x98, 0xCA, 0xAA, 0xEC, 0x6C, 0x98, 0xA9, + 0x3E, 0x63, 0x2A, 0xC7, 0x49, 0x53, 0x4C, 0xA5, 0xE7, 0xC1, 0xA5, 0x98, 0xCA, 0x9A, 0x08, 0x47, 0x4C, 0x3D, 0x86, 0x40, 0x47, 0x26, 0xF4, 0x53, 0xE2, 0xA5, + 0x43, 0xDD, 0xE5, 0xC5, 0xE1, 0xAB, 0xB7, 0x17, 0x1A, 0x5D, 0xD2, 0x74, 0xED, 0x92, 0x11, 0x2F, 0xD9, 0xE9, 0x1F, 0x2A, 0xE6, 0x51, 0xD2, 0x63, 0x51, 0x2F, + 0xFA, 0xDE, 0x7B, 0x51, 0xC0, 0xE3, 0x90, 0x65, 0x42, 0xDE, 0xA0, 0xD3, 0xAB, 0x52, 0x21, 0x0C, 0x89, 0xDC, 0x52, 0xD0, 0xA3, 0xE8, 0xBB, 0x91, 0x0C, 0x2E, + 0xCB, 0xC9, 0xA0, 0x54, 0x95, 0x34, 0x29, 0x83, 0x12, 0x61, 0x5F, 0x10, 0xB9, 0x4D, 0x19, 0x60, 0x94, 0xBC, 0xBC, 0xD0, 0x3E, 0xFE, 0x4D, 0xBB, 0xBC, 0x5D, + 0xBA, 0xFE, 0xCA, 0x23, 0x85, 0x51, 0x85, 0xC3, 0xA5, 0x3E, 0xF8, 0x3E, 0x18, 0xF4, 0x54, 0x03, 0xCB, 0x20, 0x7B, 0x08, 0x98, 0x76, 0x36, 0x18, 0x2F, 0x29, + 0xA1, 0xFD, 0x90, 0xC1, 0x1F, 0x08, 0x68, 0x5A, 0x29, 0x6E, 0x72, 0xC0, 0x24, 0x87, 0x7A, 0x07, 0xB7, 0x57, 0x2B, 0x32, 0x28, 0xCF, 0x28, 0x7B, 0x1B, 0x1D, + 0x0E, 0x28, 0x95, 0x83, 0x90, 0xBD, 0x4F, 0x3F, 0x5E, 0xA9, 0x31, 0x96, 0xAE, 0xA3, 0x95, 0x53, 0x5D, 0xD6, 0x23, 0xA3, 0x1B, 0x1A, 0x14, 0xA4, 0x37, 0xCE, + 0x0E, 0x21, 0xF4, 0xAE, 0xC3, 0x64, 0x48, 0xF2, 0x6C, 0x6A, 0xCD, 0xC0, 0x8E, 0xE5, 0x7D, 0x50, 0xD1, 0xB2, 0x97, 0x9D, 0xE2, 0x47, 0x23, 0x5B, 0x13, 0x88, + 0xFE, 0x60, 0x12, 0xE8, 0x74, 0x42, 0xE0, 0x0B, 0x63, 0x46, 0xA2, 0xEB, 0x1A, 0x8B, 0xED, 0x79, 0x31, 0xDB, 0x60, 0x08, 0x8D, 0x6B, 0xC2, 0xBF, 0x70, 0xA9, + 0xCD, 0x3D, 0x32, 0x3D, 0x6F, 0x7C, 0x13, 0xE2, 0xE4, 0x4F, 0xE5, 0x61, 0x93, 0x86, 0x66, 0xBA, 0x37, 0x8E, 0xED, 0x1A, 0x38, 0x1E, 0x18, 0xCB, 0x00, 0x28, + 0x6D, 0xFF, 0xBE, 0xC4, 0x17, 0x5F, 0x19, 0xF8, 0x10, 0x97, 0x91, 0xD3, 0x4F, 0xCC, 0x2A, 0x26, 0xB6, 0xEB, 0x8B, 0xD9, 0x1C, 0x1E, 0x86, 0x5F, 0xC4, 0xFC, + 0x9F, 0xFF, 0x2E, 0xDA, 0x41, 0x60, 0x2D, 0x66, 0x31, 0x01, 0x34, 0x34, 0xDF, 0x9B, 0x9C, 0x37, 0x80, 0x52, 0xCF, 0xF5, 0x7D, 0xD7, 0xB3, 0x66, 0x56, 0x86, + 0x76, 0xB2, 0xA4, 0x7D, 0x28, 0x13, 0x77, 0xAA, 0xB1, 0x44, 0xF1, 0x67, 0xFE, 0xC4, 0xB3, 0x96, 0xC1, 0xE8, 0x89, 0xE9, 0x4E, 0x56, 0x0B, 0xE2, 0x04, 0x6D, + 0xC3, 0x34, 0x2F, 0xAF, 0xE1, 0xE0, 0x3D, 0x7E, 0xAC, 0x0D, 0x24, 0xDF, 0xDC, 0x7B, 0xF3, 0xF7, 0x0F, 0x38, 0x3A, 0xE3, 0x35, 0x90, 0x17, 0x31, 0xF7, 0x0E, + 0xB4, 0xE9, 0xCA, 0x61, 0x03, 0x64, 0x93, 0x60, 0xDB, 0x7D, 0xED, 0x2B, 0x60, 0xBC, 0x36, 0x3C, 0x6D, 0x6C, 0xF8, 0xE4, 0x9D, 0xEB, 0x07, 0xDA, 0xB9, 0x16, + 0x62, 0xB4, 0xDD, 0x09, 0xDD, 0xCE, 0xD1, 0x66, 0x7C, 0xF1, 0x96, 0x8C, 0xF1, 0x9F, 0x3C, 0x1B, 0x9A, 0x86, 0x50, 0xCF, 0xB5, 0xBD, 0xD3, 0x63, 0x7D, 0x0F, + 0x6D, 0x37, 0xEC, 0x62, 0x4A, 0x20, 0xFA, 0x43, 0xBB, 0xE6, 0xCA, 0xB3, 0x0F, 0xB4, 0xC9, 0x78, 0xFF, 0x2B, 0xA5, 0x9E, 0x5E, 0xC6, 0x6B, 0xFB, 0x9C, 0x99, + 0x76, 0x30, 0x27, 0x4E, 0x33, 0xA2, 0xCC, 0x23, 0xFE, 0xD2, 0x75, 0x7C, 0xC2, 0x88, 0x63, 0x3F, 0x6B, 0x1A, 0x5D, 0x6F, 0xFB, 0x81, 0x11, 0xAC, 0x7C, 0xED, + 0xE9, 0xF9, 0xB9, 0xD6, 0xED, 0x74, 0xE2, 0xCD, 0x34, 0xE8, 0x26, 0xDD, 0xEE, 0x40, 0x4B, 0x5D, 0xF8, 0x91, 0xDC, 0x06, 0xFB, 0x2F, 0x42, 0x98, 0x7B, 0x8D, + 0xD8, 0x3E, 0x49, 0x20, 0x09, 0x01, 0xF0, 0x75, 0x72, 0xCD, 0xFD, 0x24, 0x81, 0x4D, 0xD3, 0x08, 0x8C, 0xFD, 0xAF, 0x09, 0x7D, 0x41, 0xAF, 0x40, 0xC9, 0x81, + 0x46, 0x6F, 0xBD, 0x88, 0xDD, 0xBA, 0xDF, 0x6F, 0x83, 0x0C, 0x81, 0xDF, 0x10, 0x9A, 0x78, 0x5E, 0x92, 0x62, 0x0A, 0xDD, 0xD2, 0x0F, 0x34, 0xBC, 0x93, 0x84, + 0x8D, 0x11, 0xF9, 0x44, 0x5C, 0x13, 0x42, 0xCB, 0x47, 0x2B, 0x41, 0xC9, 0xD0, 0xDD, 0x27, 0x54, 0x04, 0x71, 0xE8, 0x07, 0x32, 0x03, 0x89, 0xCD, 0x0E, 0x78, + 0x58, 0x3A, 0xA0, 0x31, 0xE9, 0x80, 0x85, 0xB3, 0x98, 0xD6, 0xC0, 0xA1, 0x7D, 0xD7, 0x26, 0x60, 0x13, 0xB3, 0xE6, 0x1E, 0xFF, 0x14, 0x28, 0xD8, 0xD3, 0x5E, + 0xE7, 0x76, 0xEF, 0x39, 0x80, 0xB7, 0x03, 0xF7, 0x2A, 0xF0, 0x2C, 0x67, 0xD6, 0xD4, 0x87, 0xFB, 0x11, 0x2E, 0x7A, 0x1B, 0x11, 0xA6, 0xEE, 0xD3, 0xEB, 0xB4, + 0x8B, 0xF4, 0x8D, 0x26, 0xBF, 0xFE, 0x7C, 0x6F, 0x7F, 0x8F, 0x93, 0x4E, 0xCF, 0xC1, 0xD8, 0x9A, 0xEC, 0xE0, 0x19, 0xA5, 0x70, 0x5F, 0x3B, 0x3B, 0xE3, 0xDD, + 0xB0, 0x56, 0x78, 0x11, 0x1A, 0xD1, 0x3F, 0xA9, 0x5B, 0xA1, 0x21, 0xFE, 0xF6, 0x97, 0xAF, 0xC2, 0x62, 0xEF, 0x0F, 0x81, 0xEA, 0x97, 0x18, 0x97, 0xFF, 0xF2, + 0x15, 0xFE, 0xBF, 0x7F, 0x46, 0x43, 0xF1, 0x5F, 0xBE, 0xE2, 0x9F, 0xFB, 0x67, 0xD0, 0x13, 0x1C, 0xD3, 0xFE, 0xEE, 0x7F, 0xA3, 0x52, 0x58, 0x97, 0xDD, 0x2C, + 0x53, 0x76, 0xA1, 0xD0, 0x4A, 0xD3, 0x34, 0xCB, 0x21, 0xEA, 0xB7, 0xC8, 0x7B, 0x9B, 0x13, 0xD7, 0x04, 0xE5, 0x04, 0x60, 0xC7, 0x42, 0xE5, 0x36, 0xA8, 0x44, + 0x08, 0xAA, 0x23, 0x54, 0x6E, 0x4D, 0x69, 0x4B, 0x8D, 0x3B, 0x4A, 0x64, 0x1E, 0xA2, 0xE5, 0xD2, 0xF0, 0x7C, 0xF2, 0x9D, 0x13, 0x34, 0x83, 0x84, 0x4B, 0x64, + 0x48, 0x7C, 0x34, 0x4A, 0xB0, 0x80, 0x3F, 0x80, 0x83, 0x76, 0x7B, 0x5C, 0x69, 0xA1, 0xA9, 0x3D, 0x09, 0xAD, 0x30, 0xA2, 0x94, 0xDD, 0xCC, 0xB0, 0xC2, 0x5F, + 0x26, 0xF6, 0x97, 0xE6, 0x2D, 0xFC, 0x97, 0x0E, 0x14, 0x6B, 0x22, 0xC2, 0x46, 0x2F, 0xF1, 0x3F, 0x90, 0x0B, 0xFE, 0xC9, 0xD4, 0x0F, 0x60, 0xFD, 0x68, 0xDB, + 0x4D, 0xF6, 0xD9, 0x2F, 0x50, 0xCD, 0x0A, 0x82, 0x90, 0x7F, 0x87, 0xE1, 0xC0, 0x75, 0x83, 0xCF, 0x07, 0xDA, 0xD2, 0x03, 0xC2, 0xE8, 0x97, 0x3E, 0xE0, 0x18, + 0x10, 0x11, 0x87, 0xFD, 0x2D, 0xA4, 0x60, 0x69, 0xDB, 0x2F, 0x19, 0x56, 0x20, 0x81, 0x1D, 0x80, 0xA6, 0x56, 0x68, 0x31, 0xF0, 0xFF, 0xFD, 0x33, 0xE8, 0x04, + 0x0E, 0xE1, 0xFF, 0xFB, 0x67, 0xD8, 0x15, 0xEA, 0x12, 0x7B, 0xBC, 0x7F, 0x06, 0x3D, 0xC2, 0x09, 0xFC, 0x0F, 0x6D, 0xB0, 0x5F, 0x6C, 0x85, 0x7F, 0xE1, 0x0E, + 0xED, 0x1F, 0x6F, 0xD2, 0x03, 0x76, 0x81, 0x9F, 0xE6, 0x31, 0xC8, 0xDE, 0x74, 0xDF, 0xA4, 0x6F, 0x1E, 0xFF, 0x7C, 0x0B, 0xEC, 0xD0, 0x83, 0x3B, 0x70, 0x7C, + 0xC7, 0xC4, 0x73, 0xFC, 0x73, 0x27, 0xCC, 0x13, 0x2F, 0xF0, 0x23, 0xB8, 0x46, 0xDF, 0xCE, 0x8A, 0x97, 0xD8, 0x01, 0xB6, 0xA2, 0xEF, 0xD2, 0xA4, 0xAD, 0xD8, + 0x11, 0x5C, 0xE3, 0x6F, 0x60, 0x3C, 0xD0, 0xF8, 0x3B, 0xFE, 0x0A, 0x85, 0x13, 0xBD, 0x83, 0xEF, 0xA5, 0x7F, 0x8B, 0x0C, 0x32, 0xD2, 0x50, 0x2A, 0xE1, 0xD9, + 0xDD, 0xFD, 0x33, 0x82, 0xF7, 0x28, 0x91, 0x70, 0x7C, 0xC7, 0x8F, 0xE1, 0x3A, 0xD0, 0x87, 0x77, 0x04, 0xC1, 0xF4, 0xC2, 0x5D, 0x74, 0x01, 0x5A, 0x04, 0x78, + 0x9F, 0x13, 0x0F, 0x67, 0x77, 0xE1, 0x19, 0x42, 0x53, 0x58, 0xCE, 0x06, 0x9C, 0xDE, 0x45, 0xA7, 0x70, 0x17, 0x79, 0x41, 0x05, 0x70, 0x9E, 0xEE, 0x9F, 0x71, + 0x9E, 0x50, 0x8B, 0xEC, 0x28, 0x2D, 0x6A, 0x0C, 0x7A, 0x01, 0x0F, 0x92, 0xAF, 0x59, 0x0E, 0x12, 0x1B, 0x1E, 0x21, 0x00, 0x5C, 0xDA, 0x04, 0x0F, 0x5F, 0xDF, + 0x7D, 0x67, 0x36, 0xF7, 0xF8, 0xA7, 0x5B, 0xF7, 0x30, 0x44, 0xC7, 0x61, 0xDA, 0xAE, 0x33, 0xB1, 0xAD, 0x09, 0x46, 0x82, 0xE6, 0xBE, 0x76, 0x3E, 0xE2, 0x61, + 0x1A, 0x3D, 0x16, 0x9A, 0xC7, 0xBD, 0x30, 0x13, 0xB5, 0xC7, 0x3F, 0x3E, 0xBA, 0xB7, 0xDF, 0xA6, 0x8E, 0xC6, 0x9D, 0x09, 0x51, 0xF0, 0x18, 0xA3, 0x86, 0x03, + 0x1B, 0x4B, 0x70, 0xAC, 0x85, 0x83, 0x5C, 0x24, 0xB4, 0x75, 0x0C, 0x0B, 0x45, 0x13, 0x1F, 0x49, 0x3A, 0xA9, 0x41, 0x24, 0x27, 0x6C, 0x89, 0x08, 0xF5, 0x34, + 0x1D, 0xA1, 0x40, 0x55, 0x5E, 0xD0, 0xDC, 0xBB, 0xF4, 0x3C, 0xD7, 0xFB, 0xE7, 0xDE, 0x73, 0x6C, 0xF4, 0x7C, 0xEF, 0x5F, 0xA7, 0xDA, 0xDE, 0xF3, 0x78, 0xA8, + 0xBA, 0x4F, 0xC7, 0x14, 0xA6, 0xB1, 0x99, 0xA2, 0xC6, 0x66, 0x31, 0x8D, 0xCD, 0x36, 0xAB, 0xB1, 0xF8, 0x27, 0x63, 0xEB, 0x68, 0x2D, 0xFE, 0x89, 0xD6, 0x1C, + 0xCD, 0x15, 0xC2, 0x73, 0xA5, 0x71, 0x6D, 0xCD, 0x64, 0xDA, 0xAA, 0xA2, 0x26, 0x36, 0x86, 0x83, 0xF7, 0x10, 0xEF, 0xDD, 0x8F, 0x1F, 0xDE, 0xE3, 0x58, 0x20, + 0x57, 0x59, 0xA8, 0xB1, 0x74, 0xB6, 0x25, 0xC1, 0x80, 0xC9, 0x41, 0x62, 0x64, 0x4A, 0x24, 0x09, 0xCF, 0xF7, 0xB4, 0x26, 0x45, 0x89, 0x29, 0x42, 0x81, 0x21, + 0xF0, 0x91, 0x45, 0xCD, 0x77, 0x71, 0x34, 0x11, 0xCE, 0x1B, 0x41, 0xE5, 0xD8, 0x02, 0x02, 0x28, 0x29, 0x91, 0x61, 0x5E, 0x73, 0x98, 0xD8, 0xA0, 0xB7, 0x73, + 0x17, 0xA1, 0xFE, 0xEA, 0xAB, 0x06, 0x35, 0x11, 0xD3, 0xA3, 0xD8, 0xE6, 0x17, 0x4A, 0x87, 0x47, 0x7E, 0x25, 0x01, 0xF1, 0x4F, 0x81, 0x48, 0x0C, 0x9C, 0x8F, + 0x18, 0x25, 0xB0, 0xDC, 0x49, 0xB0, 0xD0, 0x91, 0x46, 0x09, 0x07, 0xFD, 0x7C, 0x44, 0x06, 0x06, 0x35, 0x2A, 0xE8, 0xF7, 0x1A, 0x24, 0x18, 0xC4, 0x98, 0xA6, + 0x84, 0x44, 0x7C, 0x6B, 0x20, 0x1B, 0x8F, 0x1A, 0x31, 0xE2, 0x0D, 0xFF, 0x12, 0x3C, 0x7C, 0x0C, 0x55, 0x42, 0xC3, 0xDF, 0x4E, 0x9F, 0x89, 0x45, 0x8D, 0x18, + 0xFE, 0x42, 0x78, 0x19, 0x4F, 0x7C, 0xCC, 0x56, 0xE3, 0x89, 0xBF, 0xC7, 0x3C, 0x1B, 0x8F, 0xA2, 0x6C, 0xF8, 0xBB, 0xC3, 0x65, 0x56, 0xC7, 0x52, 0x84, 0x5C, + 0xC7, 0x60, 0x4D, 0x00, 0x98, 0x97, 0xA5, 0x5F, 0xEA, 0xA7, 0x9D, 0x08, 0x03, 0xCF, 0x28, 0xF2, 0x30, 0xF0, 0x26, 0x69, 0x0C, 0x22, 0x3A, 0x3C, 0x40, 0x6E, + 0xF7, 0x10, 0x51, 0x08, 0x72, 0x74, 0xB5, 0x28, 0x04, 0x69, 0xB7, 0x08, 0x3F, 0x21, 0x4C, 0x46, 0xF8, 0xA1, 0x05, 0x0D, 0xF6, 0x05, 0xE3, 0x3C, 0xF9, 0x87, + 0x1F, 0x04, 0x96, 0x29, 0x11, 0x71, 0x40, 0x3A, 0xAF, 0x64, 0x49, 0xFC, 0xDB, 0xB7, 0x29, 0x43, 0xA2, 0xC5, 0x92, 0x3B, 0x5F, 0x2D, 0x74, 0xDD, 0xF9, 0x19, + 0x18, 0xE8, 0xDC, 0x41, 0x2D, 0x37, 0xE3, 0x1F, 0x8B, 0x95, 0x20, 0x81, 0x39, 0x87, 0x12, 0x0A, 0xFE, 0xC5, 0x4C, 0x19, 0x23, 0xF4, 0x83, 0x8A, 0x4A, 0xAC, + 0x88, 0x8F, 0x27, 0xCA, 0xE8, 0xA0, 0xD3, 0x9B, 0x3C, 0xA5, 0xF0, 0x4F, 0xD4, 0x65, 0x69, 0x64, 0xA9, 0x3A, 0xE4, 0x8A, 0xCF, 0xB1, 0x49, 0x86, 0xDD, 0x8A, + 0xB3, 0xC2, 0x87, 0x19, 0xA2, 0x67, 0x9F, 0xC4, 0x5C, 0x9C, 0xD8, 0xD2, 0x54, 0x94, 0xD8, 0x6D, 0x23, 0x80, 0xE4, 0x68, 0xBC, 0x0A, 0x88, 0xDF, 0xC6, 0xFA, + 0x41, 0x28, 0x9C, 0xB5, 0x5B, 0x6D, 0x07, 0x08, 0xA0, 0x08, 0xF7, 0xE3, 0xB1, 0x8A, 0x05, 0x8E, 0x35, 0x5C, 0xEC, 0x72, 0x16, 0x3A, 0x76, 0x37, 0x03, 0x23, + 0x4F, 0x6F, 0x93, 0x10, 0x78, 0x31, 0x0B, 0x1B, 0xAD, 0x11, 0xC5, 0x70, 0x75, 0x07, 0x83, 0xF5, 0x24, 0x97, 0x77, 0xC0, 0x96, 0x95, 0x50, 0x20, 0x6D, 0x2C, + 0xD1, 0x47, 0x65, 0xAF, 0x09, 0xCC, 0x42, 0xB5, 0x3D, 0xB1, 0xA6, 0xB4, 0x77, 0xBA, 0x56, 0xCF, 0x00, 0x08, 0x6E, 0x55, 0xDA, 0x4B, 0x46, 0xE3, 0x69, 0x54, + 0x2C, 0xD1, 0xB4, 0xB1, 0x47, 0x8C, 0x2F, 0x2F, 0x12, 0xC8, 0x68, 0xF5, 0x3F, 0xC4, 0xC4, 0xAE, 0x61, 0x51, 0x30, 0x75, 0x89, 0x3D, 0x71, 0xD3, 0x72, 0x1D, + 0x22, 0xEF, 0x35, 0x51, 0x1D, 0xE1, 0x1D, 0xF1, 0x33, 0x93, 0x4C, 0x8D, 0x95, 0x1D, 0x44, 0x60, 0x1E, 0x09, 0x56, 0x9E, 0xC3, 0xAB, 0x25, 0xEB, 0x93, 0x2B, + 0x69, 0x99, 0x6E, 0x87, 0xB6, 0x79, 0x78, 0xA8, 0xBD, 0x0A, 0x02, 0x03, 0x14, 0x80, 0xCB, 0xAC, 0x73, 0x94, 0x8F, 0x66, 0xF0, 0x82, 0xAF, 0xEB, 0xA1, 0x51, + 0x62, 0xFD, 0xD9, 0x03, 0xAE, 0xA9, 0x37, 0xFA, 0x00, 0x22, 0x9C, 0x94, 0xA2, 0x6A, 0xFF, 0x7B, 0x45, 0xBC, 0xBB, 0x2B, 0x2A, 0x30, 0xD7, 0x7B, 0x05, 0xBE, + 0xB8, 0xD7, 0x8E, 0x96, 0x4A, 0xF6, 0x58, 0x7D, 0xB3, 0x0D, 0xA8, 0x2E, 0xA1, 0x0F, 0xD0, 0x71, 0x64, 0xF3, 0x8C, 0x9B, 0x50, 0xEF, 0xDA, 0xF9, 0xF9, 0x39, + 0x57, 0x46, 0xBA, 0xA0, 0x0A, 0x2D, 0x5C, 0xE7, 0x0B, 0xB9, 0x5B, 0x2D, 0x41, 0xFC, 0x51, 0x89, 0x34, 0x55, 0xB4, 0xE5, 0xD2, 0x21, 0x6D, 0x68, 0x79, 0xC1, + 0xCB, 0x64, 0x7A, 0x4F, 0xD2, 0x28, 0x52, 0x01, 0xB5, 0x4E, 0xF4, 0xC4, 0x17, 0x6B, 0x8D, 0xEE, 0x9F, 0xC8, 0xCF, 0x24, 0xE5, 0x65, 0x4E, 0x20, 0x17, 0x9E, + 0x18, 0xBA, 0x52, 0x3D, 0x3C, 0x49, 0xA2, 0xBA, 0xDF, 0x7F, 0x12, 0x45, 0x86, 0xD5, 0xD2, 0x34, 0x02, 0x92, 0x0C, 0x0E, 0xA1, 0x2D, 0x88, 0x9B, 0x0B, 0x37, + 0x20, 0xA9, 0x88, 0x61, 0x39, 0x56, 0x60, 0x19, 0xF6, 0xA7, 0xC8, 0x1A, 0xB7, 0xEA, 0xFE, 0x12, 0x1F, 0x2F, 0xE1, 0xFF, 0x6B, 0x15, 0x5E, 0xB5, 0xAA, 0xE4, + 0x9A, 0x85, 0x84, 0xF1, 0x20, 0xB2, 0x92, 0xB8, 0x1C, 0x12, 0x61, 0x81, 0xDF, 0x17, 0x3D, 0x3D, 0x7D, 0x4A, 0x8F, 0x9E, 0x84, 0x4A, 0x13, 0xD1, 0xE3, 0x5C, + 0x8B, 0x6E, 0xA4, 0x14, 0xBC, 0x8E, 0x3B, 0x85, 0x43, 0x20, 0x8F, 0x61, 0x60, 0xBE, 0x15, 0xAA, 0x77, 0x09, 0x53, 0x5D, 0xB4, 0x85, 0xFF, 0x8F, 0xFA, 0x8F, + 0x28, 0xEA, 0x6F, 0x2F, 0xC4, 0xE7, 0xD8, 0x76, 0xCA, 0x03, 0x18, 0x9C, 0x7C, 0xD1, 0xE5, 0xF9, 0xDE, 0x81, 0x26, 0x5F, 0x55, 0x49, 0xA5, 0x15, 0x73, 0xCB, + 0x64, 0x24, 0x47, 0x76, 0x85, 0x12, 0xC2, 0x85, 0x51, 0x5C, 0x3A, 0xC4, 0x75, 0xC4, 0xE6, 0x1E, 0x5B, 0xB5, 0xA5, 0xD1, 0xF8, 0x3E, 0x4A, 0x48, 0xE6, 0xEE, + 0x4D, 0x1E, 0xA4, 0x07, 0x31, 0xE7, 0x9A, 0xA4, 0x80, 0x43, 0x68, 0xD3, 0xF2, 0x8D, 0xB1, 0x5D, 0xDC, 0x35, 0x6F, 0x67, 0xF2, 0xA1, 0x00, 0x1A, 0x88, 0x2B, + 0x00, 0x1A, 0x78, 0xD4, 0x67, 0x62, 0x68, 0x89, 0x53, 0x84, 0x55, 0x90, 0x95, 0x8B, 0x78, 0x6A, 0x80, 0x13, 0x27, 0x31, 0xB3, 0x40, 0x5A, 0x22, 0xC4, 0xC6, + 0x2F, 0x03, 0x44, 0xF2, 0xF4, 0x5C, 0x73, 0x56, 0xB6, 0x0D, 0x16, 0x88, 0x2C, 0x80, 0x05, 0xC6, 0xEF, 0x4A, 0x03, 0xF4, 0x1F, 0x37, 0x9A, 0x85, 0x94, 0x27, + 0x24, 0xF0, 0xEC, 0x59, 0x12, 0x1B, 0x2E, 0xDF, 0xB2, 0xD4, 0x3C, 0xEC, 0x8D, 0xB5, 0x67, 0x6F, 0xD3, 0x8D, 0x46, 0x59, 0x4E, 0x12, 0x0C, 0xD5, 0x4F, 0x13, + 0x82, 0x8F, 0x65, 0x38, 0x40, 0x88, 0x65, 0x52, 0x01, 0xE1, 0x26, 0x8D, 0xC6, 0xDA, 0x4A, 0xD7, 0x4B, 0x6A, 0xF5, 0x4D, 0xC2, 0xF7, 0xE8, 0xEC, 0x83, 0xFC, + 0xD1, 0x98, 0xA3, 0x0B, 0x22, 0xDB, 0x09, 0xBB, 0x8A, 0x63, 0x9C, 0x25, 0x30, 0x22, 0x63, 0x29, 0xBA, 0xF1, 0x47, 0x3B, 0x80, 0xA6, 0xB8, 0x43, 0x26, 0x36, + 0x78, 0xAF, 0x8F, 0xFE, 0xB4, 0xE3, 0xF5, 0x86, 0xB9, 0x14, 0xDC, 0x8C, 0x3F, 0xCF, 0xA0, 0xB9, 0x8C, 0x31, 0x8A, 0xEE, 0x66, 0x8C, 0x2C, 0x51, 0x12, 0xE0, + 0x30, 0x13, 0x95, 0xD6, 0x00, 0x63, 0xFF, 0x6C, 0xE1, 0x96, 0x00, 0xDF, 0x0A, 0xEE, 0xD6, 0xD1, 0x8D, 0xB4, 0x96, 0x2E, 0x70, 0x42, 0xD3, 0xB7, 0xB8, 0x6D, + 0x26, 0xC4, 0x1C, 0x5E, 0x48, 0xA6, 0x86, 0xC2, 0x69, 0xC2, 0x75, 0xA9, 0xB8, 0x16, 0xD9, 0x00, 0x16, 0x8D, 0x5E, 0x91, 0x99, 0x6C, 0x28, 0x9E, 0xEB, 0x18, + 0xCC, 0xA5, 0x51, 0xB7, 0x66, 0x28, 0xCF, 0xC1, 0xC9, 0xB6, 0xA3, 0xA4, 0x91, 0xAE, 0xC6, 0x0B, 0x2B, 0x90, 0x20, 0xDC, 0xD3, 0xF7, 0xCA, 0x8C, 0x0A, 0x71, + 0x1F, 0x62, 0x71, 0x88, 0x26, 0xCC, 0x80, 0x28, 0xB1, 0xCE, 0x36, 0x61, 0xDB, 0x2E, 0x5F, 0xC2, 0xA4, 0x18, 0x57, 0xCF, 0x50, 0xC1, 0xA9, 0x65, 0x6B, 0x86, + 0x82, 0xED, 0xB6, 0xA0, 0x28, 0x92, 0xFB, 0x2D, 0xC4, 0x1E, 0x87, 0x64, 0x7E, 0x1D, 0x5F, 0xE6, 0xFF, 0xCD, 0x23, 0x00, 0xE7, 0x63, 0x8D, 0x4F, 0xFB, 0xCB, + 0x57, 0x8A, 0xE2, 0x5E, 0x9B, 0x82, 0x0F, 0xFB, 0x73, 0x62, 0xD2, 0x7A, 0x54, 0xB0, 0xF2, 0x4F, 0x35, 0x5C, 0xAA, 0x4E, 0xEC, 0xAF, 0xB8, 0xFF, 0x2D, 0xB4, + 0x90, 0x70, 0x08, 0x28, 0x9C, 0x02, 0xD0, 0x6D, 0x38, 0xF9, 0xD9, 0x3F, 0x4B, 0x9A, 0x25, 0xE5, 0x1E, 0xFC, 0x31, 0xFF, 0xB6, 0xDB, 0x90, 0x69, 0x40, 0x37, + 0xDF, 0x43, 0x4E, 0x91, 0x32, 0xD3, 0x7D, 0x3E, 0x79, 0x01, 0x0D, 0x98, 0x22, 0x10, 0x31, 0x1D, 0xE1, 0x14, 0x85, 0x89, 0x29, 0x21, 0x61, 0xC6, 0x0C, 0xE7, + 0xA5, 0x78, 0x8F, 0x0A, 0x1F, 0x9B, 0x43, 0x59, 0xFC, 0xEE, 0xC3, 0x94, 0x63, 0xFF, 0x49, 0x28, 0x86, 0x75, 0x1C, 0xD8, 0x41, 0x0C, 0x41, 0x42, 0x44, 0x59, + 0x62, 0xE2, 0x46, 0x93, 0x9C, 0x2D, 0xE5, 0xC8, 0x8C, 0xFD, 0x62, 0x23, 0x19, 0x1D, 0xC6, 0x68, 0xCF, 0xFF, 0xA4, 0x46, 0xF3, 0xAF, 0x03, 0x36, 0xF4, 0xC5, + 0x22, 0xD1, 0x7E, 0x19, 0x82, 0xD6, 0xA6, 0x6E, 0x85, 0xC4, 0x6C, 0x2C, 0x89, 0x15, 0x3F, 0x08, 0x70, 0x14, 0x1F, 0xA4, 0x8A, 0x6B, 0x93, 0xB7, 0x78, 0xA6, + 0x15, 0x32, 0x28, 0x91, 0x4D, 0x6C, 0xE6, 0x25, 0xC4, 0x23, 0xCD, 0xB2, 0x32, 0xC5, 0xC5, 0xEC, 0x8B, 0x39, 0xEC, 0xB5, 0x45, 0x6E, 0x72, 0xAB, 0x99, 0x74, + 0x07, 0x15, 0x95, 0x57, 0x04, 0x70, 0x11, 0xEE, 0x98, 0x2B, 0x84, 0x8C, 0x76, 0xD7, 0xC5, 0x70, 0xD0, 0xAD, 0x73, 0x6A, 0x4B, 0x9E, 0xB4, 0x69, 0x02, 0x14, + 0xB1, 0x16, 0xC3, 0x8A, 0xDD, 0xD4, 0x69, 0xF2, 0xA9, 0xEF, 0x16, 0x83, 0xC7, 0x77, 0xDA, 0xC5, 0x7B, 0x37, 0xAE, 0x15, 0x80, 0xA3, 0xED, 0x81, 0x31, 0x50, + 0x31, 0x22, 0xE5, 0x01, 0x42, 0x1B, 0xB6, 0xFD, 0x73, 0x2F, 0xA6, 0x21, 0x3F, 0x70, 0x97, 0x57, 0x94, 0x90, 0x54, 0x28, 0xB9, 0xA1, 0x45, 0xF7, 0x36, 0xDE, + 0x6F, 0xF2, 0xF4, 0x23, 0x2E, 0x9D, 0xE4, 0x3A, 0xE5, 0x15, 0x56, 0xE4, 0x35, 0x86, 0x67, 0x2F, 0x99, 0x3B, 0xD2, 0x62, 0xBD, 0xB4, 0x07, 0xD4, 0x75, 0xDB, + 0xF7, 0x26, 0x2C, 0xA0, 0x87, 0x9B, 0xE9, 0x30, 0xDE, 0xE0, 0xE1, 0x6F, 0xAC, 0x4F, 0x1C, 0x80, 0x13, 0x56, 0xB1, 0x5F, 0x48, 0x8B, 0xBB, 0x4C, 0x93, 0x12, + 0x95, 0x6A, 0x98, 0x87, 0xFA, 0x18, 0xC9, 0xD9, 0xE0, 0x85, 0x31, 0x2F, 0x66, 0x31, 0x19, 0x81, 0x35, 0x12, 0x13, 0x8F, 0x64, 0x49, 0xE2, 0x63, 0xA3, 0x11, + 0xDB, 0x89, 0xF9, 0xF2, 0xF3, 0x64, 0x0C, 0x03, 0xD0, 0x1B, 0x70, 0x1F, 0xF0, 0xD7, 0x9B, 0xE6, 0xFE, 0x7D, 0x1E, 0x3B, 0x4C, 0x5C, 0x91, 0xED, 0xA8, 0x12, + 0x41, 0x43, 0xBD, 0x1C, 0x5B, 0x42, 0x3E, 0x72, 0x74, 0x71, 0x93, 0xBF, 0x74, 0x44, 0xFA, 0x9F, 0x25, 0xD8, 0xF3, 0x75, 0xD1, 0xB2, 0x0C, 0x30, 0x81, 0x20, + 0x0A, 0xE2, 0x6B, 0xC4, 0xA6, 0x12, 0xC0, 0x98, 0x5D, 0x88, 0x06, 0x21, 0xED, 0xA1, 0x1F, 0xE4, 0xAC, 0x6A, 0x4C, 0x0C, 0xE7, 0xDA, 0x48, 0xAC, 0x6A, 0x4C, + 0x00, 0x57, 0x40, 0xB8, 0xC9, 0x37, 0x1B, 0xAC, 0x41, 0x83, 0xDB, 0x2E, 0x3B, 0x6B, 0xD3, 0xE7, 0x43, 0x30, 0x49, 0x47, 0xF5, 0xD1, 0x93, 0xC4, 0xED, 0x39, + 0xA1, 0xEF, 0xE4, 0xE3, 0xF7, 0xD9, 0x19, 0x6B, 0x10, 0xF6, 0x32, 0x76, 0xCD, 0xBB, 0xB6, 0xB1, 0x5C, 0x12, 0xC7, 0xBC, 0x98, 0x5B, 0xB6, 0xD9, 0x64, 0xA0, + 0xB1, 0xD2, 0x3E, 0xC6, 0x24, 0x42, 0x77, 0x83, 0x71, 0xAC, 0xE0, 0x88, 0x17, 0xEC, 0x5A, 0x73, 0xAF, 0x6B, 0x8A, 0xCD, 0x7C, 0xBC, 0x59, 0xDB, 0xF4, 0x8C, + 0x9B, 0xEF, 0x70, 0xAB, 0x30, 0xD5, 0xE4, 0x41, 0xE7, 0xA0, 0xC3, 0x1B, 0x04, 0x90, 0xEB, 0x08, 0x69, 0x21, 0x5E, 0xDC, 0x52, 0xF9, 0xD3, 0x0F, 0xEF, 0x23, + 0xBC, 0x81, 0xFB, 0x86, 0x5D, 0x6A, 0xEE, 0xD1, 0xBD, 0xC6, 0x87, 0xBF, 0x2F, 0x71, 0x0B, 0x87, 0x88, 0xF1, 0x31, 0x31, 0xE2, 0x36, 0x62, 0x14, 0x15, 0x6B, + 0xFE, 0x22, 0x8E, 0x14, 0x2E, 0x3B, 0x10, 0xA4, 0xD1, 0x52, 0x9B, 0x32, 0x50, 0xB1, 0xC9, 0x18, 0xC1, 0x91, 0x93, 0x6F, 0x61, 0xEE, 0xF6, 0x2B, 0x31, 0x3C, + 0xD0, 0xC7, 0x73, 0xAD, 0xD9, 0xE8, 0x34, 0x9E, 0x37, 0xE9, 0xF5, 0x0F, 0xC0, 0xCE, 0xBC, 0xB9, 0xFF, 0x5C, 0xDF, 0xDF, 0x6F, 0xFB, 0xA0, 0x33, 0xD2, 0x6C, + 0x75, 0x45, 0x13, 0xF8, 0x43, 0xDB, 0xB0, 0x4E, 0xB2, 0xEF, 0xBF, 0x73, 0x57, 0x9E, 0x9F, 0xD7, 0xE0, 0x83, 0xE5, 0xE0, 0x30, 0x98, 0xD7, 0xE4, 0x8A, 0x80, + 0x60, 0xCD, 0xB5, 0x26, 0x0D, 0xBA, 0x37, 0x5A, 0x4C, 0xA3, 0xE8, 0x96, 0x51, 0xC8, 0xB8, 0x63, 0xB9, 0x36, 0x4F, 0xF7, 0x08, 0x16, 0x81, 0x9B, 0x62, 0xA5, + 0xE6, 0x3E, 0x6E, 0x1C, 0x51, 0x42, 0xC5, 0x27, 0xD2, 0x6B, 0xFA, 0x4F, 0xC5, 0x1A, 0x9E, 0xA2, 0xAC, 0x95, 0x87, 0x55, 0xB2, 0x40, 0x69, 0x7A, 0x93, 0x9B, + 0x0E, 0x26, 0x6B, 0xA8, 0xE9, 0x09, 0x64, 0x32, 0xF5, 0xBB, 0x58, 0x81, 0x7B, 0x2E, 0x44, 0x30, 0x64, 0xD7, 0x70, 0x16, 0x16, 0x46, 0x6D, 0x98, 0x95, 0xE5, + 0x0D, 0x25, 0x70, 0x3B, 0x36, 0xF8, 0xF0, 0x29, 0x5C, 0x01, 0x00, 0x9D, 0xB7, 0x85, 0x03, 0x90, 0x86, 0x50, 0xEB, 0x64, 0xC7, 0xCB, 0x07, 0x9C, 0x78, 0x68, + 0xB7, 0x1F, 0x06, 0x1B, 0x04, 0xE2, 0x53, 0x9E, 0x48, 0x77, 0xEB, 0x13, 0xCE, 0x74, 0xAC, 0x59, 0x9B, 0x68, 0xDE, 0xC7, 0xB4, 0x25, 0x9E, 0x57, 0x89, 0xF8, + 0x21, 0xF9, 0xCC, 0x93, 0x38, 0xF3, 0x62, 0xE2, 0x5C, 0x00, 0xF1, 0x99, 0x26, 0xD8, 0x71, 0xF6, 0x89, 0x22, 0xFB, 0x84, 0xB3, 0x8F, 0x00, 0xD1, 0x6C, 0xAF, + 0x78, 0x16, 0x1F, 0x1A, 0xE3, 0xCF, 0xAF, 0x23, 0xCE, 0x6E, 0xC6, 0xB9, 0x74, 0xF2, 0xD9, 0x75, 0x8C, 0xBD, 0x7C, 0x00, 0x68, 0xBF, 0x00, 0x7F, 0x88, 0xB3, + 0x75, 0x33, 0x56, 0x63, 0x4B, 0xCC, 0xCE, 0x11, 0x20, 0x62, 0x4B, 0x3E, 0x87, 0x17, 0xAC, 0xBC, 0x21, 0x01, 0x7F, 0xAA, 0xCD, 0x70, 0x4C, 0x6D, 0xEA, 0x19, + 0x0B, 0x82, 0x1F, 0x31, 0x0F, 0x89, 0x0D, 0xAF, 0xE4, 0xD1, 0x1C, 0x36, 0x62, 0x69, 0x50, 0x78, 0xAA, 0x44, 0x76, 0xD8, 0x3A, 0x32, 0xC9, 0x08, 0x81, 0xA8, + 0x1C, 0x0C, 0xD2, 0x75, 0x1C, 0x96, 0x50, 0x9B, 0x94, 0xFA, 0x54, 0xDA, 0x1C, 0x6F, 0xE0, 0x41, 0xE8, 0x9A, 0x39, 0x80, 0x29, 0xD1, 0x86, 0x99, 0x2A, 0x73, + 0x5D, 0xF6, 0x3B, 0x3B, 0x14, 0x8F, 0x3C, 0xB0, 0x33, 0x1C, 0x92, 0x46, 0x4F, 0xCE, 0x0E, 0xE7, 0xC1, 0xC2, 0x1E, 0x3D, 0xF9, 0x5F, 0x0C, 0x79, 0xF5, 0x60, + 0xD6, 0x04, 0x01, 0x00 }; -//File: index_ov5640.html.gz, Size: 9124 -#define index_ov5640_html_gz_len 9124 -const uint8_t index_ov5640_html_gz[] = { - 0x1F, 0x8B, 0x08, 0x08, 0xD9, 0x6C, 0x6A, 0x5E, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x6F, 0x76, 0x35, 0x36, 0x34, 0x30, 0x2E, 0x68, 0x74, 0x6D, - 0x6C, 0x00, 0xED, 0x3D, 0x6B, 0x77, 0xDB, 0xB6, 0x92, 0xDF, 0xF3, 0x2B, 0x18, 0xF5, 0x6E, 0x24, 0x9F, 0x58, 0xB6, 0xA8, 0x97, 0x1F, 0xB1, 0x95, 0x4D, 0x1C, - 0x27, 0xE9, 0xB9, 0x4D, 0x6F, 0x1A, 0xA7, 0x69, 0x7B, 0xBA, 0x3D, 0x29, 0x25, 0x41, 0x12, 0x1B, 0x8A, 0xD4, 0x25, 0x29, 0xCB, 0x6E, 0x8E, 0x7F, 0xC7, 0xFE, - 0xA0, 0xFD, 0x63, 0x3B, 0x03, 0x80, 0x24, 0x48, 0x81, 0x24, 0x48, 0x4A, 0xB2, 0xDB, 0x5D, 0xE5, 0x9C, 0x98, 0x0F, 0xCC, 0x60, 0xDE, 0x18, 0x0C, 0x40, 0xF2, - 0xEC, 0xF1, 0xD8, 0x19, 0xF9, 0xB7, 0x0B, 0xA2, 0xCD, 0xFC, 0xB9, 0x35, 0x78, 0x74, 0xC6, 0xFE, 0x68, 0xF0, 0x3B, 0x9B, 0x11, 0x63, 0xCC, 0x0E, 0xE9, 0xE9, - 0x9C, 0xF8, 0x86, 0x36, 0x9A, 0x19, 0xAE, 0x47, 0xFC, 0xF3, 0xDA, 0xD2, 0x9F, 0x34, 0x8F, 0x6B, 0xC9, 0xDB, 0xB6, 0x31, 0x27, 0xE7, 0xB5, 0x6B, 0x93, 0xAC, - 0x16, 0x8E, 0xEB, 0xD7, 0xB4, 0x91, 0x63, 0xFB, 0xC4, 0x86, 0xE6, 0x2B, 0x73, 0xEC, 0xCF, 0xCE, 0xC7, 0xE4, 0xDA, 0x1C, 0x91, 0x26, 0x3D, 0xD9, 0x37, 0x6D, - 0xD3, 0x37, 0x0D, 0xAB, 0xE9, 0x8D, 0x0C, 0x8B, 0x9C, 0xEB, 0x22, 0x2E, 0xDF, 0xF4, 0x2D, 0x32, 0xB8, 0xBC, 0x7A, 0xDF, 0x69, 0x6B, 0xFF, 0xFA, 0xD4, 0xEB, - 0x77, 0x5B, 0x67, 0x87, 0xEC, 0x5A, 0xD4, 0xC6, 0xF3, 0x6F, 0xC5, 0x73, 0xFC, 0x0D, 0x9D, 0xF1, 0xAD, 0xF6, 0x35, 0x76, 0x09, 0x7F, 0x13, 0x20, 0xA2, 0x39, - 0x31, 0xE6, 0xA6, 0x75, 0x7B, 0xAA, 0xBD, 0x70, 0xA1, 0xCF, 0xFD, 0xB7, 0xC4, 0xBA, 0x26, 0xBE, 0x39, 0x32, 0xF6, 0x3D, 0xC3, 0xF6, 0x9A, 0x1E, 0x71, 0xCD, - 0xC9, 0xB3, 0x35, 0xC0, 0xA1, 0x31, 0xFA, 0x32, 0x75, 0x9D, 0xA5, 0x3D, 0x3E, 0xD5, 0xBE, 0xD1, 0x8F, 0xF1, 0xDF, 0x7A, 0xA3, 0x91, 0x63, 0x39, 0x2E, 0xDC, - 0xBF, 0x7C, 0x8D, 0xFF, 0xD6, 0xEF, 0xD3, 0xDE, 0x3D, 0xF3, 0x4F, 0x72, 0xAA, 0xE9, 0xFD, 0xC5, 0x4D, 0xEC, 0xFE, 0xDD, 0xA3, 0xD8, 0xE9, 0xAC, 0x9D, 0x46, - 0x3D, 0x87, 0x3F, 0xCE, 0x86, 0xF7, 0xC8, 0xC8, 0x37, 0x1D, 0xFB, 0x60, 0x6E, 0x98, 0xB6, 0x04, 0xD3, 0xD8, 0xF4, 0x16, 0x96, 0x01, 0x32, 0x98, 0x58, 0x24, - 0x13, 0xCF, 0x37, 0x73, 0x62, 0x2F, 0xF7, 0x73, 0xB0, 0x21, 0x92, 0xE6, 0xD8, 0x74, 0x59, 0xAB, 0x53, 0x94, 0xC3, 0x72, 0x6E, 0xE7, 0xA2, 0xCD, 0xA2, 0xCB, - 0x76, 0x6C, 0x22, 0x11, 0x20, 0x76, 0xB4, 0x72, 0x8D, 0x05, 0x36, 0xC0, 0xBF, 0xEB, 0x4D, 0xE6, 0xA6, 0xCD, 0x8C, 0xEA, 0x54, 0xEB, 0x74, 0x5B, 0x8B, 0x9B, - 0x1C, 0x55, 0x76, 0xFA, 0xF8, 0x6F, 0xBD, 0xD1, 0xC2, 0x18, 0x8F, 0x4D, 0x7B, 0x7A, 0xAA, 0x1D, 0x4B, 0x51, 0x38, 0xEE, 0x98, 0xB8, 0x4D, 0xD7, 0x18, 0x9B, - 0x4B, 0xEF, 0x54, 0xEB, 0xCA, 0xDA, 0xCC, 0x0D, 0x77, 0x0A, 0xB4, 0xF8, 0x0E, 0x10, 0xDB, 0xD4, 0xA5, 0x94, 0xF0, 0x26, 0xAE, 0x39, 0x9D, 0xF9, 0xA0, 0xD2, - 0xB5, 0x36, 0x49, 0xA1, 0x71, 0x17, 0xCA, 0xD3, 0x67, 0xA6, 0xDC, 0xE4, 0x52, 0x33, 0x2C, 0x73, 0x6A, 0x37, 0x4D, 0x9F, 0xCC, 0x81, 0x1D, 0xCF, 0x77, 0x89, - 0x3F, 0x9A, 0x65, 0x91, 0x32, 0x31, 0xA7, 0x4B, 0x97, 0x48, 0x08, 0x09, 0xE5, 0x96, 0xC1, 0x30, 0xDC, 0x5C, 0xBF, 0xD5, 0x5C, 0x91, 0xE1, 0x17, 0xD3, 0x6F, - 0x72, 0x99, 0x0C, 0xC9, 0xC4, 0x71, 0x89, 0xB4, 0x65, 0xD0, 0xC2, 0x72, 0x46, 0x5F, 0x9A, 0x9E, 0x6F, 0xB8, 0xBE, 0x0A, 0x42, 0x63, 0xE2, 0x13, 0x37, 0x1F, - 0x1F, 0x41, 0xAB, 0xC8, 0xC7, 0x96, 0xDE, 0x2D, 0x6F, 0x60, 0xDA, 0x96, 0x69, 0x13, 0x75, 0xF2, 0xD2, 0xFA, 0x8D, 0xA3, 0x63, 0xAD, 0x14, 0x14, 0x63, 0xCE, - 0xA7, 0x59, 0x56, 0x42, 0x79, 0x5D, 0xEF, 0x8C, 0xFB, 0x8D, 0xDE, 0x6A, 0xFD, 0xC7, 0xFA, 0xCD, 0x19, 0x61, 0x66, 0x6A, 0x2C, 0x7D, 0xA7, 0xBA, 0x47, 0xAC, - 0xB9, 0x55, 0x82, 0x8F, 0xFF, 0x9C, 0x93, 0xB1, 0x69, 0x68, 0x0D, 0xC1, 0x9D, 0x8F, 0x5B, 0x60, 0x53, 0x7B, 0x9A, 0x61, 0x8F, 0xB5, 0x86, 0xE3, 0x9A, 0xE0, - 0x08, 0x06, 0x0D, 0x37, 0x16, 0x5C, 0x81, 0x81, 0x63, 0x41, 0xF6, 0x24, 0x2C, 0x67, 0xF8, 0x8C, 0x28, 0x11, 0xB9, 0xDB, 0xE0, 0x4F, 0x21, 0xE4, 0xE0, 0x2F, - 0xD7, 0x81, 0x24, 0x3C, 0x52, 0xF4, 0x59, 0xFA, 0x12, 0x29, 0x4C, 0xD3, 0x19, 0xFE, 0xE6, 0xC6, 0x4D, 0x33, 0x53, 0x77, 0x41, 0xA3, 0x40, 0x87, 0x30, 0xCC, - 0x8E, 0x1A, 0xD0, 0xF4, 0x7A, 0xA6, 0x35, 0x35, 0x8C, 0x92, 0x7B, 0x72, 0x18, 0x8E, 0x54, 0xAE, 0x72, 0xFC, 0x89, 0x46, 0x51, 0x80, 0x5D, 0x39, 0xAB, 0x51, - 0xEC, 0x60, 0xFF, 0x64, 0x36, 0xC4, 0x38, 0x49, 0x8D, 0x22, 0xF8, 0x53, 0x8F, 0x24, 0x11, 0xB2, 0xDC, 0x68, 0x22, 0x41, 0x9C, 0x1E, 0x51, 0xD6, 0xF0, 0xA6, - 0x79, 0xB7, 0x04, 0x6B, 0x36, 0x09, 0xAA, 0xD1, 0x45, 0x82, 0x38, 0x8B, 0x86, 0xDC, 0x28, 0x83, 0xBF, 0x3B, 0x85, 0x7C, 0xE3, 0x9B, 0xE1, 0xD2, 0xF7, 0x1D, - 0xDB, 0xAB, 0x34, 0x44, 0xA5, 0xF9, 0xD9, 0x1F, 0x4B, 0xCF, 0x37, 0x27, 0xB7, 0x4D, 0xEE, 0xD2, 0xE0, 0x67, 0x0B, 0x03, 0x52, 0xC8, 0x21, 0xF1, 0x57, 0x84, - 0x64, 0xA7, 0x1B, 0xB6, 0x71, 0x0D, 0x71, 0x67, 0x3A, 0xB5, 0x64, 0xB6, 0x37, 0x5A, 0xBA, 0x1E, 0xE6, 0x6D, 0x0B, 0xC7, 0x04, 0xC4, 0xEE, 0x7A, 0xC7, 0x71, - 0x1F, 0x54, 0xEC, 0xA8, 0x39, 0x1A, 0x4A, 0xFA, 0x72, 0x96, 0x3E, 0xCA, 0x58, 0xAA, 0x09, 0x07, 0xD8, 0x31, 0xFD, 0x5B, 0xE9, 0x3D, 0xEE, 0x89, 0x92, 0x3B, - 0x81, 0x0B, 0x66, 0x0E, 0x0B, 0x71, 0xBA, 0x4E, 0x47, 0x33, 0x32, 0xFA, 0x42, 0xC6, 0x4F, 0x73, 0xD3, 0xB0, 0xBC, 0xF4, 0xF0, 0xC0, 0xB4, 0x17, 0x4B, 0xBF, - 0x89, 0xE9, 0xD4, 0x62, 0x2B, 0x3A, 0xA7, 0x06, 0x19, 0xB0, 0xD8, 0x6E, 0x67, 0x25, 0x15, 0xBD, 0xC5, 0x4D, 0xB6, 0x10, 0x44, 0x62, 0x07, 0x96, 0x31, 0x24, - 0x56, 0x16, 0xC9, 0xDC, 0x19, 0x52, 0xC2, 0x2E, 0x8F, 0x55, 0xE9, 0xB9, 0x1B, 0xA5, 0x2C, 0x1A, 0xBC, 0xBA, 0x47, 0xFF, 0xA1, 0x2C, 0x47, 0x7A, 0xBC, 0x1F, - 0xBB, 0xE4, 0x11, 0x0B, 0x1C, 0x2C, 0x71, 0x6D, 0x61, 0xA4, 0x26, 0xE3, 0xD0, 0x62, 0x05, 0x54, 0x65, 0x76, 0xE9, 0x1A, 0xF6, 0x94, 0x40, 0x74, 0xB8, 0xD9, - 0x0F, 0x0E, 0xB3, 0xA7, 0x0A, 0x4A, 0x02, 0xC1, 0xE0, 0xDD, 0xCB, 0x9E, 0x9A, 0xB0, 0x10, 0xB1, 0xAF, 0x1D, 0xB0, 0x83, 0x12, 0x79, 0x8A, 0xA0, 0xF1, 0x4C, - 0x42, 0x74, 0xA9, 0xBD, 0xB0, 0x54, 0x45, 0xEA, 0x4B, 0x71, 0x6B, 0x93, 0xA6, 0xFE, 0xB9, 0xC1, 0x22, 0x98, 0x04, 0x4E, 0x26, 0x79, 0xD3, 0xC8, 0xC9, 0xA4, - 0xD3, 0xEA, 0x74, 0x73, 0x73, 0x29, 0x29, 0x97, 0x89, 0xA9, 0xA4, 0x24, 0x98, 0x84, 0x81, 0x26, 0x5F, 0x17, 0xA7, 0x33, 0xE7, 0x9A, 0xB8, 0x12, 0x45, 0x24, - 0xC8, 0xED, 0x9E, 0x74, 0xC7, 0x0A, 0xD8, 0x0C, 0x18, 0x0A, 0xAE, 0x65, 0x81, 0x36, 0x8E, 0xAE, 0xAD, 0x8F, 0xDA, 0x99, 0x16, 0xCA, 0xD0, 0x1D, 0x80, 0x35, - 0x18, 0x43, 0x8B, 0x8C, 0x33, 0x22, 0xF7, 0x98, 0x4C, 0x8C, 0xA5, 0xE5, 0xE7, 0xC8, 0xDB, 0x68, 0xE1, 0xBF, 0xAC, 0x1E, 0xA9, 0x7B, 0xFD, 0x8A, 0x35, 0x90, - 0x73, 0xEA, 0x12, 0xBF, 0x49, 0xFA, 0x0C, 0x86, 0x55, 0x63, 0xB1, 0x20, 0x06, 0xB4, 0x1A, 0x91, 0xB4, 0xD9, 0xAA, 0x52, 0x3A, 0x2D, 0x8F, 0x69, 0x4A, 0x73, - 0xD4, 0x5C, 0x53, 0x0C, 0x13, 0xA5, 0x42, 0x3C, 0x9F, 0x4E, 0x9C, 0xD1, 0x52, 0x36, 0x82, 0xAB, 0x99, 0xD4, 0x3A, 0xBE, 0xD3, 0x40, 0x64, 0x9E, 0x65, 0x52, - 0xC3, 0x5E, 0xDA, 0x36, 0x6A, 0xB4, 0xE9, 0xBB, 0xC0, 0xA6, 0xA4, 0x23, 0x35, 0xC1, 0x95, 0xF2, 0xCE, 0x98, 0x60, 0xD3, 0xEA, 0x34, 0x09, 0x07, 0x94, 0x04, - 0x8A, 0x30, 0x86, 0x68, 0x9E, 0x03, 0x4C, 0x05, 0xA8, 0xAA, 0xC9, 0xC5, 0x9F, 0x2D, 0xE7, 0xB2, 0x9C, 0x21, 0xE8, 0x4C, 0x87, 0x01, 0x8E, 0x75, 0xE7, 0x4E, - 0x87, 0x46, 0xA3, 0xB5, 0xDF, 0xDA, 0xEF, 0xC0, 0x7F, 0x92, 0xDC, 0x3D, 0xDB, 0xB8, 0xB8, 0x78, 0x53, 0x2C, 0x2F, 0x11, 0x7C, 0xF2, 0x4B, 0x28, 0x69, 0x61, - 0x2C, 0x57, 0x17, 0xEA, 0x9E, 0x14, 0xAF, 0xA5, 0xE8, 0x07, 0x39, 0x23, 0x4C, 0x8A, 0x49, 0x17, 0x37, 0x44, 0x89, 0xB5, 0x14, 0x55, 0xF1, 0xDC, 0xF9, 0xB3, - 0xC9, 0x86, 0xD7, 0xFF, 0xF3, 0xD6, 0x2E, 0x88, 0xE2, 0x6F, 0x6D, 0xE9, 0x85, 0xE5, 0xE2, 0xDD, 0xB7, 0x6D, 0xB4, 0xD2, 0xB5, 0xDE, 0xE4, 0xF9, 0x0C, 0x50, - 0x68, 0x43, 0xC6, 0xE9, 0xC2, 0xC4, 0x2B, 0x35, 0xE7, 0x11, 0xDA, 0x94, 0x90, 0xC1, 0xC4, 0xB4, 0xAC, 0xA6, 0xE5, 0xAC, 0xF2, 0x33, 0x91, 0x6C, 0x4B, 0x5E, - 0xB3, 0xD3, 0x7C, 0x93, 0x2F, 0x4B, 0xED, 0x12, 0x22, 0xD7, 0x5F, 0x82, 0xDA, 0xBF, 0xB7, 0xC3, 0x65, 0xBA, 0x46, 0xB9, 0x81, 0xA2, 0x84, 0x3D, 0x56, 0xEB, - 0x48, 0xC9, 0x94, 0x58, 0x26, 0x98, 0x39, 0xAB, 0xF3, 0x56, 0xA6, 0x3F, 0x9A, 0x95, 0x98, 0x54, 0x2D, 0x1C, 0xCF, 0x64, 0xCB, 0x37, 0x2E, 0xB1, 0x0C, 0xCC, - 0xE0, 0x4B, 0xCD, 0xC6, 0x73, 0x27, 0x26, 0x22, 0xB8, 0x0A, 0x27, 0x54, 0x74, 0x0F, 0xA7, 0x92, 0x72, 0xC0, 0x72, 0x87, 0xF4, 0x58, 0x2D, 0x37, 0xEB, 0x9C, - 0x74, 0x3F, 0xEE, 0x19, 0xF2, 0x46, 0x05, 0x22, 0x7A, 0x10, 0xB4, 0xA7, 0x2E, 0xB9, 0x55, 0x60, 0x66, 0x9F, 0xFF, 0x3D, 0x65, 0xB5, 0xD2, 0xF2, 0x45, 0x00, - 0x3A, 0x00, 0x70, 0x2B, 0x3A, 0xE8, 0x7A, 0x0A, 0x5D, 0xA7, 0x77, 0xA9, 0x62, 0x8F, 0x61, 0x25, 0xB0, 0x56, 0x53, 0x08, 0x37, 0x19, 0x43, 0xA8, 0xDC, 0x54, - 0x83, 0xD1, 0x57, 0x7A, 0xD3, 0x22, 0x13, 0x3F, 0x65, 0xA1, 0x83, 0xE6, 0xA9, 0x9D, 0xEC, 0xE8, 0xD6, 0x14, 0xEA, 0x04, 0xB9, 0x91, 0x23, 0x2C, 0xD8, 0xA5, - 0x5B, 0x9F, 0x14, 0x33, 0x46, 0xCF, 0xC2, 0xC8, 0xD3, 0x55, 0x12, 0xA4, 0xCF, 0x54, 0xCD, 0xD0, 0x66, 0xCE, 0x87, 0x7C, 0x50, 0x0F, 0xF9, 0xB9, 0xD1, 0xEE, - 0x4B, 0xD7, 0x11, 0x32, 0x1A, 0x67, 0x91, 0xC6, 0x2A, 0x5E, 0x4A, 0x43, 0x56, 0xEA, 0x04, 0x59, 0x8C, 0x45, 0x52, 0x45, 0x65, 0x7B, 0x65, 0x56, 0x84, 0x59, - 0xAF, 0xD1, 0x64, 0x1A, 0xBB, 0x39, 0x37, 0x20, 0xED, 0x45, 0x73, 0x35, 0x00, 0xA3, 0x4C, 0x7F, 0x2A, 0xE6, 0x2E, 0xD4, 0x13, 0xF5, 0x7E, 0x2B, 0xA7, 0xCB, - 0x91, 0xE5, 0x78, 0xD9, 0x7E, 0x65, 0x0C, 0x41, 0x7E, 0x4B, 0x5F, 0xD2, 0x11, 0xAF, 0x6A, 0x4A, 0x2B, 0x4F, 0xD4, 0xB8, 0xA5, 0x77, 0x94, 0x86, 0xEE, 0x4C, - 0x9F, 0xCA, 0x76, 0xC7, 0x84, 0xCC, 0xF5, 0x96, 0x34, 0xD2, 0x66, 0xD6, 0xDF, 0x7C, 0x72, 0x03, 0xF3, 0x4D, 0x5C, 0xAB, 0x3B, 0xD5, 0x46, 0x44, 0x1E, 0x46, - 0x63, 0x83, 0x9C, 0xAE, 0x52, 0x04, 0xCC, 0xD4, 0xC3, 0xCC, 0x1C, 0x8F, 0x49, 0x66, 0x95, 0x13, 0xE7, 0xBC, 0xD9, 0xA1, 0xD2, 0x90, 0x96, 0xD3, 0x0A, 0x68, - 0xB2, 0x9D, 0xAE, 0xCA, 0xCC, 0xE1, 0x2A, 0x25, 0xF4, 0xC5, 0x24, 0x24, 0x6D, 0x22, 0x54, 0x61, 0xE5, 0x21, 0x12, 0x15, 0x31, 0x26, 0x23, 0xC7, 0x65, 0x8B, - 0xB8, 0x29, 0x13, 0xFF, 0x72, 0x33, 0x2B, 0x44, 0x2E, 0x2B, 0xDD, 0x6D, 0x25, 0x74, 0x64, 0x6E, 0x74, 0xD0, 0xB7, 0x1D, 0x57, 0xF8, 0x70, 0x9C, 0x56, 0x49, - 0x8F, 0x27, 0x6C, 0x99, 0xA4, 0x4A, 0x43, 0x60, 0xA8, 0x46, 0x14, 0x19, 0xC8, 0x01, 0x5B, 0xAD, 0x2B, 0x34, 0x41, 0x15, 0x5D, 0x5A, 0x39, 0xE0, 0xAB, 0x4D, - 0x7C, 0x61, 0xB0, 0x99, 0xB6, 0xDE, 0xB2, 0xC1, 0xC5, 0x37, 0x6A, 0x01, 0xC9, 0x7E, 0x53, 0x45, 0x73, 0x4F, 0xF9, 0x63, 0x06, 0x91, 0xE1, 0x40, 0x1C, 0x6C, - 0xB7, 0x8A, 0xB7, 0x2A, 0x1B, 0x42, 0xCE, 0x0E, 0x85, 0xFD, 0x71, 0x67, 0x87, 0xD1, 0x56, 0xBE, 0x33, 0xDC, 0x24, 0x27, 0x6E, 0xA3, 0xE3, 0xFD, 0x8C, 0x2C, - 0xC3, 0xF3, 0xCE, 0x6B, 0xB8, 0xD9, 0xAB, 0x16, 0xDF, 0x55, 0x77, 0x36, 0x36, 0xAF, 0x35, 0x73, 0x7C, 0x5E, 0xB3, 0x9C, 0xA9, 0x93, 0xB8, 0x47, 0xEF, 0x33, - 0x2D, 0xC3, 0x68, 0x7F, 0x5E, 0x8B, 0xAD, 0x38, 0xD6, 0x28, 0x54, 0x74, 0xA9, 0x36, 0x78, 0xF2, 0xCD, 0xC9, 0xD1, 0x51, 0xFF, 0xD9, 0x13, 0x7B, 0xE8, 0x2D, - 0xF8, 0xFF, 0x1F, 0xD9, 0x02, 0xAD, 0x47, 0x7C, 0x1F, 0x6C, 0xCE, 0x3B, 0x3B, 0xA4, 0xD8, 0x12, 0x14, 0x1C, 0x02, 0x09, 0x29, 0x44, 0xF1, 0x6C, 0x50, 0x46, - 0x57, 0xD0, 0xC4, 0x83, 0x04, 0x67, 0x68, 0xB8, 0x92, 0x26, 0xB4, 0x19, 0x9B, 0x6B, 0xD0, 0x18, 0x52, 0xA3, 0xCA, 0x18, 0x3A, 0x37, 0x49, 0xD2, 0x29, 0x37, - 0x5C, 0x53, 0xBC, 0x15, 0x19, 0xA7, 0x21, 0x04, 0x30, 0x0A, 0x8E, 0xEB, 0xAC, 0xD0, 0x46, 0xDA, 0x28, 0x26, 0x7B, 0x6C, 0x7C, 0x33, 0xB2, 0xBE, 0x04, 0x4A, - 0xAF, 0x05, 0xDA, 0xB0, 0x1D, 0x9F, 0x8D, 0x24, 0x29, 0x5D, 0xC5, 0x58, 0xE5, 0x30, 0xC2, 0x6A, 0x21, 0xE3, 0x02, 0x44, 0xDB, 0xA4, 0xD8, 0xD9, 0xB5, 0x6C, - 0x4C, 0x14, 0x9B, 0xA0, 0xD0, 0x00, 0xB8, 0x36, 0xF8, 0xF9, 0xE2, 0xBB, 0x7F, 0x6A, 0xEF, 0xDE, 0xFE, 0x29, 0xD5, 0x50, 0x1E, 0x51, 0x18, 0x9C, 0x15, 0x7A, - 0xA6, 0x60, 0x4C, 0x1F, 0x81, 0x4C, 0x6A, 0x5C, 0x33, 0x14, 0x03, 0x26, 0x43, 0x16, 0xB1, 0xA7, 0xFE, 0xEC, 0xBC, 0xA6, 0xD7, 0x70, 0x77, 0x4B, 0x70, 0xD6, - 0xAE, 0x69, 0x18, 0xB8, 0xE9, 0xC1, 0xB5, 0x61, 0x2D, 0xF1, 0xA8, 0xA5, 0xC2, 0xEB, 0xBA, 0x69, 0x49, 0x9B, 0xF1, 0x88, 0x12, 0xCA, 0x58, 0x88, 0xC0, 0x71, - 0x29, 0xD7, 0x06, 0x57, 0xC4, 0x3F, 0x3B, 0x64, 0xB7, 0x72, 0xB4, 0x96, 0xDD, 0x37, 0xB8, 0x30, 0x33, 0x87, 0x2C, 0x13, 0xCA, 0x52, 0xFC, 0xC4, 0x35, 0xE6, - 0x04, 0xA5, 0xA2, 0xA4, 0x79, 0x51, 0xEB, 0x21, 0x64, 0x6D, 0xF0, 0x81, 0xD0, 0x2C, 0x03, 0xC8, 0x50, 0x52, 0xFC, 0x19, 0x4F, 0xE1, 0x63, 0xFD, 0x87, 0xF6, - 0xCC, 0x97, 0xEC, 0x9A, 0x06, 0x33, 0x73, 0x05, 0xB9, 0x3F, 0x6E, 0x36, 0xB5, 0xDE, 0xBB, 0xF7, 0x5A, 0xB3, 0xA9, 0xD0, 0xD8, 0x59, 0x50, 0x77, 0x0A, 0xF4, - 0x0F, 0x16, 0xC2, 0xA8, 0x21, 0x54, 0x3F, 0xEC, 0xA8, 0x36, 0xF8, 0xE1, 0xEA, 0xE7, 0x37, 0x2F, 0x1A, 0xED, 0x5E, 0xBF, 0x75, 0xA3, 0x9F, 0xB4, 0x5B, 0x7B, - 0x67, 0x87, 0x0C, 0xAE, 0x78, 0x07, 0x60, 0x60, 0xEF, 0xB5, 0xD7, 0x6F, 0x5F, 0x35, 0xF4, 0xD6, 0x71, 0x55, 0x64, 0xFA, 0x49, 0x6D, 0xF0, 0xD3, 0x0F, 0x11, - 0x65, 0xFD, 0x56, 0x15, 0x64, 0xC7, 0xC0, 0x26, 0xD0, 0xC5, 0x50, 0x75, 0xBB, 0x85, 0x50, 0xA1, 0xC8, 0x3B, 0xE5, 0x44, 0xAE, 0x1F, 0x41, 0xBF, 0x94, 0x87, - 0x56, 0xF7, 0xF8, 0x46, 0xEF, 0xF5, 0xBB, 0x15, 0x78, 0xE8, 0xA3, 0x74, 0x81, 0x90, 0xC6, 0x71, 0xBF, 0x5B, 0x15, 0x57, 0x0F, 0x71, 0x81, 0x40, 0x8E, 0xDA, - 0x20, 0x8F, 0xF6, 0x71, 0x15, 0xD1, 0x76, 0x6B, 0x03, 0xAA, 0xF2, 0x13, 0x44, 0xD5, 0x2A, 0x86, 0x0A, 0x45, 0xDB, 0x2E, 0x29, 0xDA, 0x4E, 0x6D, 0xF0, 0x23, - 0x8A, 0x16, 0x2D, 0x03, 0x78, 0xA8, 0x64, 0x1E, 0x6D, 0x88, 0x52, 0x14, 0x57, 0x1B, 0xED, 0xB6, 0xD5, 0xAE, 0x22, 0x5A, 0xBD, 0x36, 0x40, 0x71, 0x20, 0xA6, - 0xA3, 0x4A, 0x0E, 0x00, 0xDE, 0x44, 0x69, 0x02, 0x72, 0x6E, 0x8E, 0xFA, 0xC7, 0xE5, 0x31, 0x81, 0x27, 0x5D, 0x7D, 0x02, 0x4C, 0xC7, 0x20, 0xA8, 0x4A, 0x6E, - 0x04, 0x5E, 0x84, 0x78, 0xFA, 0xDD, 0xD6, 0x4D, 0xB7, 0x8A, 0xCD, 0x80, 0x57, 0xBC, 0x45, 0x44, 0x80, 0xE4, 0xA6, 0x53, 0x45, 0x46, 0xE0, 0x12, 0x17, 0xDF, - 0xBE, 0x6E, 0x74, 0x81, 0xB1, 0xF6, 0x49, 0xBF, 0x3C, 0x1E, 0x70, 0x87, 0x1F, 0x90, 0x20, 0x20, 0xE6, 0xA6, 0x5D, 0x2C, 0x3A, 0xC4, 0x11, 0x81, 0x33, 0x00, - 0x3C, 0xE2, 0x28, 0x8D, 0x02, 0xEC, 0xFA, 0x2D, 0x25, 0x06, 0x11, 0xE9, 0x47, 0x15, 0xB8, 0x02, 0xAB, 0xFE, 0x01, 0xC5, 0x03, 0x48, 0x30, 0xE8, 0x55, 0x30, - 0x45, 0x40, 0x44, 0x49, 0xD2, 0xFB, 0xD4, 0xD5, 0xCA, 0x63, 0x02, 0x9B, 0x3E, 0xE9, 0xDF, 0x9C, 0xF4, 0xD5, 0x10, 0xE0, 0x88, 0x8F, 0xA3, 0x54, 0x56, 0x4E, - 0x90, 0x9D, 0x32, 0x64, 0xA5, 0x03, 0xFF, 0x5E, 0x1A, 0x16, 0xCC, 0x6F, 0x0A, 0x27, 0x03, 0x1C, 0x0E, 0x64, 0xC2, 0x0E, 0xD4, 0xF2, 0x00, 0x81, 0x92, 0x70, - 0xA3, 0x59, 0x6D, 0xD0, 0x55, 0xC8, 0xB7, 0x62, 0x09, 0x39, 0x85, 0x8D, 0xD1, 0x4F, 0x93, 0x40, 0xB4, 0x3C, 0x4C, 0xFF, 0xC0, 0x25, 0x3A, 0x35, 0x21, 0x82, - 0x94, 0x4A, 0x34, 0x24, 0xB4, 0x1A, 0x37, 0xB5, 0x41, 0xBF, 0x93, 0x9B, 0xA0, 0x95, 0x57, 0xC6, 0x90, 0xD6, 0x68, 0x6C, 0xE2, 0x79, 0x85, 0xF5, 0x11, 0x81, - 0xD6, 0x06, 0x2F, 0xC3, 0xE3, 0x2A, 0x5A, 0x69, 0xE6, 0x71, 0x4A, 0x61, 0x53, 0xD4, 0x22, 0x90, 0xC3, 0x34, 0xD3, 0xEC, 0x70, 0xD5, 0x44, 0x9A, 0xD9, 0xAC, - 0x62, 0xB6, 0xA9, 0x17, 0x9C, 0x4E, 0xBA, 0x86, 0xE7, 0x17, 0xD6, 0x4A, 0x00, 0x08, 0x11, 0x9A, 0x1F, 0xDD, 0x9B, 0x46, 0x42, 0x52, 0xFE, 0x06, 0xFA, 0xF0, - 0x0C, 0x7F, 0xC9, 0xAA, 0x85, 0x85, 0x35, 0x12, 0x81, 0x42, 0x3E, 0x10, 0x1E, 0x57, 0xD2, 0x4A, 0x95, 0xF0, 0x25, 0x90, 0xC3, 0xF5, 0x12, 0x84, 0xB0, 0xEE, - 0x96, 0xF4, 0x92, 0x47, 0x6D, 0x25, 0xBD, 0xCC, 0x0C, 0x77, 0x51, 0x2A, 0x7C, 0x85, 0x90, 0xA0, 0x95, 0xE0, 0xF0, 0xDE, 0x5C, 0x25, 0x22, 0xE6, 0x6F, 0xE0, - 0x2B, 0x63, 0x62, 0x3B, 0xA6, 0x57, 0x7C, 0xB6, 0xCF, 0xE1, 0x6A, 0x83, 0x57, 0xA4, 0xF9, 0x3D, 0x1E, 0x55, 0x51, 0xC7, 0x8B, 0xA5, 0xEF, 0x54, 0x50, 0x48, - 0x40, 0x0B, 0x53, 0x47, 0x8B, 0x6B, 0xE3, 0x78, 0x4B, 0xDA, 0x38, 0xDE, 0xA2, 0x36, 0x0C, 0xF2, 0xD9, 0x22, 0xD7, 0xC4, 0x2A, 0xAC, 0x8E, 0x00, 0xB0, 0x36, - 0xB8, 0xBC, 0x59, 0x38, 0x1E, 0x3E, 0x3A, 0xF5, 0x1D, 0x9E, 0x57, 0x72, 0x92, 0x5E, 0x05, 0x9D, 0x84, 0x04, 0x71, 0x1F, 0xE9, 0x71, 0xAD, 0xF4, 0xB6, 0xA4, - 0x95, 0x3C, 0x5A, 0xAB, 0x68, 0x65, 0x6A, 0x98, 0xF6, 0x88, 0x98, 0x16, 0x3E, 0xC6, 0x51, 0x54, 0x31, 0x02, 0x6C, 0x6D, 0xF0, 0x26, 0x3A, 0xA9, 0xA2, 0x98, - 0x56, 0x05, 0xBD, 0x88, 0xF4, 0xC4, 0xFD, 0xA5, 0x07, 0xB3, 0xF2, 0x2D, 0xE9, 0x46, 0xD7, 0xB7, 0x39, 0xAA, 0x2C, 0xC8, 0xC8, 0x34, 0xAC, 0xCF, 0x64, 0x32, - 0x81, 0x69, 0x50, 0xF1, 0xA1, 0x25, 0x06, 0x0E, 0xE3, 0x0B, 0x3B, 0xD7, 0x2E, 0xE9, 0x79, 0xE1, 0xFA, 0x65, 0x02, 0x5D, 0xF9, 0x22, 0x66, 0x72, 0x4E, 0x28, - 0x2D, 0x4B, 0x7E, 0xEF, 0x84, 0x74, 0x96, 0x9F, 0xB6, 0x7E, 0x4F, 0xA6, 0x74, 0x1B, 0x41, 0x95, 0x39, 0xF4, 0x1B, 0xD7, 0xB8, 0xA5, 0xEF, 0x64, 0xA8, 0x32, - 0xA5, 0xFF, 0x40, 0xC6, 0xDA, 0x47, 0xD3, 0x2E, 0xCF, 0x4C, 0x17, 0x09, 0x21, 0xC4, 0xAE, 0x86, 0xA5, 0x07, 0x53, 0x24, 0x38, 0xA8, 0x86, 0xA4, 0x8F, 0x35, - 0xFD, 0x85, 0x69, 0x3C, 0x84, 0x49, 0xBC, 0xB1, 0x1A, 0x16, 0x1F, 0x50, 0x56, 0x43, 0x18, 0x97, 0x7F, 0x7A, 0xA9, 0x5D, 0xD2, 0x8D, 0xEF, 0x85, 0xC3, 0x15, - 0xDB, 0x93, 0xA7, 0x62, 0xE8, 0xD1, 0xD2, 0x0D, 0xF6, 0xB9, 0xB6, 0xA6, 0x26, 0x77, 0x20, 0xD5, 0x75, 0x35, 0x09, 0x7B, 0x01, 0x81, 0x74, 0x0B, 0x53, 0x4D, - 0xE0, 0x56, 0x8D, 0xC7, 0x2D, 0xA6, 0x62, 0xA3, 0x55, 0xF1, 0x34, 0x6C, 0xB4, 0x02, 0x35, 0x8D, 0xAF, 0xF1, 0x99, 0x88, 0xB1, 0x06, 0xFA, 0xDA, 0x89, 0xA2, - 0xB0, 0xD7, 0xFB, 0x51, 0x14, 0xE5, 0xF7, 0xBE, 0x15, 0x05, 0xD6, 0xF2, 0x19, 0xC7, 0xD1, 0x32, 0x4E, 0x45, 0x01, 0x6B, 0x83, 0x77, 0x86, 0xBD, 0x84, 0x41, - 0x66, 0x57, 0x0A, 0x0B, 0x3B, 0xBE, 0x37, 0xF7, 0xE2, 0x7C, 0xDF, 0xB7, 0xEA, 0x80, 0x90, 0xB9, 0x33, 0x2E, 0x3E, 0xDD, 0xE1, 0x70, 0x2C, 0x24, 0xBE, 0x83, - 0xA3, 0xC2, 0x89, 0x41, 0x80, 0x61, 0xCB, 0x19, 0x01, 0x9B, 0x4A, 0x95, 0x4F, 0x06, 0xAE, 0x96, 0xB6, 0x7D, 0x5B, 0x25, 0x13, 0xB8, 0xB0, 0x9C, 0xE5, 0xB8, - 0x3C, 0x06, 0x48, 0x03, 0xFE, 0x35, 0x99, 0x98, 0xA3, 0xF2, 0x89, 0x04, 0x24, 0x01, 0x6F, 0x9D, 0xB9, 0x22, 0xFC, 0x96, 0x07, 0x5E, 0x32, 0x2A, 0x31, 0x93, - 0x1B, 0x81, 0x16, 0x2F, 0x2F, 0x76, 0x3A, 0xF0, 0x42, 0x9F, 0xF7, 0x14, 0x19, 0x90, 0xDB, 0xFB, 0x0E, 0x0A, 0x40, 0xC4, 0x67, 0x6A, 0x3C, 0x65, 0x94, 0xC5, - 0x20, 0xC3, 0x88, 0x1E, 0x4C, 0xBF, 0xEF, 0x6B, 0x7E, 0x17, 0x51, 0x14, 0x9F, 0xDD, 0xE1, 0xD2, 0x73, 0x38, 0xBD, 0xEB, 0xB4, 0x37, 0x3B, 0xC1, 0x43, 0xE4, - 0xDB, 0xD5, 0x4F, 0xBB, 0x8C, 0x6A, 0x20, 0x1A, 0x7D, 0x8F, 0xEB, 0x0C, 0x05, 0x02, 0x76, 0x75, 0x47, 0x6A, 0xDF, 0x9F, 0x27, 0xB5, 0x1F, 0x80, 0x2B, 0x4D, - 0x4B, 0x44, 0xBC, 0x29, 0x46, 0xBC, 0x37, 0x17, 0xBB, 0xD1, 0xD0, 0xF4, 0xDE, 0x42, 0xDD, 0xF4, 0x5E, 0x43, 0x9D, 0xC6, 0x37, 0x05, 0x06, 0x52, 0x28, 0x99, - 0xC1, 0x72, 0x40, 0x56, 0xCB, 0xAA, 0x12, 0xE4, 0xF4, 0x9B, 0x2A, 0x51, 0x2E, 0x20, 0x23, 0x1E, 0xE4, 0xFA, 0xD1, 0xAA, 0x48, 0x6F, 0xB3, 0xCB, 0xBA, 0xDD, - 0x3C, 0x6A, 0xAB, 0x38, 0x8D, 0x6B, 0xAC, 0x3E, 0x4F, 0xE7, 0x46, 0x61, 0x65, 0x70, 0x38, 0xD0, 0xC5, 0xBB, 0x17, 0xBB, 0x4C, 0x17, 0x82, 0x7E, 0xEF, 0xC7, - 0x8F, 0x42, 0xAE, 0xEF, 0x3B, 0xD6, 0x59, 0xC4, 0x2E, 0x1E, 0xEC, 0x10, 0xA8, 0x36, 0xF8, 0x8E, 0xD8, 0x9E, 0x76, 0xE1, 0xB8, 0xFC, 0x45, 0x98, 0x3B, 0xD1, - 0x1A, 0xED, 0xF9, 0x7E, 0x54, 0xC6, 0x98, 0xBE, 0x6F, 0x7D, 0xCD, 0xE6, 0xA6, 0xEB, 0x3A, 0x6E, 0x61, 0x95, 0x71, 0x38, 0x98, 0x56, 0x34, 0xDF, 0xD1, 0xA3, - 0x9D, 0xA8, 0x2B, 0xE8, 0xF5, 0x7E, 0x34, 0x16, 0xF2, 0x7C, 0xDF, 0x4A, 0xBB, 0x9E, 0x58, 0xE6, 0xA2, 0xB0, 0xCA, 0x28, 0x54, 0x6D, 0xF0, 0xA9, 0xF9, 0x1A, - 0xFE, 0xEE, 0x44, 0x5D, 0xAC, 0xC7, 0xFB, 0x51, 0x16, 0xE7, 0xF6, 0xBE, 0x55, 0x35, 0x5C, 0x14, 0x0F, 0x87, 0x00, 0x53, 0x1B, 0xBC, 0x7C, 0xBF, 0x9B, 0xDC, - 0x0F, 0x3B, 0x53, 0xD4, 0x50, 0x25, 0x7D, 0x50, 0xA6, 0xEE, 0x5B, 0x1B, 0xAB, 0x12, 0xDA, 0x58, 0x21, 0xE1, 0x3F, 0xED, 0x48, 0x1B, 0x2B, 0x75, 0x6D, 0x6C, - 0xD8, 0x5F, 0x56, 0x0F, 0x41, 0x3F, 0xF4, 0xE9, 0xD3, 0xA1, 0x51, 0x7C, 0x38, 0x0A, 0x00, 0x71, 0xD3, 0x18, 0x1C, 0x69, 0x2F, 0x8D, 0xDD, 0x0C, 0x48, 0x61, - 0xBF, 0xBB, 0x70, 0xA1, 0x88, 0xC9, 0xFB, 0xD6, 0xD3, 0xC4, 0x18, 0x91, 0xCF, 0x63, 0xE2, 0x97, 0x59, 0x5B, 0x16, 0x60, 0x6B, 0x83, 0xD7, 0x70, 0xA2, 0xBD, - 0xA2, 0x27, 0xBB, 0x4A, 0xF9, 0xC4, 0xFE, 0x77, 0xA1, 0xB5, 0x18, 0xBF, 0x0F, 0x42, 0x71, 0x90, 0x60, 0x3B, 0x53, 0xBB, 0xD4, 0xE3, 0x4C, 0x31, 0x70, 0xAE, - 0xBE, 0x0F, 0xEC, 0x7C, 0xB7, 0x0A, 0x8C, 0x88, 0xD8, 0x99, 0x0E, 0x05, 0xBE, 0x37, 0xA8, 0x46, 0xC5, 0xA7, 0x1A, 0xF9, 0x9B, 0x81, 0xF3, 0x74, 0xC5, 0x9F, - 0xAE, 0xA3, 0x9B, 0x5A, 0x88, 0xDF, 0xF4, 0x7C, 0xD3, 0xB2, 0x60, 0x2A, 0x4C, 0x7C, 0xED, 0x0A, 0x0F, 0x15, 0x1F, 0xA7, 0x13, 0xB0, 0x04, 0x0F, 0xD1, 0xFA, - 0x2E, 0x31, 0xE6, 0xB5, 0xC1, 0x15, 0xBE, 0x33, 0x19, 0x70, 0xE1, 0x59, 0x71, 0x64, 0x54, 0x8C, 0xC4, 0x76, 0x1D, 0x20, 0x2A, 0x54, 0x13, 0x7F, 0x3F, 0x65, - 0x4D, 0x0B, 0x8E, 0x84, 0x6B, 0x83, 0x4B, 0xDA, 0x58, 0x43, 0x3B, 0xCB, 0xEF, 0x4E, 0xF9, 0x39, 0x3F, 0xFA, 0x44, 0x2F, 0x3E, 0xA2, 0x1B, 0x7F, 0xA3, 0x3A, - 0xE8, 0x95, 0xBD, 0xD5, 0x60, 0x70, 0x46, 0xDF, 0x0E, 0xCB, 0x9B, 0xD1, 0x87, 0xD9, 0x57, 0xFC, 0xE9, 0xE4, 0xA1, 0x63, 0x8D, 0x9F, 0x09, 0xAB, 0xCB, 0x57, - 0xE1, 0xE3, 0xB6, 0x08, 0x02, 0x86, 0x11, 0x60, 0xC8, 0x51, 0xFE, 0xCC, 0x0D, 0xD0, 0xB3, 0x27, 0xA2, 0xF1, 0x1D, 0x65, 0x19, 0xCA, 0x4D, 0x79, 0x34, 0xD8, - 0x25, 0xD3, 0x50, 0x90, 0xB2, 0x27, 0xC6, 0xA5, 0x0F, 0x0A, 0x7F, 0x20, 0x53, 0xD3, 0x03, 0x1A, 0x35, 0x30, 0x8B, 0x43, 0xFA, 0x8C, 0x25, 0x33, 0x66, 0xB5, - 0xE7, 0x77, 0xC5, 0x2E, 0xF9, 0xCB, 0x19, 0xA4, 0x8F, 0x63, 0x17, 0xCA, 0x4F, 0x92, 0x0F, 0x4F, 0xC7, 0x31, 0xE6, 0x19, 0xFD, 0xE3, 0x66, 0x73, 0xD6, 0xC5, - 0xA7, 0x45, 0xB5, 0x80, 0xB5, 0xB3, 0xC3, 0x59, 0x37, 0xEF, 0xD1, 0xB0, 0xDC, 0x47, 0x7D, 0x81, 0xD3, 0xD2, 0x4F, 0xFA, 0xA2, 0x94, 0x06, 0x40, 0xCD, 0xBE, - 0xF6, 0xCE, 0xF0, 0xBE, 0xEC, 0x6B, 0x9F, 0xB0, 0x00, 0xB7, 0xC3, 0x07, 0x7E, 0x91, 0x76, 0x63, 0x3C, 0x76, 0x53, 0x1F, 0xFA, 0xED, 0xC6, 0x1E, 0xFA, 0xED, - 0x07, 0x0F, 0xFD, 0xF6, 0xA3, 0xDD, 0x6E, 0x37, 0x9D, 0x56, 0xEB, 0x58, 0x85, 0x75, 0xC5, 0x07, 0x7F, 0x37, 0xC2, 0xD3, 0x1C, 0xA4, 0xA9, 0xC8, 0x53, 0x37, - 0xE0, 0x49, 0xD8, 0x15, 0x7E, 0x33, 0x99, 0x3C, 0x34, 0x8E, 0xF8, 0xBA, 0x54, 0x79, 0x96, 0x5A, 0xED, 0x5D, 0x3F, 0x9D, 0x4D, 0x8D, 0x7B, 0x53, 0x0F, 0x67, - 0xD3, 0x26, 0xC9, 0x68, 0xD8, 0xCB, 0x0C, 0x86, 0x14, 0x84, 0x39, 0xFD, 0x9B, 0x4D, 0x3A, 0xFD, 0xB4, 0x82, 0xD3, 0x4F, 0xD7, 0x9C, 0x7E, 0x87, 0xDE, 0x1E, - 0x10, 0xFE, 0x77, 0xF3, 0xF8, 0x80, 0xAF, 0x02, 0x5E, 0x2F, 0xE5, 0xAB, 0xD5, 0xDA, 0xA8, 0xDF, 0xE7, 0x3A, 0x49, 0x68, 0x0C, 0x6F, 0x36, 0xE9, 0x24, 0x29, - 0xA6, 0x5B, 0xCA, 0x4E, 0x79, 0xD8, 0x19, 0xEC, 0x66, 0x5C, 0xA2, 0xD9, 0x94, 0xA8, 0x50, 0xDE, 0x3B, 0x3E, 0x13, 0xDA, 0xE9, 0xF2, 0xD4, 0x69, 0x13, 0xEA, - 0x51, 0x7F, 0x11, 0x44, 0x6A, 0x93, 0xCD, 0x24, 0x66, 0x0B, 0x21, 0xC3, 0x55, 0x4E, 0xCC, 0xDE, 0x7F, 0xF7, 0x5D, 0xB1, 0x5C, 0x4C, 0xEC, 0xE5, 0x81, 0xE4, - 0x62, 0x99, 0xB5, 0xD0, 0xDB, 0x05, 0xDC, 0x40, 0xAA, 0x4B, 0x99, 0x6E, 0x04, 0x5E, 0x1B, 0xBC, 0xA4, 0xC7, 0x9A, 0x20, 0xB1, 0x42, 0xC6, 0xAB, 0x3C, 0xED, - 0xA4, 0x80, 0x42, 0xB1, 0x34, 0x22, 0x21, 0xA9, 0x1B, 0x45, 0x5C, 0x19, 0x05, 0x52, 0x81, 0x3D, 0x75, 0xA6, 0x2A, 0xFB, 0x04, 0x6D, 0x92, 0x97, 0x0A, 0x2F, - 0x5C, 0x52, 0x5A, 0x6D, 0x1C, 0xB6, 0x36, 0x78, 0xEF, 0x12, 0xED, 0x95, 0x79, 0xAD, 0xCE, 0x9B, 0xB0, 0x51, 0x30, 0x44, 0xA2, 0x26, 0xE5, 0xE4, 0x0E, 0x3E, - 0xE9, 0xAE, 0x40, 0x5C, 0x6C, 0x57, 0xDD, 0x4E, 0x27, 0xC1, 0x0A, 0x69, 0x57, 0xBB, 0x1A, 0x86, 0x4E, 0x6D, 0xD0, 0xA9, 0x86, 0xA1, 0x5B, 0x1B, 0x74, 0xAB, - 0x61, 0xE8, 0x81, 0x1C, 0x0E, 0x7A, 0xD5, 0x70, 0xF4, 0x6B, 0x83, 0x7E, 0x35, 0x0C, 0x47, 0x20, 0xCB, 0xAA, 0x54, 0x40, 0xE6, 0x72, 0x5C, 0x00, 0x43, 0xFE, - 0x26, 0x47, 0xD6, 0xAA, 0xBA, 0xF3, 0xCC, 0x97, 0x56, 0x69, 0xE7, 0xE1, 0xB0, 0xB5, 0xC1, 0xBB, 0xA5, 0xE5, 0x9B, 0x0B, 0xCB, 0x84, 0x69, 0x7B, 0xA3, 0xAB, - 0x35, 0xB5, 0x76, 0xAF, 0xBD, 0xB7, 0xC3, 0x0C, 0x33, 0xA0, 0x43, 0xED, 0x1D, 0x52, 0x9D, 0x20, 0x09, 0xD3, 0x8F, 0xC5, 0x77, 0x0A, 0x3C, 0x88, 0x70, 0xE6, - 0x3A, 0x8E, 0x5F, 0x5A, 0x1D, 0x01, 0x30, 0xA4, 0xF9, 0x70, 0x54, 0x3A, 0x9A, 0x45, 0x68, 0xCA, 0x18, 0x7A, 0xCA, 0x26, 0xE7, 0x8A, 0xE1, 0x4C, 0x2F, 0x16, - 0xCE, 0x76, 0xE7, 0x3E, 0xDE, 0x6D, 0xF9, 0x94, 0x81, 0xC3, 0xC2, 0x6C, 0xF5, 0x16, 0x66, 0x88, 0x73, 0x54, 0x98, 0xD6, 0x68, 0x81, 0xFB, 0xE8, 0xBD, 0x5D, - 0x7A, 0x4F, 0x40, 0x46, 0xC1, 0x37, 0xB0, 0x89, 0xDE, 0xF3, 0x30, 0x9C, 0x87, 0xEA, 0x83, 0x58, 0xE3, 0x5E, 0x79, 0x8D, 0x04, 0xD0, 0x90, 0x0F, 0xE0, 0x7B, - 0xF0, 0x2A, 0xF9, 0x91, 0x80, 0xAC, 0x9C, 0x23, 0x55, 0x77, 0x1A, 0xA9, 0x2B, 0x56, 0xCC, 0x0B, 0xDA, 0x95, 0x47, 0xF5, 0xCE, 0x43, 0x1C, 0x0B, 0x17, 0xF8, - 0xEA, 0x44, 0xA2, 0xB6, 0xBD, 0x92, 0x22, 0x13, 0x93, 0x48, 0x06, 0xCB, 0x8D, 0x86, 0x6D, 0x2B, 0xDF, 0x69, 0xE6, 0x1F, 0x10, 0xB0, 0xBE, 0xDA, 0x54, 0x7C, - 0x15, 0x5E, 0x60, 0x4E, 0x36, 0x11, 0x08, 0x79, 0x7D, 0x70, 0xB3, 0x00, 0x24, 0xAC, 0xF4, 0x34, 0x80, 0x03, 0x73, 0x15, 0x86, 0x91, 0xB8, 0xA3, 0x17, 0x88, - 0xC4, 0xE2, 0x8C, 0x20, 0xC4, 0x57, 0x72, 0xB4, 0xBB, 0xF7, 0xF4, 0xBF, 0x2B, 0x0F, 0x1E, 0x15, 0x5D, 0xBF, 0x60, 0x1A, 0x2C, 0xC1, 0x80, 0xEF, 0x0B, 0xD4, - 0x8B, 0x24, 0xF4, 0x9B, 0x0B, 0x1F, 0xC2, 0x5A, 0x21, 0x35, 0x38, 0x3A, 0x74, 0x13, 0x9F, 0xCD, 0xF3, 0x0B, 0x2C, 0xFF, 0xA5, 0x36, 0xD9, 0x4C, 0x75, 0x67, - 0x65, 0xDA, 0xC5, 0xAB, 0x3B, 0x3F, 0x99, 0xF6, 0xD8, 0x59, 0x15, 0x2B, 0xF0, 0x88, 0x1D, 0xFD, 0x05, 0x0A, 0x3C, 0x34, 0x3D, 0xC0, 0x15, 0xE2, 0xA6, 0x4B, - 0xD4, 0xDE, 0x67, 0x93, 0x14, 0x32, 0x83, 0xBE, 0xC1, 0x05, 0x56, 0x40, 0xE1, 0x69, 0x74, 0xBD, 0x79, 0xDB, 0x99, 0xDA, 0xCF, 0xA7, 0x62, 0xAE, 0xC6, 0x29, - 0x50, 0xCB, 0xD5, 0xBA, 0x92, 0x72, 0xF3, 0xBD, 0x57, 0xD0, 0x7F, 0x59, 0xE7, 0xE7, 0xF6, 0xDE, 0xF9, 0xD9, 0xC4, 0x00, 0x44, 0xEC, 0x71, 0x69, 0xCB, 0x42, - 0xD8, 0xC8, 0xAE, 0x2E, 0xED, 0xF1, 0x4E, 0xAD, 0x8A, 0xF5, 0x5E, 0x5A, 0x07, 0xED, 0x7E, 0xBB, 0xF3, 0xB0, 0xCC, 0x0A, 0x19, 0xAA, 0x60, 0x54, 0xFA, 0x49, - 0xEF, 0x01, 0x4D, 0x69, 0x9C, 0xC9, 0x84, 0xAD, 0x6B, 0x96, 0x33, 0x2D, 0x0E, 0x7E, 0x43, 0x9F, 0xD2, 0xF5, 0xC8, 0x6E, 0xE3, 0x55, 0xD8, 0x79, 0xC1, 0xD2, - 0x8C, 0xA0, 0x8B, 0xFE, 0xC3, 0x32, 0x2D, 0xCE, 0x91, 0xAA, 0x75, 0x49, 0x38, 0xEA, 0x3E, 0x1C, 0xD3, 0xF2, 0x1D, 0xDF, 0xB0, 0x4A, 0x5B, 0x16, 0x83, 0x06, - 0xC3, 0xFA, 0x88, 0x07, 0xDA, 0x15, 0xF0, 0xB9, 0x53, 0xE3, 0x0A, 0xFA, 0x2F, 0x1F, 0xB8, 0x8E, 0xBB, 0x1B, 0x52, 0x46, 0x05, 0x96, 0x7E, 0x59, 0x67, 0xA9, - 0x52, 0xE8, 0xEA, 0x6F, 0x68, 0x91, 0x7C, 0x23, 0xA1, 0x6B, 0xE9, 0xE3, 0xD5, 0xD2, 0xA1, 0x8B, 0x81, 0x63, 0xE8, 0xA2, 0x47, 0xBB, 0x37, 0xB1, 0x90, 0x82, - 0xF2, 0x36, 0xD6, 0x3B, 0xD9, 0xE4, 0x16, 0x98, 0x4D, 0x44, 0x30, 0xC6, 0x53, 0x25, 0x23, 0xDB, 0x94, 0xDF, 0x54, 0x36, 0xB2, 0x91, 0xA1, 0xFC, 0x8E, 0x37, - 0x8A, 0x4C, 0xCC, 0xE6, 0x19, 0x2C, 0xCC, 0xE1, 0xD8, 0xC1, 0x4E, 0x2B, 0x36, 0x41, 0xE7, 0x1B, 0x5F, 0xA8, 0x0D, 0xB9, 0x7A, 0x48, 0xF5, 0x99, 0xA1, 0x69, - 0xDB, 0x65, 0xD5, 0xC4, 0x61, 0x6B, 0x83, 0x97, 0xEC, 0x60, 0xB7, 0x4B, 0xEA, 0xBC, 0xF3, 0xCD, 0xAF, 0xA7, 0x07, 0x5C, 0xED, 0x5A, 0x4D, 0x89, 0x22, 0x86, - 0x1B, 0x7E, 0xF7, 0xA3, 0xC6, 0xF7, 0xA8, 0x46, 0xDF, 0x01, 0x79, 0x38, 0x25, 0x8D, 0xA9, 0x31, 0xC7, 0x87, 0x97, 0x8B, 0x16, 0x35, 0xDE, 0x20, 0x58, 0xB1, - 0x9A, 0x46, 0xBC, 0xA7, 0x87, 0x5D, 0xD5, 0x18, 0xC4, 0xDF, 0x5A, 0x09, 0x84, 0x37, 0x87, 0xA6, 0xE1, 0xE1, 0x83, 0xFE, 0x70, 0xAC, 0xBD, 0x84, 0x63, 0xED, - 0xBD, 0xB5, 0x0C, 0x5F, 0xBB, 0x2B, 0x73, 0x08, 0x71, 0x3F, 0x5B, 0x84, 0x21, 0xED, 0xA9, 0x05, 0xBA, 0x8D, 0x8F, 0x3F, 0xE0, 0x05, 0xC7, 0xB8, 0x7B, 0xAD, - 0xD7, 0x3D, 0x6E, 0xD5, 0x34, 0x96, 0x15, 0xF3, 0xC7, 0xFA, 0xBD, 0x2F, 0x74, 0x5B, 0x9B, 0x1E, 0x12, 0x28, 0x73, 0x00, 0x91, 0xDE, 0x90, 0x40, 0x6A, 0xBF, - 0x55, 0x76, 0x9B, 0xAD, 0x4B, 0x44, 0x0F, 0xC4, 0xD1, 0x92, 0x1A, 0x42, 0xEC, 0x3D, 0x9B, 0xAC, 0x7D, 0xFC, 0x05, 0x05, 0xED, 0x9E, 0xEC, 0xFD, 0xA7, 0x72, - 0x41, 0xE8, 0x52, 0x41, 0xE0, 0xEE, 0xBE, 0xCD, 0xF2, 0xD4, 0x0E, 0x78, 0xD2, 0xD5, 0x78, 0x6A, 0x57, 0xE0, 0xA9, 0xBD, 0x23, 0x9E, 0x3A, 0x01, 0x4F, 0x6D, - 0x35, 0x9E, 0x3A, 0x15, 0x78, 0xEA, 0xEC, 0x88, 0xA7, 0x6E, 0xC0, 0x53, 0x47, 0x8D, 0xA7, 0x6E, 0x05, 0x9E, 0xBA, 0x3B, 0xE2, 0xA9, 0x17, 0xF0, 0xD4, 0x55, - 0xE3, 0xA9, 0x57, 0x81, 0xA7, 0xDE, 0x8E, 0x78, 0xEA, 0x07, 0x3C, 0xF5, 0xD4, 0x78, 0xEA, 0x57, 0xE0, 0xA9, 0xBF, 0x23, 0x9E, 0x8E, 0x02, 0x9E, 0xFA, 0x6A, - 0x3C, 0x1D, 0x55, 0xE0, 0xE9, 0x68, 0x47, 0x3C, 0x1D, 0x07, 0x3C, 0x1D, 0xA9, 0xF1, 0x74, 0x5C, 0x81, 0xA7, 0xE3, 0x1D, 0xF1, 0x74, 0x12, 0xF0, 0x74, 0xAC, - 0xC6, 0xD3, 0x49, 0x05, 0x9E, 0x4E, 0x76, 0xC4, 0x13, 0xEE, 0xA6, 0x62, 0x4C, 0x9D, 0x28, 0x0E, 0xBA, 0xAD, 0x0A, 0x5C, 0x19, 0xBB, 0xE2, 0x2A, 0x4C, 0x25, - 0x74, 0xD5, 0x5C, 0xA2, 0x4A, 0x32, 0x31, 0xDC, 0x15, 0x5B, 0x51, 0x36, 0xA1, 0x98, 0x4E, 0xE8, 0x55, 0xF2, 0x89, 0xD1, 0xAE, 0xD8, 0x0A, 0x13, 0x0A, 0x5D, - 0x31, 0xA3, 0xD0, 0xAB, 0xA4, 0x14, 0xE3, 0x5D, 0xB1, 0x15, 0xE6, 0x14, 0xBA, 0x62, 0x52, 0xA1, 0x57, 0xC9, 0x2A, 0xC8, 0xAE, 0xD8, 0x0A, 0xD3, 0x0A, 0x5D, - 0x31, 0xAF, 0xD0, 0xAB, 0x24, 0x16, 0x93, 0x5D, 0xB1, 0x15, 0x66, 0x16, 0xBA, 0x62, 0x6A, 0xA1, 0x57, 0xC8, 0x2D, 0x4E, 0xE4, 0x13, 0xB1, 0x8D, 0xB2, 0x45, - 0x7C, 0x3E, 0x45, 0x8E, 0x26, 0x6D, 0x4A, 0x0F, 0x1C, 0x71, 0x20, 0x7C, 0x22, 0x8E, 0x09, 0xE4, 0xC2, 0xB1, 0x27, 0xE6, 0x34, 0x2C, 0x32, 0x3C, 0x98, 0x67, - 0x63, 0x3C, 0xE1, 0x85, 0xBF, 0xCA, 0x85, 0x86, 0xAB, 0x57, 0x97, 0xC5, 0xCA, 0x0C, 0x62, 0x2F, 0x7F, 0xA1, 0x22, 0x03, 0x90, 0xDD, 0x16, 0xBF, 0x3E, 0xA0, - 0x54, 0x57, 0xA0, 0x40, 0x45, 0x2A, 0x0A, 0x3D, 0xB1, 0xA2, 0xD0, 0x57, 0xAE, 0x28, 0x30, 0xE2, 0xB6, 0x53, 0x4B, 0x00, 0xDC, 0x1D, 0xF6, 0xC9, 0x04, 0x75, - 0xA6, 0x3B, 0xE5, 0x99, 0xEE, 0x15, 0x61, 0xBA, 0x53, 0x86, 0xE9, 0x12, 0xCF, 0xB4, 0x2A, 0xCA, 0x09, 0xE8, 0x7D, 0x6D, 0xDE, 0x90, 0xB1, 0xF6, 0x8B, 0xBA, - 0xA8, 0xF4, 0xF2, 0xA2, 0x3A, 0x2A, 0x22, 0x2A, 0x7D, 0x8B, 0xF6, 0xD1, 0x0B, 0xF8, 0xFE, 0x51, 0x9D, 0xEF, 0x5E, 0x79, 0xBE, 0x3B, 0x45, 0xF8, 0xEE, 0x6D, - 0x91, 0xEF, 0x6E, 0xC0, 0xF7, 0x27, 0x75, 0xBE, 0xBB, 0xE5, 0xF9, 0xEE, 0x16, 0xE1, 0xBB, 0xBB, 0x45, 0xBE, 0xDB, 0x10, 0x6C, 0x7E, 0xFC, 0xA4, 0x7D, 0x9C, - 0xB9, 0xC4, 0x9B, 0xE5, 0x57, 0xE2, 0x18, 0x44, 0xD9, 0xB1, 0xBD, 0xB7, 0x83, 0xB9, 0x1B, 0x52, 0xD8, 0x11, 0x79, 0xCA, 0xCD, 0x9B, 0x19, 0x84, 0xCA, 0x37, - 0x89, 0xE4, 0x3C, 0xC9, 0x67, 0x6E, 0xBA, 0x2A, 0x53, 0xDB, 0x8B, 0x61, 0xC7, 0xB5, 0xC1, 0xDB, 0x65, 0x81, 0xF1, 0xED, 0xB8, 0xBC, 0x3D, 0xAB, 0x57, 0xCC, - 0x19, 0x5D, 0x5B, 0xB3, 0xE7, 0x13, 0xCA, 0x33, 0xE4, 0x65, 0x9E, 0x82, 0xDA, 0xCB, 0x57, 0x21, 0x7A, 0x3B, 0xA8, 0x92, 0x63, 0xA4, 0x3F, 0x62, 0xEC, 0xFC, - 0x88, 0x0C, 0x69, 0x90, 0xB1, 0x14, 0x18, 0x8C, 0x8E, 0x0A, 0x6A, 0xF3, 0xB8, 0x64, 0x74, 0x42, 0x1A, 0xB7, 0xA6, 0x4E, 0x9C, 0x7A, 0xA0, 0x00, 0x3E, 0x95, - 0x10, 0x40, 0xBF, 0xBC, 0x00, 0x0A, 0x65, 0x2E, 0x48, 0xE3, 0xF6, 0x04, 0xD0, 0x62, 0x02, 0xB8, 0x8A, 0x5E, 0x7A, 0x9D, 0x61, 0xD0, 0x15, 0x2A, 0x50, 0xBD, - 0x1D, 0xAC, 0x91, 0x60, 0xA4, 0xD5, 0x03, 0x8B, 0x06, 0x8E, 0x8A, 0x29, 0xB4, 0x5D, 0x34, 0xBF, 0x92, 0x17, 0x3F, 0x15, 0xF2, 0xEF, 0x6D, 0x26, 0x58, 0xED, - 0x56, 0x60, 0xD1, 0xC5, 0x05, 0xD0, 0x2A, 0x2F, 0x00, 0xBD, 0x90, 0x00, 0x5A, 0x0F, 0x2B, 0x19, 0xEF, 0xAF, 0x7F, 0xA7, 0x38, 0x5F, 0x5A, 0x45, 0xDD, 0x5F, - 0x18, 0xCD, 0xDA, 0x45, 0x84, 0xB5, 0x55, 0xEF, 0xEF, 0x44, 0x9C, 0x6B, 0xBF, 0x68, 0xF1, 0xAD, 0xAF, 0x59, 0x71, 0xA0, 0x7C, 0x11, 0xB0, 0xB7, 0x83, 0xF5, - 0x2A, 0xA4, 0xF0, 0x44, 0xC2, 0x59, 0xC1, 0x00, 0x7F, 0x52, 0xDE, 0x1D, 0x0A, 0x69, 0x18, 0x69, 0xDD, 0x9E, 0x8A, 0x7B, 0x31, 0x41, 0xB0, 0x6F, 0xA4, 0xAB, - 0xA8, 0xB8, 0x7C, 0xE5, 0xB0, 0xB7, 0x83, 0xA5, 0x2E, 0xA4, 0xF0, 0x58, 0xC2, 0x59, 0x41, 0x15, 0x17, 0x4D, 0x49, 0x8F, 0x4B, 0x4E, 0x2D, 0xF5, 0x6D, 0xE6, - 0xA4, 0x58, 0xED, 0x16, 0x04, 0x21, 0x7E, 0xC0, 0x22, 0x4B, 0xC1, 0xE5, 0x2B, 0xDE, 0xBD, 0x8A, 0xEB, 0xB3, 0xDB, 0x8B, 0xE4, 0x47, 0xB2, 0xAF, 0x9B, 0xE7, - 0xDB, 0x41, 0xD1, 0x5C, 0xB6, 0x55, 0x72, 0xE0, 0xDB, 0x6A, 0x2A, 0x0B, 0xBD, 0x43, 0xD6, 0xB3, 0xCE, 0x7D, 0x86, 0x09, 0x94, 0x5F, 0x79, 0xEB, 0xED, 0x60, - 0x7B, 0x08, 0x52, 0xD8, 0xAE, 0x0D, 0x3E, 0x15, 0x64, 0xAA, 0x4A, 0xFD, 0xA0, 0xF4, 0xFE, 0x90, 0xDD, 0x95, 0xDE, 0x47, 0xF3, 0x9B, 0xE2, 0xA5, 0xF7, 0x8B, - 0x77, 0x3F, 0x17, 0x2B, 0xBD, 0x8B, 0xBD, 0xEC, 0xAE, 0xF4, 0x5E, 0xCE, 0x66, 0x0A, 0x6D, 0x94, 0x05, 0xC6, 0xF0, 0x55, 0x48, 0x23, 0xD3, 0xA3, 0x5D, 0x82, - 0x60, 0xB4, 0xF7, 0xC1, 0x69, 0x28, 0x22, 0xE1, 0x19, 0xE5, 0x78, 0xFB, 0x2C, 0xEB, 0xE9, 0x64, 0x84, 0x85, 0x52, 0xCF, 0xF0, 0xE2, 0x0B, 0x75, 0xFA, 0xFC, - 0x1B, 0x5F, 0x15, 0x9E, 0x05, 0x4E, 0x7B, 0xD7, 0xC8, 0xC1, 0x51, 0x41, 0xDC, 0x5B, 0x7F, 0xC5, 0xC0, 0x20, 0xA1, 0x28, 0x9D, 0xEA, 0x47, 0xC7, 0x73, 0xE5, - 0x3A, 0x39, 0x05, 0x2B, 0x12, 0xCD, 0x3B, 0x62, 0xA9, 0x45, 0x3D, 0x9A, 0x33, 0xF2, 0xB6, 0x13, 0xCD, 0x11, 0x77, 0x8C, 0xF7, 0x02, 0x59, 0x0D, 0x83, 0x2D, - 0x26, 0x00, 0xF9, 0x26, 0x0A, 0x05, 0x01, 0xA4, 0x49, 0x60, 0x23, 0x22, 0x68, 0x53, 0x09, 0xB4, 0x13, 0xDA, 0x4F, 0x09, 0xFC, 0xB4, 0x7D, 0xD9, 0xB8, 0xDF, - 0xD9, 0x41, 0x6D, 0x02, 0xC5, 0x15, 0xE3, 0xA8, 0xA0, 0x4E, 0x8B, 0x2D, 0x0E, 0xC6, 0x74, 0x5A, 0xCC, 0xA8, 0xB7, 0xB6, 0x3A, 0x08, 0xC8, 0x3B, 0x54, 0x00, - 0x1D, 0x65, 0x95, 0x96, 0x9F, 0x66, 0x76, 0x76, 0x90, 0x9F, 0xA0, 0xB4, 0x62, 0x1C, 0x15, 0x54, 0x69, 0xB1, 0xA5, 0xCF, 0x98, 0x4A, 0xD5, 0xE7, 0x97, 0x9C, - 0xC8, 0xAD, 0xA9, 0xB4, 0x4B, 0x05, 0xD0, 0x55, 0x56, 0x69, 0xF9, 0x59, 0x47, 0x67, 0x07, 0xBB, 0x77, 0x51, 0x5A, 0x31, 0x8E, 0x0A, 0xAA, 0xB4, 0xD8, 0x92, - 0x5D, 0x4C, 0xA5, 0xEA, 0xF3, 0x49, 0x4E, 0xE4, 0xD6, 0x54, 0xDA, 0xA3, 0x02, 0xE8, 0x29, 0xAB, 0xB4, 0x7C, 0xA5, 0xA0, 0xB3, 0x83, 0x62, 0x10, 0x4A, 0x2B, - 0xC6, 0x51, 0x41, 0x95, 0x16, 0x5B, 0x7D, 0x8E, 0xA9, 0x54, 0x7D, 0x9D, 0x83, 0x13, 0xB9, 0x35, 0x95, 0xF6, 0xA9, 0x00, 0xFA, 0xCA, 0x2A, 0x2D, 0xBF, 0xBF, - 0xAA, 0xB3, 0x83, 0xBD, 0xDB, 0x28, 0xAD, 0x18, 0x47, 0x05, 0x55, 0x5A, 0xAC, 0x74, 0x1B, 0x53, 0xA9, 0xFA, 0xCA, 0x0D, 0x27, 0x72, 0x6B, 0x2A, 0x3D, 0xA2, - 0x02, 0x38, 0x52, 0x56, 0x69, 0xF9, 0xAD, 0xEB, 0x9D, 0x1D, 0xD4, 0xF3, 0x50, 0x5A, 0x31, 0x8E, 0x0A, 0xAA, 0xB4, 0x58, 0x05, 0x27, 0xA6, 0x52, 0xF5, 0xBD, - 0x53, 0x9C, 0xC8, 0xAD, 0xA9, 0xF4, 0x98, 0x0A, 0xE0, 0x58, 0x59, 0xA5, 0xE5, 0x77, 0xEE, 0x77, 0x76, 0xB0, 0x73, 0x1F, 0xA5, 0x15, 0xE3, 0xA8, 0xA0, 0x4A, - 0x8B, 0xD5, 0x66, 0x63, 0x2A, 0x55, 0xDF, 0xEE, 0xC4, 0x89, 0xDC, 0x9A, 0x4A, 0x4F, 0xA8, 0x00, 0x4E, 0x94, 0x55, 0x5A, 0x7E, 0xCB, 0x40, 0x67, 0x07, 0x9B, - 0x5F, 0x50, 0x5A, 0x2D, 0x91, 0xA3, 0x82, 0x2A, 0x2D, 0xB6, 0xC0, 0xD8, 0x49, 0xD9, 0xFA, 0xA2, 0xA0, 0xD2, 0xB4, 0x05, 0xC6, 0x07, 0x50, 0xBF, 0x33, 0x56, - 0xC3, 0x12, 0x1F, 0xFC, 0x79, 0xF1, 0xD3, 0xCB, 0xF4, 0xC2, 0x7E, 0x6A, 0x15, 0x2F, 0xD6, 0xD7, 0x43, 0x2F, 0xE3, 0x89, 0xF2, 0x42, 0xC2, 0x41, 0xCB, 0xEC, - 0x25, 0x91, 0xDA, 0x1A, 0xF3, 0xD9, 0x96, 0xC6, 0x80, 0x0B, 0x58, 0x5A, 0xA7, 0xDB, 0x92, 0x27, 0x2D, 0x39, 0x96, 0xC6, 0xA9, 0xDC, 0x4E, 0xF0, 0x40, 0xE4, - 0x30, 0x17, 0x47, 0xDE, 0x3F, 0x28, 0xAD, 0xE9, 0x30, 0x80, 0x78, 0xF8, 0xE8, 0xB6, 0x4E, 0x14, 0xE3, 0x07, 0xC8, 0x20, 0x6D, 0x63, 0xFC, 0x06, 0x03, 0x08, - 0xD2, 0xD8, 0x61, 0x4C, 0xBD, 0x51, 0x66, 0x2A, 0x59, 0x05, 0x28, 0xC4, 0x54, 0x5A, 0x65, 0x67, 0xC3, 0x4C, 0x75, 0x19, 0x53, 0x19, 0x4E, 0x9A, 0x60, 0x2A, - 0x39, 0x0F, 0x2E, 0xC4, 0x54, 0xDA, 0x44, 0x38, 0x62, 0xEA, 0x21, 0x04, 0x3A, 0x32, 0x32, 0xA6, 0xA3, 0x12, 0xA1, 0xEE, 0xF2, 0xE2, 0xF0, 0xC5, 0x9B, 0x0B, - 0x8D, 0x2E, 0x69, 0x3A, 0x56, 0xC1, 0x88, 0x17, 0xEF, 0xF4, 0x2F, 0x15, 0xF3, 0x28, 0xE9, 0x42, 0xD4, 0x7B, 0x73, 0xA1, 0x1A, 0xF0, 0x38, 0x64, 0x91, 0x90, - 0xD7, 0x6B, 0x75, 0xCA, 0x54, 0x08, 0x43, 0x22, 0xB7, 0x14, 0xF4, 0x28, 0xFA, 0x76, 0x24, 0x83, 0xCB, 0x62, 0x32, 0x28, 0x54, 0x25, 0x8D, 0xCB, 0xA0, 0x40, - 0xD8, 0x0F, 0x88, 0xDC, 0xA6, 0x0C, 0x30, 0x4A, 0x5E, 0x5E, 0x68, 0xEF, 0xFF, 0xA9, 0x5D, 0xDE, 0x2C, 0x1C, 0x6F, 0xE9, 0x92, 0xDC, 0xA8, 0xC2, 0xE1, 0xE2, - 0x71, 0xA5, 0xDF, 0xEB, 0x75, 0x54, 0x03, 0x4B, 0x2F, 0x7D, 0x08, 0x98, 0xB4, 0x36, 0x18, 0x2F, 0x29, 0xA1, 0xDD, 0x90, 0xC1, 0x0F, 0x04, 0x34, 0xAD, 0x14, - 0x37, 0x39, 0x60, 0x9C, 0x43, 0xBD, 0x85, 0xDB, 0xAB, 0x15, 0x19, 0x94, 0x67, 0x94, 0x9D, 0x8D, 0x0E, 0x07, 0x94, 0xCA, 0x5E, 0xC8, 0xDE, 0xA7, 0x8F, 0x57, - 0x6A, 0x8C, 0x25, 0xEB, 0x68, 0xC5, 0x54, 0x97, 0xF6, 0xC8, 0x68, 0xC1, 0x41, 0x41, 0xDE, 0xE8, 0xEC, 0x10, 0x62, 0xEC, 0xBA, 0x6C, 0x52, 0x44, 0x76, 0x36, - 0x31, 0xA7, 0x60, 0xB0, 0x72, 0x59, 0x52, 0x19, 0xB2, 0xB7, 0x9A, 0xE2, 0x17, 0x48, 0x9B, 0x23, 0x08, 0xF3, 0xA0, 0x7B, 0xF4, 0xAE, 0x40, 0xB2, 0x73, 0x63, - 0x4A, 0xA2, 0xEB, 0x1A, 0x0B, 0xE2, 0x59, 0xC1, 0xD9, 0x60, 0x08, 0x8D, 0x6B, 0xC2, 0x3F, 0x97, 0xAA, 0xCD, 0x5C, 0x32, 0x39, 0xAF, 0x7D, 0x13, 0xE2, 0xE4, - 0x8F, 0xDF, 0x61, 0x93, 0x9A, 0x36, 0x76, 0x56, 0xB6, 0xE5, 0x18, 0x18, 0xF8, 0x8D, 0x85, 0x0F, 0x94, 0x1E, 0xFC, 0xB1, 0xC0, 0x37, 0x5C, 0x19, 0xF8, 0xB4, - 0x96, 0x91, 0xD1, 0x8F, 0xA0, 0xFE, 0x91, 0xE5, 0x78, 0xC1, 0xB4, 0x0D, 0x0F, 0xC3, 0xCF, 0xAB, 0xFE, 0xCF, 0x7F, 0xE7, 0x6D, 0x15, 0x30, 0xE7, 0x53, 0x41, - 0x00, 0x35, 0xCD, 0x73, 0x47, 0xE7, 0x35, 0xA0, 0xD4, 0x75, 0x3C, 0xCF, 0x71, 0xCD, 0xA9, 0x99, 0x32, 0x36, 0xA7, 0x49, 0xFB, 0x50, 0x26, 0xEE, 0x44, 0x63, - 0xC9, 0xB0, 0x7F, 0xE6, 0x8D, 0x5C, 0x73, 0xE1, 0x0F, 0x1E, 0x8D, 0x9D, 0xD1, 0x72, 0x4E, 0x6C, 0xFF, 0xC0, 0x18, 0x8F, 0x2F, 0xAF, 0xE1, 0xE0, 0x3B, 0xFC, - 0x16, 0x1F, 0x48, 0xBE, 0x51, 0x7F, 0xF5, 0xAF, 0x77, 0x38, 0x0C, 0xE3, 0x35, 0x90, 0x17, 0x19, 0xD7, 0xF7, 0xB5, 0xC9, 0xD2, 0x66, 0x23, 0x61, 0x83, 0x60, - 0xDB, 0x3D, 0xED, 0x2B, 0x60, 0xBC, 0x36, 0x5C, 0x6D, 0x68, 0x78, 0xE4, 0xAD, 0xE3, 0xF9, 0xDA, 0xB9, 0x16, 0x62, 0xB4, 0x9C, 0x11, 0xDD, 0xB7, 0x71, 0xC0, - 0xF8, 0xE2, 0x2D, 0x19, 0xE3, 0x3F, 0xBA, 0x16, 0x34, 0x0D, 0xA1, 0x9E, 0x6A, 0xF5, 0xD3, 0x63, 0xBD, 0x8E, 0xF6, 0x17, 0x76, 0x31, 0x21, 0x10, 0xE6, 0xA1, - 0x5D, 0x63, 0xE9, 0x5A, 0xFB, 0xDA, 0x68, 0xB8, 0xF7, 0x95, 0x52, 0x4F, 0x2F, 0xE3, 0xB5, 0x3D, 0xCE, 0xCC, 0x81, 0x3F, 0x23, 0x76, 0x23, 0xA2, 0xCC, 0x25, - 0xDE, 0xC2, 0xB1, 0x3D, 0xC2, 0x88, 0x63, 0x3F, 0x73, 0x12, 0x5D, 0x3F, 0xF0, 0x7C, 0xC3, 0x5F, 0x7A, 0xDA, 0xE3, 0xF3, 0x73, 0xAD, 0xDD, 0x6A, 0x89, 0xCD, - 0x34, 0xE8, 0x26, 0xD9, 0x6E, 0x5F, 0x4B, 0x5C, 0xF8, 0x48, 0x6E, 0xFC, 0xBD, 0x67, 0x21, 0xCC, 0x9D, 0x46, 0x2C, 0x8F, 0xC4, 0x90, 0x84, 0x00, 0xF8, 0xDE, - 0xB8, 0xC6, 0x5E, 0x9C, 0xC0, 0xC6, 0xD8, 0xF0, 0x8D, 0xBD, 0xAF, 0x31, 0x7D, 0x41, 0xAF, 0x40, 0xC9, 0xBE, 0x46, 0x6F, 0x3D, 0x13, 0x6E, 0xDD, 0xED, 0x1D, - 0x80, 0x0C, 0x81, 0xDF, 0x10, 0x9A, 0xB8, 0x6E, 0x9C, 0x62, 0x0A, 0xDD, 0xD4, 0xF7, 0x35, 0xBC, 0x13, 0x87, 0x15, 0x88, 0x7C, 0x14, 0x5C, 0x0B, 0x84, 0x96, - 0x8D, 0x56, 0x82, 0x92, 0xA1, 0xBB, 0x8B, 0xA9, 0x08, 0x02, 0xCE, 0x07, 0x32, 0x05, 0x89, 0x4D, 0xF7, 0x79, 0xFC, 0xD9, 0xA7, 0xC1, 0x67, 0x9F, 0xC5, 0x2D, - 0x41, 0x6B, 0x87, 0x87, 0xE0, 0xD2, 0x9E, 0x63, 0x11, 0xB0, 0x8A, 0x69, 0xA3, 0xCE, 0xBF, 0xF5, 0x0A, 0x16, 0x55, 0x6F, 0xDD, 0xD4, 0x9F, 0x02, 0x82, 0x03, - 0xDF, 0xB9, 0xF2, 0x5D, 0xD3, 0x9E, 0x36, 0xF4, 0xFE, 0x5E, 0x84, 0x8D, 0xDE, 0x46, 0x94, 0x89, 0xFB, 0xF4, 0x3A, 0xED, 0x24, 0x79, 0xA3, 0xC1, 0xAF, 0x3F, - 0xAD, 0xEF, 0xD5, 0x39, 0xF1, 0xF4, 0x1C, 0xCC, 0xAD, 0xC1, 0x0E, 0x9E, 0x50, 0x1A, 0xF7, 0xB4, 0xB3, 0x33, 0xDE, 0x0D, 0x6B, 0x85, 0x17, 0xA1, 0x11, 0xFD, - 0x93, 0xB8, 0x15, 0x9A, 0xE2, 0xEF, 0xFF, 0xF8, 0x1A, 0xD8, 0xEC, 0xDD, 0x21, 0x50, 0xFD, 0x1C, 0x43, 0xF0, 0x3F, 0xBE, 0xC2, 0xFF, 0x77, 0x4F, 0x68, 0xD4, - 0xFD, 0xC7, 0x57, 0xFC, 0x73, 0xF7, 0x04, 0x7A, 0x82, 0x63, 0xDA, 0xDF, 0xDD, 0xEF, 0x54, 0x0E, 0xEB, 0xD2, 0x9B, 0xA6, 0x4A, 0x2F, 0x14, 0x5B, 0x61, 0x9A, - 0xA6, 0x19, 0x44, 0xFD, 0x1E, 0xF9, 0x6F, 0x63, 0xE4, 0x8C, 0x41, 0x3D, 0x3E, 0x58, 0x72, 0xA0, 0x74, 0x0B, 0x54, 0x12, 0x08, 0xAA, 0x15, 0x28, 0xDD, 0x9C, - 0xD0, 0x96, 0x1A, 0x77, 0x95, 0xC8, 0x40, 0x82, 0x96, 0x0B, 0xC3, 0xF5, 0xC8, 0xB7, 0xB6, 0xDF, 0xF0, 0x63, 0x4E, 0x91, 0x22, 0xF1, 0xC1, 0x20, 0xC6, 0x02, - 0xFE, 0x00, 0x0E, 0xDA, 0xD5, 0xB9, 0xD2, 0x42, 0x63, 0xE3, 0x7F, 0x13, 0x66, 0xF3, 0xA6, 0x90, 0xD9, 0x34, 0xA8, 0xD8, 0xC2, 0x3E, 0xF7, 0x8A, 0x98, 0x10, - 0x90, 0x25, 0x18, 0x10, 0x75, 0x88, 0x48, 0x64, 0xEC, 0x62, 0x8A, 0x43, 0xFC, 0x3C, 0xB2, 0xBE, 0x34, 0x6E, 0xE0, 0xBF, 0x64, 0xCC, 0x5A, 0xD3, 0x15, 0x36, - 0x7A, 0x8E, 0xFF, 0x81, 0x82, 0xF0, 0x4F, 0xAA, 0xA1, 0x00, 0xD6, 0xF7, 0x96, 0xD5, 0x60, 0x1F, 0x98, 0x03, 0x1B, 0x59, 0x42, 0x3C, 0xF4, 0x6E, 0x31, 0x32, - 0x39, 0x8E, 0xFF, 0x79, 0x5F, 0x5B, 0xB8, 0x40, 0x18, 0xFD, 0x96, 0x0A, 0x1C, 0x03, 0x22, 0x62, 0xB3, 0xBF, 0xB9, 0x14, 0x2C, 0x2C, 0xEB, 0x39, 0xC3, 0x0A, - 0x24, 0xB0, 0x03, 0x30, 0x99, 0x25, 0x9A, 0x2E, 0xFC, 0x7F, 0xF7, 0x04, 0x3A, 0x81, 0x43, 0xF8, 0xFF, 0xEE, 0x09, 0x76, 0x85, 0x46, 0x85, 0x3D, 0xDE, 0x3D, - 0x81, 0x1E, 0xE1, 0x04, 0xFE, 0x87, 0x36, 0xD8, 0x2F, 0xB6, 0xC2, 0xBF, 0x70, 0x87, 0xF6, 0x8F, 0x37, 0xE9, 0x01, 0xBB, 0xC0, 0x4F, 0xB3, 0x18, 0x64, 0x6F, - 0xD7, 0x6F, 0xD0, 0xB7, 0x9D, 0x7F, 0xBE, 0x01, 0x76, 0xE8, 0xC1, 0x2D, 0xC4, 0x20, 0x7B, 0x8C, 0xE7, 0xF8, 0xE7, 0x36, 0x50, 0x30, 0x5E, 0xE0, 0x47, 0x70, - 0x8D, 0xBE, 0x11, 0x16, 0x2F, 0xB1, 0x03, 0x6C, 0x45, 0xDF, 0xDF, 0x49, 0x5B, 0xB1, 0x23, 0xB8, 0xC6, 0xDF, 0xFA, 0xB8, 0xAF, 0xF1, 0xF7, 0x0A, 0xE6, 0x0A, - 0x27, 0x7A, 0xEF, 0xDF, 0x73, 0xEF, 0x06, 0x19, 0x64, 0xA4, 0xA1, 0x54, 0xC2, 0xB3, 0xDB, 0xBB, 0x27, 0x04, 0xEF, 0x51, 0x22, 0xE1, 0xF8, 0x96, 0x1F, 0xC3, - 0x75, 0xA0, 0x0F, 0xEF, 0x04, 0x04, 0xD3, 0x0B, 0xB7, 0xD1, 0x05, 0x68, 0xE1, 0xE3, 0x7D, 0x4E, 0x3C, 0x9C, 0xDD, 0x86, 0x67, 0x08, 0x4D, 0x61, 0x39, 0x1B, - 0x70, 0x7A, 0x1B, 0x9D, 0xC2, 0x5D, 0xE4, 0x05, 0x15, 0xC0, 0x79, 0xBA, 0x7B, 0xC2, 0x79, 0x42, 0x2D, 0xB2, 0xA3, 0xB8, 0xA8, 0xE1, 0x7F, 0xF4, 0x23, 0x9F, - 0x07, 0xEC, 0x4F, 0x81, 0x77, 0x12, 0x6B, 0x4F, 0x3B, 0x1F, 0xF0, 0xB8, 0x8F, 0x01, 0x00, 0x3C, 0x0A, 0xAE, 0x13, 0xEB, 0xC0, 0xF0, 0xC1, 0x21, 0x20, 0x6F, - 0x22, 0xDE, 0x01, 0x46, 0x94, 0xD0, 0xCD, 0xD7, 0x6E, 0x1D, 0xD8, 0xE0, 0x16, 0x14, 0xE1, 0xDE, 0x29, 0x0F, 0x1B, 0x88, 0x88, 0x71, 0xB9, 0x86, 0x8B, 0x5D, - 0x4E, 0x43, 0xC7, 0xEE, 0xA6, 0x60, 0xE4, 0xA1, 0x30, 0x0E, 0x81, 0x17, 0xD3, 0xB0, 0xD1, 0x51, 0x43, 0xC0, 0xD5, 0xEE, 0xF5, 0x22, 0x6C, 0x89, 0x48, 0xC7, - 0xE6, 0x94, 0x28, 0x90, 0x03, 0xCC, 0xCF, 0xA3, 0xA1, 0x70, 0x04, 0xE6, 0xA0, 0xD5, 0x83, 0x09, 0x65, 0xFD, 0x74, 0x2D, 0xC2, 0x01, 0x04, 0x2F, 0x20, 0x68, - 0xCF, 0x19, 0x8D, 0xA7, 0x51, 0xF8, 0xD4, 0xB4, 0x21, 0x24, 0x3B, 0x5F, 0x9E, 0xC5, 0x90, 0xD1, 0xD4, 0x3F, 0xC4, 0xC4, 0xAE, 0x61, 0xA2, 0x90, 0xB8, 0xC4, - 0xB6, 0xDB, 0x35, 0x1D, 0x9B, 0xC8, 0x7B, 0x8D, 0xC5, 0x4B, 0xDE, 0x11, 0x3F, 0x1B, 0x93, 0x89, 0xB1, 0xB4, 0xFC, 0x08, 0xCC, 0x25, 0x90, 0xE8, 0xDA, 0x3C, - 0x6C, 0xB1, 0x24, 0x3F, 0x77, 0xE8, 0xCE, 0x18, 0x2A, 0x82, 0x51, 0xE1, 0x71, 0x72, 0x54, 0x00, 0xAB, 0x74, 0xFD, 0x46, 0xFD, 0xD2, 0x75, 0x1D, 0xF7, 0xD7, - 0xFA, 0x53, 0x6C, 0xF4, 0xB4, 0xFE, 0xDB, 0xA9, 0x46, 0xE3, 0xE9, 0x5E, 0x3C, 0xB8, 0x0B, 0xE1, 0xF3, 0xF0, 0x50, 0x7B, 0xE1, 0xFB, 0x06, 0x28, 0x00, 0x6B, - 0x2C, 0x33, 0x94, 0x8F, 0x66, 0xF0, 0x24, 0xD0, 0x71, 0xD1, 0x28, 0xD9, 0xF7, 0xEE, 0x41, 0x22, 0x98, 0x58, 0x7A, 0x00, 0x12, 0x24, 0x99, 0x14, 0xD5, 0xC1, - 0xBF, 0x97, 0xC4, 0xBD, 0xBD, 0xA2, 0x02, 0x73, 0xDC, 0x17, 0x10, 0x2A, 0xEB, 0x07, 0xD1, 0x3C, 0xA9, 0xCE, 0x72, 0x9E, 0x03, 0x40, 0x75, 0x09, 0x7D, 0x80, - 0x8E, 0x23, 0x9B, 0x67, 0xDC, 0x84, 0x7A, 0x87, 0x71, 0xEE, 0x9C, 0x2B, 0x23, 0x99, 0x64, 0x41, 0x0B, 0xC7, 0xFE, 0x42, 0x6E, 0x97, 0x0B, 0x10, 0x7F, 0x94, - 0x36, 0x25, 0x12, 0x39, 0x2E, 0x1D, 0x72, 0x00, 0x2D, 0x2F, 0xF8, 0xC0, 0xA9, 0x77, 0x24, 0x8D, 0x22, 0x15, 0x50, 0xEB, 0x44, 0x4F, 0x7C, 0xB6, 0xD6, 0xE8, - 0xEE, 0x91, 0xFC, 0x4C, 0x92, 0x72, 0x72, 0x02, 0xB9, 0xF0, 0xC0, 0xB5, 0xA9, 0x63, 0x27, 0x7A, 0x48, 0xA4, 0x83, 0x90, 0x0C, 0x46, 0x91, 0x61, 0xB9, 0x80, - 0xE4, 0x93, 0xC4, 0x83, 0x43, 0x68, 0x0B, 0xC1, 0xCD, 0xB9, 0xE3, 0x93, 0x44, 0xC4, 0x30, 0x6D, 0xD3, 0x37, 0x0D, 0xEB, 0x53, 0x64, 0x8D, 0x5B, 0x75, 0x7F, - 0x89, 0x8F, 0x17, 0xF0, 0xFF, 0xB5, 0x9C, 0x4F, 0x2D, 0x4F, 0x59, 0xB3, 0x90, 0x30, 0x1E, 0x44, 0x56, 0x22, 0xCA, 0x21, 0x16, 0x16, 0xF8, 0xFD, 0xA0, 0xA7, - 0xC7, 0x8F, 0xE9, 0xD1, 0xA3, 0x50, 0x69, 0x41, 0xF4, 0x38, 0xD7, 0xA2, 0x1B, 0x09, 0x05, 0xAF, 0xE3, 0x4E, 0xE0, 0x08, 0x90, 0x0B, 0x18, 0x12, 0x81, 0x7F, - 0x01, 0xE9, 0x0D, 0xDA, 0xC2, 0xFF, 0x47, 0xFD, 0x07, 0x14, 0xF5, 0xB7, 0x17, 0xE2, 0x33, 0x6C, 0x3B, 0xE1, 0x01, 0x0C, 0x4E, 0x9E, 0x4F, 0x3F, 0x85, 0x44, - 0x5B, 0x9E, 0x24, 0x87, 0xA1, 0x3B, 0x9C, 0xEC, 0xC3, 0x64, 0xE6, 0x92, 0x85, 0xE7, 0x97, 0xB7, 0xDF, 0x8E, 0x1B, 0xF5, 0xF0, 0x8D, 0x46, 0xF5, 0x3D, 0x8C, - 0x4B, 0x96, 0x39, 0xFA, 0x12, 0x86, 0xA5, 0xC8, 0xF2, 0x20, 0xA5, 0xC1, 0xEC, 0x1F, 0x27, 0xD6, 0xE6, 0x88, 0x9B, 0xEA, 0xAB, 0x0F, 0x2F, 0xDE, 0x7D, 0x7E, - 0xF1, 0xF1, 0xE3, 0x07, 0x6D, 0x09, 0x36, 0xAB, 0xF7, 0x3F, 0x63, 0xDA, 0x02, 0x93, 0x00, 0xF7, 0x33, 0xD0, 0xE7, 0x7D, 0xA6, 0x48, 0x5B, 0xBF, 0xFE, 0xF6, - 0x6B, 0xFB, 0x37, 0x00, 0xFD, 0xFA, 0x5F, 0x76, 0x9D, 0x31, 0x82, 0xA8, 0x9E, 0x02, 0x2E, 0x3C, 0xFE, 0x5A, 0x7F, 0x1A, 0x18, 0x7C, 0x23, 0x9D, 0xC2, 0xF0, - 0xF5, 0xBA, 0xF5, 0x3D, 0x60, 0xF5, 0x6E, 0x1F, 0x50, 0xB1, 0x74, 0x10, 0xC6, 0x9C, 0x06, 0x96, 0x2A, 0x4C, 0xE8, 0x40, 0x7F, 0x06, 0x7F, 0xCE, 0x34, 0xFD, - 0x08, 0xFE, 0x3E, 0x7D, 0x1A, 0x99, 0x48, 0xC9, 0xEE, 0xEA, 0x4F, 0x4D, 0xDA, 0x19, 0xCC, 0x4E, 0x1A, 0xE6, 0x19, 0x48, 0xF2, 0x79, 0x7D, 0xBF, 0x7E, 0x5A, - 0xAF, 0xC3, 0xB5, 0xA0, 0xFB, 0xBB, 0x18, 0x3B, 0x77, 0xCF, 0x42, 0x0E, 0xD9, 0xE8, 0x0A, 0x37, 0x22, 0xF1, 0x8B, 0x59, 0xDD, 0x4B, 0x56, 0xE5, 0x3A, 0x4F, - 0xD7, 0x09, 0x7B, 0x9B, 0xF5, 0x94, 0x0E, 0x88, 0x22, 0x4C, 0x86, 0x82, 0x58, 0x68, 0x08, 0x7D, 0x2D, 0x15, 0x35, 0x1D, 0x6E, 0xC7, 0x63, 0x17, 0xB4, 0x4D, - 0xAD, 0x65, 0x6F, 0xCD, 0x85, 0xD5, 0x70, 0x60, 0x63, 0x09, 0x8E, 0xB5, 0xE9, 0x66, 0x26, 0x12, 0xDA, 0x5A, 0xC0, 0xB2, 0x96, 0xF0, 0xB4, 0xEE, 0x2F, 0xD7, - 0x61, 0x1A, 0x9B, 0x2A, 0x6A, 0x6C, 0x2A, 0x68, 0x6C, 0xBA, 0x59, 0x8D, 0x71, 0xD4, 0x95, 0xB5, 0x16, 0xE0, 0xC9, 0xD1, 0x5C, 0x2E, 0x3C, 0x57, 0x1A, 0xD7, - 0xD6, 0x54, 0xA6, 0xAD, 0x32, 0x6A, 0x62, 0xB1, 0x0B, 0x26, 0x45, 0xC4, 0x7D, 0xFB, 0xF1, 0xDD, 0x77, 0x18, 0x6D, 0xE4, 0x2A, 0x0B, 0x35, 0x96, 0x4C, 0xAE, - 0x24, 0x18, 0x30, 0x28, 0xC6, 0x2A, 0x1F, 0x89, 0xB0, 0xA9, 0x45, 0x15, 0x84, 0x1C, 0x43, 0xE0, 0x05, 0x03, 0x35, 0xDF, 0xC5, 0x22, 0x41, 0xE0, 0xBC, 0x11, - 0x54, 0x86, 0x2D, 0x20, 0x80, 0x92, 0x12, 0x19, 0xE6, 0x35, 0x87, 0x11, 0x6A, 0x19, 0x3B, 0x77, 0x11, 0xEA, 0xAF, 0x9E, 0x6A, 0x50, 0x0B, 0xA6, 0xEA, 0x51, - 0x6C, 0xF3, 0x72, 0xA5, 0xC3, 0x27, 0xF4, 0x4A, 0x02, 0xE2, 0x5F, 0x95, 0x93, 0x18, 0x38, 0x2F, 0x04, 0x14, 0xC0, 0x72, 0x2B, 0xC1, 0x42, 0x0B, 0x08, 0x4A, - 0x38, 0xE8, 0x97, 0xC8, 0x52, 0x30, 0xA8, 0x51, 0x41, 0x3F, 0xFD, 0x25, 0xC1, 0x10, 0x94, 0x2A, 0x94, 0x90, 0x04, 0x9F, 0xAD, 0x4A, 0xC7, 0xA3, 0x46, 0x4C, - 0xF0, 0xB1, 0x28, 0x09, 0x1E, 0x5E, 0x1A, 0x51, 0x42, 0xC3, 0x3F, 0x74, 0x94, 0x8A, 0x45, 0x8D, 0x18, 0xFE, 0x6D, 0x21, 0x19, 0x4F, 0xBC, 0x14, 0xA3, 0xC6, - 0x13, 0xFF, 0x24, 0x4E, 0x3A, 0x1E, 0x45, 0xD9, 0xF0, 0xCF, 0xD0, 0xC8, 0xAC, 0x8E, 0x55, 0x7E, 0x32, 0x1D, 0x83, 0x35, 0x01, 0x60, 0x9E, 0xAA, 0x3E, 0xD7, - 0xC5, 0xCC, 0x9A, 0x17, 0x8A, 0xB2, 0x30, 0xF0, 0x26, 0x49, 0x0C, 0x41, 0x74, 0xB8, 0x87, 0x92, 0xDD, 0x7D, 0x44, 0xA1, 0xF7, 0x96, 0xA5, 0x16, 0x85, 0x16, - 0x96, 0x15, 0x84, 0x9F, 0x10, 0x26, 0x25, 0xFC, 0xD0, 0x25, 0x33, 0x5A, 0x6F, 0xCD, 0x94, 0x3F, 0x6D, 0xC1, 0xF0, 0xAE, 0x2B, 0x11, 0x71, 0xCC, 0x97, 0x96, - 0x92, 0x25, 0x41, 0x3B, 0x8E, 0x46, 0x34, 0x24, 0xBA, 0x1C, 0x77, 0xEB, 0xA9, 0x85, 0xAE, 0x5B, 0x2F, 0x05, 0x03, 0x2D, 0x09, 0xAB, 0xE5, 0x66, 0xD0, 0x32, - 0x05, 0xC9, 0xC2, 0x55, 0x4B, 0xEF, 0xA0, 0x5D, 0x1A, 0x23, 0x58, 0x7F, 0x56, 0x63, 0x25, 0xF8, 0xEA, 0xB8, 0x8C, 0x0E, 0x5A, 0xB5, 0xCE, 0x52, 0x0A, 0xFF, - 0xBA, 0x73, 0x9A, 0x46, 0x16, 0xAA, 0x43, 0x6E, 0xF0, 0xFD, 0x63, 0xC9, 0xB0, 0x5B, 0xB2, 0xD8, 0xBF, 0x7B, 0xE7, 0x30, 0xAE, 0x89, 0x82, 0x6B, 0x84, 0x2B, - 0xF3, 0x3C, 0xBB, 0x8B, 0xC0, 0x32, 0xBC, 0x63, 0x64, 0xD8, 0xD7, 0x46, 0xCC, 0x3B, 0x46, 0x30, 0xFD, 0xF5, 0x09, 0x47, 0xDD, 0xA8, 0xB1, 0x06, 0x35, 0x4E, - 0x23, 0x3B, 0x3B, 0xA0, 0x5B, 0xD6, 0xB0, 0x10, 0x62, 0x92, 0x15, 0x3B, 0x89, 0xDD, 0x9E, 0x11, 0xFA, 0x9A, 0x10, 0x7E, 0x9F, 0x9D, 0xB1, 0x06, 0x61, 0x2F, - 0x43, 0x67, 0x7C, 0x7B, 0x60, 0x2C, 0x16, 0x10, 0xBC, 0x2E, 0x66, 0xA6, 0x35, 0x6E, 0x30, 0x50, 0xC1, 0x44, 0x70, 0x6F, 0x02, 0xA1, 0xAB, 0x56, 0x1C, 0x2B, - 0x30, 0x7C, 0xC1, 0xAE, 0x35, 0xEA, 0xED, 0x71, 0xB0, 0x66, 0xC4, 0x9B, 0x1D, 0x8C, 0x5D, 0x63, 0xF5, 0x2D, 0x6E, 0x6A, 0x68, 0x60, 0xA7, 0xFB, 0xAD, 0xFD, - 0x16, 0x6F, 0xE0, 0xBB, 0xB7, 0x61, 0x96, 0x89, 0x78, 0x71, 0xF1, 0xF7, 0xC7, 0x0F, 0xDF, 0x45, 0x78, 0x7D, 0xE7, 0x15, 0xBB, 0xD4, 0xA8, 0xD3, 0x5D, 0x11, - 0x87, 0x7F, 0x2C, 0x70, 0x2A, 0x10, 0x28, 0x45, 0x10, 0x23, 0x6E, 0x78, 0x40, 0x51, 0xB1, 0xE6, 0xCF, 0x44, 0xA4, 0x70, 0xD9, 0x26, 0x2B, 0x0D, 0x30, 0x91, - 0x86, 0x0C, 0x34, 0xD8, 0x0E, 0x81, 0xE0, 0xC8, 0xC9, 0xEB, 0xA5, 0x65, 0xFD, 0x42, 0x0C, 0x17, 0xF4, 0xF1, 0x54, 0x6B, 0xD4, 0x5A, 0xB5, 0xA7, 0x0D, 0x7A, - 0xFD, 0x1D, 0xB0, 0x33, 0x6B, 0xEC, 0x3D, 0xD5, 0xF7, 0xF6, 0x0E, 0x3C, 0xD0, 0x19, 0x69, 0x34, 0xDB, 0x41, 0x13, 0xF8, 0x43, 0xDB, 0xB0, 0x4E, 0xD2, 0xEF, - 0xBF, 0x75, 0x96, 0xAE, 0x97, 0xD5, 0xE0, 0x9D, 0x69, 0x63, 0x11, 0x27, 0xAB, 0xC9, 0x15, 0x01, 0xC1, 0x8E, 0xD7, 0x9A, 0xD4, 0xE8, 0x2E, 0x0E, 0x5E, 0xFE, - 0xD0, 0xE8, 0xE2, 0xB6, 0xD6, 0x10, 0x2B, 0x3A, 0x7C, 0x19, 0x91, 0xA0, 0xA1, 0x37, 0x02, 0x8F, 0xBF, 0x13, 0x8D, 0x03, 0x5C, 0x16, 0x0C, 0xE0, 0x7B, 0xF0, - 0x80, 0x03, 0x97, 0xCC, 0x9D, 0x6B, 0xB2, 0xA6, 0x7F, 0x6C, 0x1E, 0x1A, 0xFF, 0xCC, 0x1C, 0xB3, 0x32, 0x4D, 0x64, 0xB7, 0x58, 0x15, 0xC2, 0x0D, 0x22, 0xB8, - 0x85, 0x02, 0xF7, 0x53, 0x34, 0xEA, 0x6C, 0xF7, 0x0A, 0x1D, 0x15, 0xEE, 0x22, 0xB7, 0x99, 0x39, 0xAB, 0x2C, 0x48, 0xD6, 0x7B, 0x02, 0x38, 0x84, 0x1E, 0x9B, - 0x9E, 0x31, 0xB4, 0xF2, 0xBB, 0xE6, 0xED, 0xC6, 0xBC, 0xFC, 0x0D, 0x0D, 0x82, 0x2B, 0x00, 0xEA, 0xBB, 0xB4, 0x4E, 0x28, 0xA0, 0x25, 0x76, 0x1E, 0xD6, 0x80, - 0xAC, 0x4C, 0xC4, 0x13, 0x03, 0x26, 0x4F, 0x71, 0xCC, 0xAC, 0x78, 0x5C, 0xA0, 0xAC, 0x2C, 0x5E, 0x06, 0x88, 0xF8, 0x29, 0x98, 0x33, 0x58, 0xA8, 0xF6, 0x9C, - 0xB2, 0xA0, 0x9D, 0xC6, 0xEE, 0x86, 0xB9, 0x8D, 0x58, 0x30, 0x7D, 0xC4, 0xE2, 0xDF, 0x5F, 0xB0, 0x82, 0x1B, 0x52, 0x1E, 0x93, 0xC0, 0x93, 0x27, 0x71, 0x6C, - 0xB8, 0x8D, 0x85, 0x0D, 0x20, 0x61, 0x6F, 0xAC, 0x3D, 0x7B, 0x7D, 0x78, 0xB4, 0xB2, 0xC0, 0x49, 0x82, 0xA1, 0xE0, 0x71, 0x4C, 0xF0, 0xC2, 0x18, 0x01, 0x84, - 0x98, 0x63, 0x2A, 0x20, 0xDC, 0x95, 0x56, 0x5B, 0x5B, 0xEF, 0x7F, 0x4E, 0xAD, 0xBE, 0x41, 0xF8, 0xA6, 0xC4, 0x3D, 0x90, 0x3F, 0x1A, 0x73, 0x74, 0x21, 0x3E, - 0x87, 0x4E, 0x60, 0x9C, 0xC6, 0x30, 0x22, 0x63, 0x09, 0xBA, 0xF1, 0x77, 0x78, 0x48, 0x31, 0x4E, 0x0D, 0xD3, 0xBE, 0x20, 0x26, 0xA6, 0x7F, 0x7B, 0xC2, 0x5D, - 0xDA, 0x3D, 0x20, 0xC2, 0x0D, 0x83, 0xC2, 0x72, 0xC6, 0xFA, 0x7A, 0xC8, 0xE1, 0x21, 0x6D, 0x9A, 0x82, 0x86, 0x76, 0xB1, 0x8E, 0x26, 0x93, 0xFA, 0xD5, 0xF0, - 0x33, 0x62, 0x93, 0x09, 0x85, 0xA2, 0x5B, 0x0D, 0x51, 0x1C, 0xB4, 0x57, 0x38, 0xCC, 0x42, 0x35, 0x31, 0x46, 0xE4, 0xB3, 0x0B, 0x71, 0x6C, 0x6A, 0x9B, 0x7F, - 0x12, 0x19, 0x42, 0xE6, 0x8C, 0x0D, 0x62, 0xBB, 0x4E, 0x90, 0x48, 0x22, 0x72, 0xEE, 0x67, 0xF1, 0xEB, 0xF1, 0x21, 0x3A, 0x70, 0xBD, 0x70, 0x69, 0x5D, 0xB4, - 0x05, 0x56, 0xFA, 0x8F, 0x26, 0xC0, 0x91, 0xB1, 0x6D, 0xA8, 0x12, 0xAE, 0x63, 0x19, 0x3C, 0x6C, 0x22, 0xD6, 0xAB, 0x2B, 0x16, 0xC1, 0x33, 0x70, 0xB2, 0xCD, - 0x7D, 0x49, 0xA4, 0xCB, 0xE1, 0xDC, 0xF4, 0x25, 0x08, 0xEB, 0x7A, 0x5D, 0x8A, 0x2B, 0xA5, 0x9E, 0x2E, 0x7A, 0x22, 0x8B, 0x66, 0x74, 0xA9, 0x11, 0x10, 0xC5, - 0xB6, 0x0A, 0x8C, 0xD8, 0x6E, 0xF5, 0xE7, 0x30, 0xE2, 0xE2, 0x06, 0x00, 0x54, 0x75, 0x62, 0x0B, 0x10, 0x43, 0xC1, 0xF6, 0xAE, 0x51, 0x14, 0xF1, 0xDD, 0x6B, - 0xC1, 0x8E, 0xB1, 0xF8, 0xCA, 0xA4, 0xB8, 0xF7, 0xE5, 0x77, 0x97, 0x00, 0x9C, 0x87, 0xF3, 0x59, 0xED, 0x1F, 0x5F, 0x29, 0x8A, 0x3B, 0x6D, 0x02, 0x91, 0xC0, - 0x9B, 0x91, 0x31, 0x9D, 0x7B, 0xF9, 0x4B, 0xEF, 0x54, 0xC3, 0x6D, 0x3F, 0xB1, 0xDD, 0x6A, 0x77, 0xBF, 0x87, 0x16, 0x12, 0x0E, 0x24, 0xD1, 0x7A, 0x2B, 0x67, - 0x7B, 0x6D, 0xDD, 0xF5, 0x91, 0xC2, 0x0A, 0x2B, 0x87, 0x55, 0x5A, 0x65, 0x95, 0x2D, 0x4E, 0x26, 0xA3, 0x14, 0xCF, 0x31, 0x1F, 0xA9, 0xF4, 0x4D, 0x77, 0x5D, - 0xAA, 0x74, 0x29, 0xC9, 0x2E, 0xF1, 0xC7, 0xC2, 0x98, 0x25, 0x8C, 0xF9, 0x09, 0x3F, 0xDA, 0xE3, 0x72, 0x02, 0x13, 0x19, 0x07, 0xF1, 0x96, 0x19, 0x11, 0xAE, - 0x3E, 0x33, 0x3D, 0xC6, 0x4C, 0x80, 0x49, 0x9B, 0x0B, 0x3B, 0x7F, 0x4B, 0x22, 0x5F, 0x76, 0x09, 0x95, 0xF5, 0x87, 0x07, 0x79, 0x7B, 0x28, 0x02, 0x19, 0x0E, - 0xEC, 0x40, 0x40, 0x10, 0x13, 0x51, 0x21, 0x15, 0x65, 0xC8, 0x8C, 0xFD, 0x84, 0x01, 0x9B, 0x8E, 0xD6, 0xB4, 0xE7, 0x5F, 0xA9, 0x55, 0xFF, 0xB6, 0xCF, 0x46, - 0x78, 0x21, 0x68, 0xEE, 0x15, 0x21, 0x68, 0x6D, 0x55, 0x3E, 0x97, 0x98, 0x8D, 0xAD, 0x4F, 0x06, 0x3F, 0x88, 0xC5, 0x14, 0xDF, 0xB9, 0xD6, 0x5A, 0x5B, 0x97, - 0x17, 0x17, 0xD1, 0x42, 0x06, 0x25, 0xB2, 0x11, 0x16, 0xD5, 0x03, 0xF1, 0x48, 0x17, 0xD0, 0x52, 0xC5, 0xC5, 0xEC, 0x8B, 0x45, 0x14, 0x9C, 0x16, 0x64, 0xCE, - 0x9F, 0xE8, 0x86, 0x59, 0x2A, 0xAF, 0x08, 0xE0, 0x22, 0xDC, 0x20, 0x9D, 0x0B, 0x19, 0x6D, 0xA6, 0x16, 0x70, 0xD0, 0xF9, 0x98, 0xDA, 0xFA, 0x03, 0x9F, 0xBA, - 0x09, 0xA0, 0x88, 0x35, 0x1F, 0x36, 0x78, 0x4A, 0x26, 0x49, 0xBE, 0x38, 0x7A, 0x65, 0xC1, 0xD3, 0x61, 0x92, 0x35, 0x16, 0xA0, 0xA9, 0xE7, 0xE7, 0x03, 0x8B, - 0xDB, 0xB2, 0xEB, 0x82, 0xB0, 0x3D, 0xDF, 0x59, 0x5C, 0xD1, 0xAB, 0x89, 0xA8, 0xB0, 0xA2, 0xC5, 0xAC, 0x03, 0xBC, 0x1F, 0xCC, 0x93, 0x44, 0x46, 0xE3, 0xF5, - 0xFF, 0x2B, 0xAC, 0x74, 0x69, 0x0C, 0x4F, 0x3D, 0x9E, 0xED, 0xD2, 0x22, 0x98, 0xB4, 0x07, 0x3A, 0xE7, 0xF4, 0xDC, 0x11, 0x1B, 0x3C, 0xC2, 0x6D, 0xD0, 0x18, - 0x3A, 0xF0, 0xF0, 0x77, 0xD6, 0x27, 0xA6, 0x12, 0x31, 0x05, 0xEF, 0xE5, 0xD2, 0xE2, 0x2C, 0x92, 0xA4, 0x44, 0x01, 0x9E, 0x39, 0x9B, 0x87, 0xA3, 0x06, 0x1B, - 0x28, 0x31, 0x7C, 0x09, 0xCA, 0x4F, 0x89, 0x91, 0x91, 0x98, 0x78, 0x50, 0x8A, 0x13, 0x2F, 0x8C, 0x7C, 0x6C, 0x0F, 0xFD, 0xF3, 0xCF, 0xA3, 0x21, 0x0C, 0x76, - 0x38, 0x01, 0x04, 0xD7, 0x5B, 0x35, 0xF6, 0xEE, 0xB2, 0xD8, 0x61, 0xE2, 0x8A, 0x14, 0xA9, 0x4A, 0x04, 0x8D, 0xDA, 0x72, 0x6C, 0x31, 0xF9, 0xC8, 0xD1, 0x89, - 0xD6, 0x7B, 0x69, 0x07, 0x13, 0x96, 0x34, 0xC1, 0x9E, 0xAF, 0x8B, 0x96, 0xE5, 0xAC, 0x31, 0x04, 0x51, 0x3C, 0x5E, 0x23, 0x36, 0x91, 0x94, 0x0A, 0x76, 0x11, - 0x34, 0x08, 0x68, 0x17, 0x1D, 0x22, 0x85, 0xF6, 0xF8, 0x58, 0x99, 0x48, 0xFF, 0x02, 0x95, 0x5F, 0x2C, 0x81, 0x88, 0x79, 0xA0, 0x72, 0x76, 0x0D, 0x33, 0xDC, - 0xD0, 0x36, 0x21, 0xE3, 0xCD, 0xF2, 0x19, 0xB8, 0x2D, 0x38, 0x1A, 0x4F, 0x8F, 0x73, 0x00, 0x68, 0x4E, 0xCC, 0x1E, 0x84, 0xA1, 0xB0, 0x6C, 0xC3, 0xAE, 0xAF, - 0x09, 0x89, 0x77, 0x66, 0x7C, 0x81, 0x66, 0x23, 0xD6, 0x4C, 0x40, 0x02, 0x78, 0xD7, 0xD3, 0x05, 0x89, 0x20, 0xA0, 0xDD, 0x5E, 0xA8, 0x17, 0x04, 0xE2, 0x99, - 0x68, 0xA4, 0x95, 0xF4, 0xD9, 0xC4, 0xFA, 0x4C, 0x22, 0xA1, 0xB0, 0xF4, 0x19, 0xC4, 0xFA, 0xEC, 0xE1, 0x4E, 0xD0, 0x42, 0xF0, 0xC4, 0x56, 0x24, 0x48, 0x92, - 0x2D, 0x75, 0x22, 0x4A, 0x3D, 0x98, 0x49, 0xE5, 0x40, 0x7C, 0xA6, 0xA9, 0x88, 0x28, 0x32, 0xA2, 0x28, 0x32, 0xC2, 0x45, 0x86, 0x00, 0x51, 0xE2, 0x9E, 0x3F, - 0xAD, 0x0B, 0xE3, 0xCA, 0x4F, 0x2F, 0x23, 0xCE, 0x56, 0xC3, 0x4C, 0x3A, 0xF9, 0x94, 0x49, 0x60, 0x2F, 0x1B, 0x00, 0xDA, 0xCF, 0x61, 0xDC, 0x16, 0xD9, 0x5A, - 0x0D, 0xD5, 0xD8, 0x0A, 0xA6, 0x5C, 0x08, 0x10, 0xB1, 0x25, 0x9F, 0x98, 0x05, 0xAC, 0xBC, 0x22, 0x3E, 0x7F, 0xAE, 0xD3, 0xB0, 0xC7, 0xDA, 0xC4, 0x35, 0xE6, - 0xC4, 0x83, 0x09, 0x59, 0x48, 0xEC, 0x98, 0xDE, 0xCF, 0x1D, 0xA2, 0x58, 0x33, 0x81, 0xC9, 0x70, 0x6A, 0x97, 0x0B, 0x1A, 0xB6, 0x14, 0xA0, 0x43, 0x3A, 0x32, - 0xA1, 0x83, 0x46, 0x6C, 0x6C, 0x0B, 0x4F, 0x95, 0x84, 0x15, 0xB6, 0x8E, 0x9C, 0x27, 0x42, 0xC0, 0x26, 0x4A, 0x03, 0xAD, 0x97, 0x2C, 0x27, 0xB0, 0x84, 0x87, - 0x31, 0x9B, 0x48, 0x6B, 0xC4, 0x06, 0x21, 0x4B, 0xB1, 0x36, 0xA1, 0x83, 0x30, 0xF8, 0x34, 0x32, 0x73, 0x49, 0x61, 0xE5, 0xEA, 0xDA, 0x7B, 0x8B, 0xE0, 0x34, - 0x8F, 0xBF, 0xA9, 0xF3, 0xE2, 0xDB, 0xD7, 0x9A, 0xE3, 0x6A, 0x96, 0xB3, 0x22, 0xB8, 0x59, 0x34, 0x58, 0x0A, 0xD6, 0x86, 0x04, 0x12, 0x4B, 0xC2, 0x26, 0xD3, - 0x18, 0x87, 0xFC, 0x99, 0xE9, 0x41, 0xEA, 0x8E, 0xEF, 0x93, 0x25, 0x8F, 0x6B, 0x61, 0x31, 0x34, 0x97, 0xBD, 0xF5, 0xAD, 0x55, 0x31, 0x71, 0x32, 0x98, 0x48, - 0x96, 0x8F, 0x39, 0x8F, 0x6B, 0xC1, 0x28, 0x6B, 0xFA, 0x5E, 0x40, 0x84, 0xE1, 0xED, 0x07, 0x2B, 0x45, 0x39, 0x03, 0xB9, 0x82, 0x0C, 0xC1, 0x22, 0x59, 0x46, - 0xBC, 0xAE, 0x49, 0x53, 0x56, 0x23, 0xC9, 0xD0, 0x28, 0x96, 0x0B, 0xA5, 0x71, 0x3E, 0x5D, 0x2B, 0x4C, 0xE2, 0x2C, 0x55, 0x67, 0xBF, 0xB3, 0xC3, 0xE0, 0xB1, - 0x39, 0x76, 0x86, 0x8B, 0x05, 0x83, 0x47, 0x67, 0x87, 0x33, 0x7F, 0x6E, 0x0D, 0x1E, 0xFD, 0x2F, 0x5E, 0x72, 0x0B, 0x89, 0x42, 0x10, 0x01, 0x00 +//File: index_ov5640.html.gz, Size: 8880 +#define index_ov5640_html_gz_len 8880 +const unsigned char index_ov5640_html_gz[] = { + 0x1F, 0x8B, 0x08, 0x08, 0x5B, 0xA3, 0x7B, 0x67, 0x00, 0x03, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x6F, 0x76, 0x35, 0x36, 0x34, 0x30, 0x2E, 0x68, 0x74, 0x6D, + 0x6C, 0x00, 0xED, 0x3D, 0xDB, 0x72, 0xDB, 0xC6, 0x92, 0xEF, 0xFE, 0x0A, 0x98, 0xC9, 0x9A, 0x64, 0x59, 0xA4, 0x08, 0xDE, 0x74, 0xB1, 0x44, 0xAF, 0x2D, 0x2B, + 0x76, 0xEA, 0xD8, 0x39, 0x8E, 0xE5, 0x38, 0x49, 0x65, 0x53, 0x0E, 0x48, 0x0C, 0x49, 0xC4, 0x20, 0xC0, 0x03, 0x80, 0xA2, 0x74, 0x5C, 0xFA, 0x8E, 0xFD, 0xA0, + 0xFD, 0xB1, 0xED, 0x9E, 0x19, 0x5C, 0x39, 0x00, 0x06, 0x00, 0x49, 0x29, 0xD9, 0xA5, 0xAB, 0x2C, 0x5C, 0xA6, 0x7B, 0xFA, 0x3E, 0x3D, 0x3D, 0x03, 0xE0, 0xEC, + 0xB1, 0x6E, 0x4F, 0xBC, 0xDB, 0x25, 0x51, 0xE6, 0xDE, 0xC2, 0x1C, 0x3D, 0x3A, 0x63, 0x7F, 0x14, 0xF8, 0x9D, 0xCD, 0x89, 0xA6, 0xB3, 0x43, 0x7A, 0xBA, 0x20, + 0x9E, 0xA6, 0x4C, 0xE6, 0x9A, 0xE3, 0x12, 0xEF, 0xBC, 0xB6, 0xF2, 0xA6, 0xAD, 0xE3, 0x5A, 0xF2, 0xB6, 0xA5, 0x2D, 0xC8, 0x79, 0xED, 0xDA, 0x20, 0xEB, 0xA5, + 0xED, 0x78, 0x35, 0x65, 0x62, 0x5B, 0x1E, 0xB1, 0xA0, 0xF9, 0xDA, 0xD0, 0xBD, 0xF9, 0xB9, 0x4E, 0xAE, 0x8D, 0x09, 0x69, 0xD1, 0x93, 0x03, 0xC3, 0x32, 0x3C, + 0x43, 0x33, 0x5B, 0xEE, 0x44, 0x33, 0xC9, 0xB9, 0x1A, 0xC5, 0xE5, 0x19, 0x9E, 0x49, 0x46, 0x97, 0x57, 0xEF, 0x7B, 0x5D, 0xE5, 0x9F, 0x9F, 0x06, 0xC3, 0x7E, + 0xE7, 0xEC, 0x90, 0x5D, 0x0B, 0xDB, 0xB8, 0xDE, 0x6D, 0xF4, 0x1C, 0x7F, 0x63, 0x5B, 0xBF, 0x55, 0xBE, 0xC6, 0x2E, 0xE1, 0x6F, 0x0A, 0x44, 0xB4, 0xA6, 0xDA, + 0xC2, 0x30, 0x6F, 0x4F, 0x95, 0x17, 0x0E, 0xF4, 0x79, 0xF0, 0x86, 0x98, 0xD7, 0xC4, 0x33, 0x26, 0xDA, 0x81, 0xAB, 0x59, 0x6E, 0xCB, 0x25, 0x8E, 0x31, 0x7D, + 0xB6, 0x01, 0x38, 0xD6, 0x26, 0x5F, 0x66, 0x8E, 0xBD, 0xB2, 0xF4, 0x53, 0xE5, 0x1B, 0xF5, 0x18, 0xFF, 0x6D, 0x36, 0x9A, 0xD8, 0xA6, 0xED, 0xC0, 0xFD, 0xCB, + 0xEF, 0xF0, 0xDF, 0xE6, 0x7D, 0xDA, 0xBB, 0x6B, 0xFC, 0x9B, 0x9C, 0x2A, 0xEA, 0x70, 0x79, 0x13, 0xBB, 0x7F, 0xF7, 0x28, 0x76, 0x3A, 0xEF, 0xA6, 0x51, 0xCF, + 0xE1, 0x8F, 0xB3, 0xE1, 0x5D, 0x32, 0xF1, 0x0C, 0xDB, 0x6A, 0x2F, 0x34, 0xC3, 0x12, 0x60, 0xD2, 0x0D, 0x77, 0x69, 0x6A, 0x20, 0x83, 0xA9, 0x49, 0x32, 0xF1, + 0x7C, 0xB3, 0x20, 0xD6, 0xEA, 0x20, 0x07, 0x1B, 0x22, 0x69, 0xE9, 0x86, 0xC3, 0x5A, 0x9D, 0xA2, 0x1C, 0x56, 0x0B, 0x2B, 0x17, 0x6D, 0x16, 0x5D, 0x96, 0x6D, + 0x11, 0x81, 0x00, 0xB1, 0xA3, 0xB5, 0xA3, 0x2D, 0xB1, 0x01, 0xFE, 0xDD, 0x6C, 0xB2, 0x30, 0x2C, 0x66, 0x54, 0xA7, 0x4A, 0xAF, 0xDF, 0x59, 0xDE, 0xE4, 0xA8, + 0xB2, 0x37, 0xC4, 0x7F, 0x9B, 0x8D, 0x96, 0x9A, 0xAE, 0x1B, 0xD6, 0xEC, 0x54, 0x39, 0x16, 0xA2, 0xB0, 0x1D, 0x9D, 0x38, 0x2D, 0x47, 0xD3, 0x8D, 0x95, 0x7B, + 0xAA, 0xF4, 0x45, 0x6D, 0x16, 0x9A, 0x33, 0x03, 0x5A, 0x3C, 0x1B, 0x88, 0x6D, 0xA9, 0x42, 0x4A, 0x78, 0x13, 0xC7, 0x98, 0xCD, 0x3D, 0x50, 0xE9, 0x46, 0x9B, + 0xA4, 0xD0, 0xB8, 0x0B, 0xE5, 0xE9, 0x33, 0x53, 0x6E, 0x62, 0xA9, 0x69, 0xA6, 0x31, 0xB3, 0x5A, 0x86, 0x47, 0x16, 0xC0, 0x8E, 0xEB, 0x39, 0xC4, 0x9B, 0xCC, + 0xB3, 0x48, 0x99, 0x1A, 0xB3, 0x95, 0x43, 0x04, 0x84, 0x04, 0x72, 0xCB, 0x60, 0x18, 0x6E, 0x6E, 0xDE, 0x6A, 0xAD, 0xC9, 0xF8, 0x8B, 0xE1, 0xB5, 0xB8, 0x4C, + 0xC6, 0x64, 0x6A, 0x3B, 0x44, 0xD8, 0xD2, 0x6F, 0x61, 0xDA, 0x93, 0x2F, 0x2D, 0xD7, 0xD3, 0x1C, 0x4F, 0x06, 0xA1, 0x36, 0xF5, 0x88, 0x93, 0x8F, 0x8F, 0xA0, + 0x55, 0xE4, 0x63, 0x4B, 0xEF, 0x96, 0x37, 0x30, 0x2C, 0xD3, 0xB0, 0x88, 0x3C, 0x79, 0x69, 0xFD, 0xC6, 0xD1, 0xB1, 0x56, 0x12, 0x8A, 0x31, 0x16, 0xB3, 0x2C, + 0x2B, 0xA1, 0xBC, 0x6E, 0x76, 0xC6, 0xFD, 0x46, 0xED, 0x74, 0xFE, 0x63, 0xF3, 0xE6, 0x9C, 0x30, 0x33, 0xD5, 0x56, 0x9E, 0x5D, 0xDD, 0x23, 0x36, 0xDC, 0x2A, + 0xC1, 0xC7, 0x7F, 0x2E, 0x88, 0x6E, 0x68, 0x4A, 0x23, 0xE2, 0xCE, 0xC7, 0x1D, 0xB0, 0xA9, 0xA6, 0xA2, 0x59, 0xBA, 0xD2, 0xB0, 0x1D, 0x03, 0x1C, 0x41, 0xA3, + 0xE1, 0xC6, 0x84, 0x2B, 0x30, 0x70, 0x2C, 0x49, 0x53, 0xC0, 0x72, 0x86, 0xCF, 0x44, 0x25, 0x22, 0x76, 0x1B, 0xFC, 0x49, 0x84, 0x1C, 0xFC, 0xE5, 0x3A, 0x90, + 0x80, 0x47, 0x8A, 0x3E, 0x4B, 0x5F, 0x51, 0x0A, 0xD3, 0x74, 0x86, 0xBF, 0x85, 0x76, 0xD3, 0xCA, 0xD4, 0x9D, 0xDF, 0xC8, 0xD7, 0x21, 0x0C, 0xB3, 0x93, 0x06, + 0x34, 0xBD, 0x9E, 0x2B, 0x2D, 0x05, 0xA3, 0x64, 0x53, 0x0C, 0xC3, 0x91, 0x8A, 0x55, 0x8E, 0xBF, 0xA8, 0x51, 0x14, 0x60, 0x57, 0xCC, 0x6A, 0x18, 0x3B, 0xD8, + 0x3F, 0x91, 0x0D, 0x31, 0x4E, 0x52, 0xA3, 0x08, 0xFE, 0xE4, 0x23, 0x49, 0x88, 0x2C, 0x37, 0x9A, 0x08, 0x10, 0xA7, 0x47, 0x94, 0x0D, 0xBC, 0x69, 0xDE, 0x2D, + 0xC0, 0x9A, 0x4D, 0x82, 0x6C, 0x74, 0x11, 0x20, 0xCE, 0xA2, 0x21, 0x37, 0xCA, 0xE0, 0xEF, 0x4E, 0x22, 0xDF, 0xF8, 0x66, 0xBC, 0xF2, 0x3C, 0xDB, 0x72, 0x2B, + 0x0D, 0x51, 0x69, 0x7E, 0xF6, 0xE7, 0xCA, 0xF5, 0x8C, 0xE9, 0x6D, 0x8B, 0xBB, 0x34, 0xF8, 0xD9, 0x52, 0x83, 0x14, 0x72, 0x4C, 0xBC, 0x35, 0x21, 0xD9, 0xE9, + 0x86, 0xA5, 0x5D, 0x43, 0xDC, 0x99, 0xCD, 0x4C, 0x91, 0xED, 0x4D, 0x56, 0x8E, 0x8B, 0x79, 0xDB, 0xD2, 0x36, 0x00, 0xB1, 0xB3, 0xD9, 0x71, 0xDC, 0x07, 0x25, + 0x3B, 0x6A, 0x4D, 0xC6, 0x82, 0xBE, 0xEC, 0x95, 0x87, 0x32, 0x16, 0x6A, 0xC2, 0x06, 0x76, 0x0C, 0xEF, 0x56, 0x78, 0x8F, 0x7B, 0xA2, 0xE0, 0x8E, 0xEF, 0x82, + 0x99, 0xC3, 0x42, 0x9C, 0xAE, 0xD3, 0xC9, 0x9C, 0x4C, 0xBE, 0x10, 0xFD, 0x69, 0x6E, 0x1A, 0x96, 0x97, 0x1E, 0xB6, 0x0D, 0x6B, 0xB9, 0xF2, 0x5A, 0x98, 0x4E, + 0x2D, 0x77, 0xA2, 0x73, 0x6A, 0x90, 0x3E, 0x8B, 0xDD, 0x6E, 0x56, 0x52, 0x31, 0x58, 0xDE, 0x64, 0x0B, 0x21, 0x4A, 0xEC, 0xC8, 0xD4, 0xC6, 0xC4, 0xCC, 0x22, + 0x99, 0x3B, 0x43, 0x4A, 0xD8, 0xE5, 0xB1, 0x2A, 0x3D, 0x77, 0xA3, 0x94, 0x85, 0x83, 0x57, 0xFF, 0xE8, 0x3F, 0xA4, 0xE5, 0x48, 0x8F, 0x0F, 0x62, 0x97, 0x5C, + 0x62, 0x82, 0x83, 0x25, 0xAE, 0x2D, 0xB5, 0xD4, 0x64, 0x1C, 0x5A, 0xAC, 0x81, 0xAA, 0xCC, 0x2E, 0x1D, 0xCD, 0x9A, 0x11, 0x88, 0x0E, 0x37, 0x07, 0xFE, 0x61, + 0xF6, 0x54, 0x41, 0x4A, 0x20, 0x18, 0xBC, 0x07, 0xD9, 0x53, 0x13, 0x16, 0x22, 0x0E, 0x94, 0x36, 0x3B, 0x28, 0x91, 0xA7, 0x44, 0x34, 0x9E, 0x49, 0x88, 0x2A, + 0xB4, 0x17, 0x96, 0xAA, 0x08, 0x7D, 0x29, 0x6E, 0x6D, 0xC2, 0xD4, 0x3F, 0x37, 0x58, 0xF8, 0x93, 0xC0, 0xE9, 0x34, 0x6F, 0x1A, 0x39, 0x9D, 0xF6, 0x3A, 0xBD, + 0x7E, 0x6E, 0x2E, 0x25, 0xE4, 0x32, 0x31, 0x95, 0x14, 0x04, 0x93, 0x20, 0xD0, 0xE4, 0xEB, 0xE2, 0x74, 0x6E, 0x5F, 0x13, 0x47, 0xA0, 0x88, 0x04, 0xB9, 0xFD, + 0x93, 0xBE, 0x2E, 0x81, 0x4D, 0x83, 0xA1, 0xE0, 0x5A, 0x14, 0x68, 0xE3, 0xE8, 0xBA, 0xEA, 0xA4, 0x9B, 0x69, 0xA1, 0x0C, 0x5D, 0x1B, 0xAC, 0x41, 0x1B, 0x9B, + 0x44, 0xCF, 0x88, 0xDC, 0x3A, 0x99, 0x6A, 0x2B, 0xD3, 0xCB, 0x91, 0xB7, 0xD6, 0xC1, 0x7F, 0x59, 0x3D, 0x52, 0xF7, 0xFA, 0x0D, 0x6B, 0x20, 0xE7, 0xD4, 0x25, + 0x7E, 0x17, 0xF4, 0xE9, 0x0F, 0xAB, 0xDA, 0x72, 0x49, 0x34, 0x68, 0x35, 0x21, 0x69, 0xB3, 0x55, 0xA9, 0x74, 0x5A, 0x1C, 0xD3, 0xA4, 0xE6, 0xA8, 0xB9, 0xA6, + 0x18, 0x24, 0x4A, 0x85, 0x78, 0x3E, 0x9D, 0xDA, 0x93, 0x95, 0x68, 0x04, 0x97, 0x33, 0xA9, 0x4D, 0x7C, 0xA7, 0xBE, 0xC8, 0x5C, 0xD3, 0xA0, 0x86, 0xBD, 0xB2, + 0x2C, 0xD4, 0x68, 0xCB, 0x73, 0x80, 0x4D, 0x41, 0x47, 0x72, 0x82, 0x2B, 0xE5, 0x9D, 0x31, 0xC1, 0xA6, 0xD5, 0x69, 0x12, 0x0E, 0x28, 0x08, 0x14, 0x41, 0x0C, + 0x51, 0x5C, 0x1B, 0x98, 0xF2, 0x51, 0x55, 0x93, 0x8B, 0x37, 0x5F, 0x2D, 0x44, 0x39, 0x83, 0xDF, 0x99, 0x0A, 0x03, 0x1C, 0xEB, 0xCE, 0x99, 0x8D, 0xB5, 0x46, + 0xE7, 0xA0, 0x73, 0xD0, 0x83, 0xFF, 0x04, 0xB9, 0x7B, 0xB6, 0x71, 0x71, 0xF1, 0xA6, 0x58, 0x5E, 0x22, 0xF8, 0xE4, 0x97, 0x50, 0xD2, 0xC2, 0x58, 0xAE, 0x2E, + 0xE4, 0x3D, 0x29, 0x5E, 0x4B, 0x51, 0xDB, 0x39, 0x23, 0x4C, 0x8A, 0x49, 0x17, 0x37, 0x44, 0x81, 0xB5, 0x14, 0x55, 0xF1, 0xC2, 0xFE, 0x77, 0x8B, 0x0D, 0xAF, + 0xFF, 0xE7, 0xAD, 0x3D, 0x22, 0x8A, 0xBF, 0xB5, 0xA5, 0x17, 0x96, 0x8B, 0x7B, 0xDF, 0xB6, 0xD1, 0x49, 0xD7, 0x7A, 0x8B, 0xE7, 0x33, 0x40, 0xA1, 0x05, 0x19, + 0xA7, 0x03, 0x13, 0xAF, 0xD4, 0x9C, 0x27, 0xD2, 0xA6, 0x84, 0x0C, 0xA6, 0x86, 0x69, 0xB6, 0x4C, 0x7B, 0x9D, 0x9F, 0x89, 0x64, 0x5B, 0xF2, 0x86, 0x9D, 0xE6, + 0x9B, 0x7C, 0x59, 0x6A, 0x57, 0x10, 0xB9, 0xFE, 0x12, 0xD4, 0xFE, 0xBD, 0x1D, 0x2E, 0xD3, 0x35, 0xCA, 0x0D, 0x14, 0x25, 0xEC, 0xB1, 0x5A, 0x47, 0x52, 0xA6, + 0xC4, 0x32, 0xC1, 0xCC, 0x59, 0x9D, 0xBB, 0x36, 0xBC, 0xC9, 0xBC, 0xC4, 0xA4, 0x6A, 0x69, 0xBB, 0x06, 0x5B, 0xBE, 0x71, 0x88, 0xA9, 0x61, 0x06, 0x5F, 0x6A, + 0x36, 0x9E, 0x3B, 0x31, 0x89, 0x82, 0xCB, 0x70, 0x42, 0x45, 0xF7, 0x70, 0x2A, 0x29, 0x6D, 0x96, 0x3B, 0xA4, 0xC7, 0x6A, 0xB1, 0x59, 0xE7, 0xA4, 0xFB, 0x71, + 0xCF, 0x10, 0x37, 0x2A, 0x10, 0xD1, 0xFD, 0xA0, 0x3D, 0x73, 0xC8, 0xAD, 0x04, 0x33, 0x07, 0xFC, 0xEF, 0x29, 0xAB, 0x95, 0x96, 0x2F, 0x02, 0xD0, 0x01, 0x80, + 0x5B, 0x51, 0xBB, 0xEF, 0x4A, 0x74, 0x9D, 0xDE, 0xA5, 0x8C, 0x3D, 0x06, 0x95, 0xC0, 0x5A, 0x4D, 0x22, 0xDC, 0x64, 0x0C, 0xA1, 0x62, 0x53, 0xF5, 0x47, 0x5F, + 0xE1, 0x4D, 0x93, 0x4C, 0xBD, 0x94, 0x85, 0x0E, 0x9A, 0xA7, 0xF6, 0xB2, 0xA3, 0x5B, 0x2B, 0x52, 0x27, 0xC8, 0x8D, 0x1C, 0x41, 0xC1, 0x2E, 0xDD, 0xFA, 0x84, + 0x98, 0x31, 0x7A, 0x16, 0x46, 0x9E, 0xAE, 0x12, 0x3F, 0x7D, 0xA6, 0x6A, 0x86, 0x36, 0x0B, 0x3E, 0xE4, 0x83, 0x7A, 0xC8, 0x2F, 0x8D, 0xEE, 0x50, 0xB8, 0x8E, + 0x90, 0xD1, 0x38, 0x8B, 0x34, 0x56, 0xF1, 0x92, 0x1A, 0xB2, 0x52, 0x27, 0xC8, 0xD1, 0x58, 0x24, 0x54, 0x54, 0xB6, 0x57, 0x66, 0x45, 0x98, 0xCD, 0x1A, 0x4D, + 0xA6, 0xB1, 0x1B, 0x0B, 0x0D, 0xD2, 0x5E, 0x34, 0x57, 0x0D, 0x30, 0x8A, 0xF4, 0x27, 0x63, 0xEE, 0x91, 0x7A, 0xA2, 0x3A, 0xEC, 0xE4, 0x74, 0x39, 0x31, 0x6D, + 0x37, 0xDB, 0xAF, 0xB4, 0x31, 0xC8, 0x6F, 0xE5, 0x09, 0x3A, 0xE2, 0x55, 0x4D, 0x61, 0xE5, 0x89, 0x1A, 0xB7, 0xF0, 0x8E, 0xD4, 0xD0, 0x9D, 0xE9, 0x53, 0xD9, + 0xEE, 0x98, 0x90, 0xB9, 0xDA, 0x11, 0x46, 0xDA, 0xCC, 0xFA, 0x9B, 0x47, 0x6E, 0x60, 0xBE, 0x89, 0x6B, 0x75, 0xA7, 0xCA, 0x84, 0x88, 0xC3, 0x68, 0x6C, 0x90, + 0x53, 0x65, 0x8A, 0x80, 0x99, 0x7A, 0x98, 0x1B, 0xBA, 0x4E, 0x32, 0xAB, 0x9C, 0x38, 0xE7, 0xCD, 0x0E, 0x95, 0x9A, 0xB0, 0x9C, 0x56, 0x40, 0x93, 0xDD, 0x74, + 0x55, 0x66, 0x0E, 0x57, 0x29, 0xA1, 0x2F, 0x26, 0x21, 0x61, 0x93, 0x48, 0x15, 0x56, 0x1C, 0x22, 0x51, 0x11, 0x3A, 0x99, 0xD8, 0x0E, 0x5B, 0xC4, 0x4D, 0x99, + 0xF8, 0x97, 0x9B, 0x59, 0x21, 0x72, 0x51, 0xE9, 0x6E, 0x27, 0xA1, 0x23, 0x73, 0xA3, 0x83, 0xBA, 0xEB, 0xB8, 0xC2, 0x87, 0xE3, 0xB4, 0x4A, 0x7A, 0x3C, 0x61, + 0xCB, 0x24, 0x55, 0x18, 0x02, 0x03, 0x35, 0xA2, 0xC8, 0x40, 0x0E, 0xD8, 0x6A, 0x53, 0xA1, 0x09, 0xAA, 0xE8, 0xD2, 0x4A, 0x9B, 0xAF, 0x36, 0xF1, 0x85, 0xC1, + 0x56, 0xDA, 0x7A, 0xCB, 0x16, 0x17, 0xDF, 0xA8, 0x05, 0x24, 0xFB, 0x4D, 0x15, 0xCD, 0x3D, 0xE5, 0x8F, 0x19, 0x44, 0x06, 0x03, 0xB1, 0xBF, 0xDD, 0x2A, 0xDE, + 0xAA, 0x6C, 0x08, 0x39, 0x3B, 0x8C, 0xEC, 0x8F, 0x3B, 0x3B, 0x0C, 0xB7, 0xF2, 0x9D, 0xE1, 0x26, 0xB9, 0xE8, 0x36, 0x3A, 0xDE, 0xCF, 0xC4, 0xD4, 0x5C, 0xF7, + 0xBC, 0x86, 0x9B, 0xBD, 0x6A, 0xF1, 0x5D, 0x75, 0x67, 0xBA, 0x71, 0xAD, 0x18, 0xFA, 0x79, 0xCD, 0xB4, 0x67, 0x76, 0xE2, 0x1E, 0xBD, 0xCF, 0xB4, 0x0C, 0xA3, + 0xFD, 0x79, 0x2D, 0xB6, 0xE2, 0x58, 0xA3, 0x50, 0xE1, 0xA5, 0xDA, 0xE8, 0xC9, 0x37, 0x27, 0x47, 0x47, 0xC3, 0x67, 0x4F, 0xAC, 0xB1, 0xBB, 0xE4, 0xFF, 0x7F, + 0x64, 0x0B, 0xB4, 0x2E, 0xF1, 0x3C, 0xB0, 0x39, 0xF7, 0xEC, 0x90, 0x62, 0x4B, 0x50, 0x70, 0x08, 0x24, 0xA4, 0x10, 0xC5, 0xB3, 0x41, 0x11, 0x5D, 0x7E, 0x13, + 0x17, 0x12, 0x9C, 0xB1, 0xE6, 0x08, 0x9A, 0xD0, 0x66, 0x6C, 0xAE, 0x41, 0x63, 0x48, 0x8D, 0x2A, 0x63, 0x6C, 0xDF, 0x24, 0x49, 0xA7, 0xDC, 0x70, 0x4D, 0xF1, + 0x56, 0x44, 0x4F, 0x43, 0x08, 0x60, 0x14, 0x1C, 0xD7, 0x59, 0xA1, 0x8D, 0xB0, 0x51, 0x4C, 0xF6, 0xD8, 0xF8, 0x66, 0x62, 0x7E, 0xF1, 0x95, 0x5E, 0xF3, 0xB5, + 0x61, 0xD9, 0x1E, 0x1B, 0x49, 0x52, 0xBA, 0x8A, 0xB1, 0xCA, 0x61, 0x22, 0xAB, 0x85, 0x8C, 0x0B, 0x10, 0x6D, 0x8B, 0x62, 0x67, 0xD7, 0xB2, 0x31, 0x51, 0x6C, + 0x11, 0x85, 0xFA, 0xC0, 0xB5, 0xD1, 0x2F, 0x17, 0x6F, 0xFF, 0xA1, 0xBC, 0x7B, 0xF3, 0x6F, 0xA1, 0x86, 0xF2, 0x88, 0xC2, 0xE0, 0x2C, 0xD1, 0x33, 0x05, 0x63, + 0xFA, 0xF0, 0x65, 0x52, 0xE3, 0x9A, 0xA1, 0x18, 0x30, 0x19, 0x32, 0x89, 0x35, 0xF3, 0xE6, 0xE7, 0x35, 0xB5, 0x86, 0xBB, 0x5B, 0xFC, 0xB3, 0x6E, 0x4D, 0xC1, + 0xC0, 0x4D, 0x0F, 0xAE, 0x35, 0x73, 0x85, 0x47, 0x1D, 0x19, 0x5E, 0x37, 0x4D, 0x4B, 0xD8, 0x8C, 0x47, 0x94, 0x40, 0xC6, 0x91, 0x08, 0x1C, 0x97, 0x72, 0x6D, + 0x74, 0x45, 0xBC, 0xB3, 0x43, 0x76, 0x2B, 0x47, 0x6B, 0xD9, 0x7D, 0x83, 0x0B, 0x33, 0x73, 0xC8, 0x32, 0xA1, 0x2C, 0xC5, 0x4F, 0x1D, 0x6D, 0x41, 0x50, 0x2A, + 0x52, 0x9A, 0x8F, 0x6A, 0x3D, 0x80, 0xAC, 0x8D, 0x3E, 0x10, 0x9A, 0x65, 0x00, 0x19, 0x52, 0x8A, 0x3F, 0xE3, 0x29, 0x7C, 0xAC, 0xFF, 0xC0, 0x9E, 0xF9, 0x92, + 0x5D, 0x4B, 0x63, 0x66, 0x2E, 0x21, 0xF7, 0xC7, 0xAD, 0x96, 0x32, 0x78, 0xF7, 0x5E, 0x69, 0xB5, 0x24, 0x1A, 0xDB, 0x4B, 0xEA, 0x4E, 0xBE, 0xFE, 0x7B, 0x35, + 0x3E, 0xA1, 0x20, 0x54, 0x3F, 0xEC, 0xA8, 0x36, 0xFA, 0xF1, 0xEA, 0x97, 0xD7, 0x2F, 0x1A, 0xDD, 0xC1, 0xB0, 0x73, 0xA3, 0x9E, 0x74, 0x3B, 0xCD, 0xB3, 0x43, + 0x06, 0x57, 0xBC, 0x83, 0x6E, 0x6D, 0xF4, 0x5E, 0xF9, 0xEE, 0xCD, 0xAB, 0x86, 0xDA, 0x39, 0xAE, 0x8C, 0x4C, 0xAD, 0x8D, 0x7E, 0xFE, 0x31, 0xA4, 0x6C, 0xD8, + 0xA9, 0x82, 0x0C, 0x4C, 0xFF, 0x47, 0xA0, 0x8B, 0xA1, 0xEA, 0xF7, 0x0B, 0xA1, 0x42, 0x91, 0xF7, 0xCA, 0x89, 0x5C, 0x3D, 0x81, 0x7E, 0x29, 0x0F, 0x9D, 0xFE, + 0xF1, 0x8D, 0x3A, 0x18, 0xF6, 0xCB, 0xF3, 0xA0, 0x1E, 0xA3, 0x74, 0x81, 0x90, 0xC6, 0xF1, 0xB0, 0x5F, 0x15, 0xD7, 0x11, 0xE2, 0x02, 0x81, 0x1C, 0x75, 0x41, + 0x1E, 0xDD, 0xE3, 0x0A, 0xA2, 0x55, 0x87, 0xB5, 0x11, 0x55, 0xF9, 0x09, 0xA2, 0xEA, 0x14, 0x43, 0x85, 0xA2, 0xED, 0x96, 0x14, 0xED, 0xA0, 0x36, 0xFA, 0x09, + 0x45, 0x8B, 0x96, 0x01, 0x3C, 0x54, 0x31, 0x0F, 0xB5, 0x0F, 0x51, 0x8A, 0xE2, 0xEA, 0xA2, 0xDD, 0x76, 0xBA, 0x55, 0x44, 0xDB, 0xAB, 0x8D, 0x50, 0x1C, 0x88, + 0xE9, 0xA8, 0x8A, 0x03, 0xA8, 0xE0, 0x4D, 0x94, 0x26, 0x20, 0xE7, 0xE6, 0x68, 0x78, 0x5C, 0x01, 0x13, 0xB8, 0xD2, 0xD5, 0x27, 0x40, 0x75, 0x0C, 0x92, 0xAA, + 0xE4, 0x47, 0x2A, 0xF8, 0x11, 0x22, 0x1A, 0xF6, 0x3B, 0x37, 0xFD, 0x2A, 0x56, 0x03, 0x7E, 0xF1, 0x06, 0x11, 0x01, 0x92, 0x9B, 0x5E, 0x15, 0x29, 0x81, 0x53, + 0x5C, 0x7C, 0xFF, 0x5D, 0xA3, 0x0F, 0x9C, 0x75, 0x4F, 0x86, 0xE5, 0xF1, 0x80, 0x43, 0x00, 0x1D, 0x48, 0x4B, 0x69, 0x14, 0xE0, 0x08, 0x3F, 0x22, 0x4F, 0x88, + 0xA7, 0x5B, 0x2C, 0xC4, 0xC4, 0x11, 0x81, 0x65, 0x03, 0x3C, 0xE2, 0x28, 0x8D, 0x02, 0x0C, 0xFA, 0x0D, 0x25, 0x06, 0x11, 0xA9, 0x47, 0x15, 0x04, 0x03, 0xE6, + 0xFC, 0x23, 0x4A, 0x18, 0x90, 0x60, 0xE4, 0xAC, 0x10, 0x83, 0x6B, 0x23, 0x70, 0x0A, 0x8C, 0x36, 0xE5, 0xCD, 0x0F, 0x68, 0xA1, 0x5C, 0xA9, 0x43, 0xEA, 0xF2, + 0xE5, 0x89, 0x01, 0x3B, 0x3E, 0x19, 0xDE, 0x9C, 0x0C, 0xE5, 0x10, 0x60, 0xE6, 0x81, 0xA3, 0x65, 0x56, 0x6E, 0x92, 0x9D, 0xBA, 0x64, 0xA5, 0x25, 0xFF, 0x5A, + 0x69, 0x26, 0xCC, 0xB3, 0x0A, 0x27, 0x25, 0x1C, 0x0E, 0x64, 0xC2, 0x0E, 0xE4, 0xF2, 0x91, 0x08, 0x25, 0xC1, 0x86, 0xB7, 0xDA, 0xA8, 0x2F, 0x91, 0xF7, 0xC5, + 0x26, 0x06, 0x14, 0x36, 0x46, 0x3F, 0x4D, 0x46, 0xD1, 0xF2, 0x30, 0x0D, 0x05, 0x6F, 0xE8, 0xD5, 0x22, 0x51, 0xA3, 0x54, 0xC2, 0x23, 0xA0, 0x55, 0xBB, 0xA9, + 0x8D, 0x86, 0xBD, 0xDC, 0x44, 0xB1, 0xBC, 0x32, 0xC6, 0xB4, 0x56, 0x64, 0x11, 0xD7, 0x2D, 0xAC, 0x8F, 0x10, 0xB4, 0x36, 0x7A, 0x19, 0x1C, 0x57, 0xD1, 0x4A, + 0x2B, 0x8F, 0x53, 0x0A, 0x9B, 0xA2, 0x96, 0x08, 0x39, 0x4C, 0x33, 0xAD, 0x1E, 0x57, 0x4D, 0xA8, 0x99, 0xED, 0x2A, 0x66, 0x97, 0x7A, 0xC1, 0x69, 0xAD, 0xA3, + 0xB9, 0x5E, 0x61, 0xAD, 0xF8, 0x80, 0x30, 0x4E, 0xF0, 0xA3, 0x7B, 0xD3, 0x48, 0x40, 0xCA, 0xDF, 0x40, 0x1F, 0xAE, 0xE6, 0xAD, 0x58, 0xD5, 0xB2, 0xB0, 0x46, + 0x42, 0x50, 0x48, 0x4B, 0x82, 0xE3, 0x4A, 0x5A, 0xA9, 0x12, 0xBE, 0x22, 0xE4, 0x70, 0xBD, 0xF8, 0x21, 0xAC, 0xBF, 0x23, 0xBD, 0xE4, 0x51, 0x5B, 0x49, 0x2F, + 0x73, 0xCD, 0x59, 0x96, 0x0A, 0x5F, 0x01, 0x24, 0x68, 0xC5, 0x3F, 0xBC, 0x37, 0x57, 0x09, 0x89, 0xF9, 0x1B, 0xF8, 0x8A, 0x4E, 0x2C, 0xDB, 0x70, 0x8B, 0x57, + 0x1D, 0x38, 0x5C, 0x6D, 0xF4, 0x8A, 0xB4, 0x7E, 0xC0, 0xA3, 0x2A, 0xEA, 0x78, 0xB1, 0xF2, 0xEC, 0x0A, 0x0A, 0xF1, 0x69, 0x61, 0xEA, 0xE8, 0x70, 0x6D, 0x1C, + 0xEF, 0x48, 0x1B, 0xC7, 0x3B, 0xD4, 0x86, 0x46, 0x3E, 0x9B, 0xE4, 0x9A, 0x98, 0x85, 0xD5, 0xE1, 0x03, 0xD6, 0x46, 0x97, 0x37, 0x4B, 0xDB, 0xC5, 0x47, 0xB8, + 0xDE, 0xE2, 0x79, 0x25, 0x27, 0x19, 0x54, 0xD0, 0x49, 0x40, 0x10, 0xF7, 0x91, 0x01, 0xD7, 0xCA, 0x60, 0x47, 0x5A, 0xC9, 0xA3, 0xB5, 0x8A, 0x56, 0x66, 0x9A, + 0x61, 0x4D, 0x88, 0x61, 0xE2, 0xE3, 0x24, 0x45, 0x15, 0x13, 0x81, 0xAD, 0x8D, 0x5E, 0x87, 0x27, 0x55, 0x14, 0xD3, 0xA9, 0xA0, 0x97, 0x28, 0x3D, 0x71, 0x7F, + 0x19, 0xC0, 0x54, 0x7C, 0x47, 0xBA, 0x51, 0xD5, 0x5D, 0x8E, 0x2A, 0x4B, 0x32, 0x31, 0x34, 0xF3, 0x33, 0x99, 0x4E, 0x61, 0x1A, 0x54, 0x7C, 0x68, 0x89, 0x81, + 0xC3, 0xF8, 0xC2, 0xCE, 0x95, 0x4B, 0x7A, 0x5E, 0xB8, 0x8E, 0x9A, 0x40, 0x57, 0xBE, 0x98, 0x9A, 0x9C, 0x13, 0x0A, 0xCB, 0xA3, 0x3F, 0xD8, 0x01, 0x9D, 0xE5, + 0xA7, 0xAD, 0x3F, 0x90, 0x19, 0xDD, 0xCE, 0x50, 0x65, 0xF6, 0xFC, 0xDA, 0xD1, 0x6E, 0xE9, 0xBB, 0x21, 0xAA, 0xCC, 0xE5, 0x3F, 0x10, 0x5D, 0xF9, 0x68, 0x58, + 0xE5, 0x99, 0xE9, 0x23, 0x21, 0x84, 0x58, 0xD5, 0xB0, 0x0C, 0x60, 0x8A, 0x04, 0x07, 0xD5, 0x90, 0x0C, 0x71, 0x6D, 0x61, 0x69, 0x68, 0x0F, 0x61, 0x12, 0xAF, + 0xAD, 0xC7, 0xC5, 0x07, 0x94, 0xF5, 0x18, 0xC6, 0xE5, 0x9F, 0x5F, 0x2A, 0x97, 0x74, 0x03, 0x7E, 0xE1, 0x70, 0xC5, 0xF6, 0x06, 0xCA, 0x18, 0x7A, 0xB8, 0x84, + 0x84, 0x7D, 0x6E, 0xAC, 0xED, 0x89, 0x1D, 0x48, 0x76, 0x7D, 0x4F, 0xC0, 0x9E, 0x4F, 0x20, 0xDD, 0x4A, 0x55, 0x8B, 0x70, 0x2B, 0xC7, 0xE3, 0x0E, 0x53, 0xB1, + 0xC9, 0xBA, 0x78, 0x1A, 0x36, 0x59, 0x83, 0x9A, 0xF4, 0x6B, 0x7C, 0x36, 0x43, 0x57, 0x40, 0x5F, 0x7B, 0x51, 0x14, 0xF6, 0x7A, 0x3F, 0x8A, 0xA2, 0xFC, 0xDE, + 0xB7, 0xA2, 0xC0, 0x5A, 0x3E, 0xE3, 0x38, 0x5A, 0xC6, 0xA9, 0x28, 0x60, 0x6D, 0xF4, 0x4E, 0xB3, 0x56, 0x30, 0xC8, 0xEC, 0x4B, 0x61, 0x41, 0xC7, 0xF7, 0xE6, + 0x5E, 0x9C, 0xEF, 0xFB, 0x56, 0x1D, 0x10, 0xB2, 0xB0, 0xF5, 0xE2, 0xD3, 0x1D, 0x0E, 0xC7, 0x42, 0xE2, 0x3B, 0x38, 0x2A, 0x9C, 0x18, 0xF8, 0x18, 0x76, 0x9C, + 0x11, 0xB0, 0xA9, 0x54, 0xF9, 0x64, 0xE0, 0x6A, 0x65, 0x59, 0xB7, 0x55, 0x32, 0x81, 0x0B, 0xD3, 0x5E, 0xE9, 0xE5, 0x31, 0x40, 0x1A, 0xF0, 0xCF, 0xE9, 0xD4, + 0x98, 0x94, 0x4F, 0x24, 0x70, 0x79, 0xC1, 0x5E, 0x48, 0xC2, 0xEF, 0x78, 0xE0, 0x25, 0x93, 0x12, 0x33, 0xB9, 0x09, 0x68, 0xF1, 0xF2, 0x62, 0xAF, 0x03, 0x2F, + 0xF4, 0x79, 0x4F, 0x91, 0x01, 0xB9, 0xBD, 0xEF, 0xA0, 0x00, 0x44, 0x7C, 0xA6, 0xC6, 0x53, 0x46, 0x59, 0x0C, 0x32, 0x88, 0xE8, 0xFE, 0xF4, 0xFB, 0xBE, 0xE6, + 0x77, 0x21, 0x45, 0xF1, 0xD9, 0x1D, 0x2E, 0x81, 0x07, 0xD3, 0xBB, 0x5E, 0x77, 0xBB, 0x13, 0x3C, 0x44, 0xBE, 0x5B, 0xFD, 0x74, 0xCB, 0xA8, 0x06, 0xA2, 0xD1, + 0x0F, 0xB8, 0xCE, 0x50, 0x20, 0x60, 0x57, 0x77, 0xA4, 0xEE, 0xFD, 0x79, 0x52, 0xF7, 0x01, 0xB8, 0xD2, 0xAC, 0x44, 0xC4, 0x9B, 0x61, 0xC4, 0x7B, 0x7D, 0xB1, + 0x1F, 0x0D, 0xCD, 0xEE, 0x2D, 0xD4, 0xCD, 0xEE, 0x35, 0xD4, 0x29, 0x7C, 0x73, 0xA2, 0x2F, 0x85, 0x92, 0x19, 0x2C, 0x07, 0x64, 0xB5, 0xAC, 0x2A, 0x41, 0x4E, + 0xBD, 0xA9, 0x12, 0xE5, 0x7C, 0x32, 0xE2, 0x41, 0x6E, 0x18, 0xAE, 0x8A, 0x0C, 0xB6, 0xBB, 0xAC, 0xDB, 0xCF, 0xA3, 0xB6, 0x8A, 0xD3, 0x38, 0xDA, 0xFA, 0xF3, + 0x6C, 0xA1, 0x15, 0x56, 0x06, 0x87, 0x03, 0x5D, 0xBC, 0x7B, 0xB1, 0xCF, 0x74, 0xC1, 0xEF, 0xF7, 0x7E, 0xFC, 0x28, 0xE0, 0xFA, 0xBE, 0x63, 0x9D, 0x49, 0xAC, + 0xE2, 0xC1, 0x0E, 0x81, 0x6A, 0xA3, 0xB7, 0xC4, 0x72, 0x95, 0x0B, 0xDB, 0xE1, 0x2F, 0xE4, 0xDC, 0x8B, 0xD6, 0x68, 0xCF, 0xF7, 0xA3, 0x32, 0xC6, 0xF4, 0x7D, + 0xEB, 0x6B, 0xBE, 0x30, 0x1C, 0xC7, 0x76, 0x0A, 0xAB, 0x8C, 0xC3, 0xC1, 0xB4, 0xA2, 0xF5, 0x8E, 0x1E, 0xED, 0x45, 0x5D, 0x7E, 0xAF, 0xF7, 0xA3, 0xB1, 0x80, + 0xE7, 0xFB, 0x56, 0xDA, 0xF5, 0xD4, 0x34, 0x96, 0x85, 0x55, 0x46, 0xA1, 0x6A, 0xA3, 0x4F, 0xAD, 0xEF, 0xE0, 0xEF, 0x5E, 0xD4, 0xC5, 0x7A, 0xBC, 0x1F, 0x65, + 0x71, 0x6E, 0xEF, 0x5B, 0x55, 0xE3, 0x65, 0xF1, 0x70, 0x08, 0x30, 0xB5, 0xD1, 0xCB, 0xF7, 0xFB, 0xC9, 0xFD, 0xB0, 0x33, 0x49, 0x0D, 0x55, 0xD2, 0x07, 0x65, + 0xEA, 0xBE, 0xB5, 0xB1, 0x2E, 0xA1, 0x8D, 0x35, 0x12, 0xFE, 0xF3, 0x9E, 0xB4, 0xB1, 0x96, 0xD7, 0xC6, 0x96, 0xFD, 0x65, 0xFD, 0x10, 0xF4, 0x43, 0x9F, 0x82, + 0x1D, 0x6B, 0xC5, 0x87, 0x23, 0x1F, 0x10, 0x37, 0x8D, 0xC1, 0x91, 0xF2, 0x52, 0xDB, 0xCF, 0x80, 0x14, 0xF4, 0xBB, 0x0F, 0x17, 0x0A, 0x99, 0xDC, 0x87, 0x9E, + 0xA2, 0xCF, 0x76, 0xF1, 0xF7, 0xA3, 0xE6, 0x29, 0x84, 0x3F, 0x63, 0x44, 0x97, 0xD4, 0x89, 0xD7, 0x72, 0x3D, 0xC3, 0x34, 0x21, 0x11, 0x27, 0x9E, 0x72, 0x85, + 0x87, 0x92, 0x0F, 0x15, 0x45, 0xB0, 0xF8, 0x8F, 0x12, 0x7A, 0x0E, 0xD1, 0x16, 0xB5, 0xD1, 0x15, 0xBE, 0x39, 0x16, 0x70, 0xE1, 0x59, 0x3E, 0x32, 0xE9, 0xC7, + 0x8F, 0xE8, 0x83, 0x86, 0xF8, 0xE4, 0x60, 0xFC, 0x45, 0xCF, 0x20, 0x66, 0xF6, 0xB0, 0xF5, 0xE8, 0x8C, 0xBE, 0xB4, 0x92, 0x37, 0xA3, 0xCF, 0xD8, 0xAE, 0xF9, + 0x43, 0x93, 0x63, 0xDB, 0xD4, 0x9F, 0x45, 0x16, 0x9B, 0xAE, 0x82, 0xA7, 0x00, 0x11, 0x04, 0xF4, 0xE4, 0x63, 0xC8, 0x11, 0xF6, 0xDC, 0xF1, 0xD1, 0xB3, 0x07, + 0x35, 0xF1, 0xD5, 0x49, 0x19, 0xD2, 0x4E, 0x79, 0x62, 0xD1, 0x21, 0xB3, 0xC0, 0xF0, 0x44, 0x0F, 0xB2, 0x0A, 0x9F, 0x5F, 0xFC, 0x40, 0x66, 0x86, 0x0B, 0x34, + 0x2A, 0xA0, 0xA7, 0x43, 0xFA, 0xE8, 0x17, 0xB3, 0x2D, 0xB9, 0xC7, 0x0A, 0xA3, 0x5D, 0xF2, 0x67, 0xC6, 0x85, 0x4F, 0x89, 0x16, 0x0A, 0x57, 0xC9, 0x67, 0x3A, + 0xE3, 0x18, 0xF3, 0xAC, 0xF0, 0x71, 0xAB, 0x35, 0xEF, 0xE3, 0x43, 0x6C, 0x8A, 0xCF, 0xDA, 0xD9, 0xE1, 0xBC, 0x9F, 0xF7, 0xC4, 0x4A, 0xEE, 0x13, 0x88, 0xC0, + 0x69, 0xE9, 0x07, 0x10, 0x51, 0x4A, 0x23, 0xA0, 0xE6, 0x40, 0x79, 0xA7, 0xB9, 0x5F, 0x0E, 0x94, 0x4F, 0x38, 0x1F, 0xDF, 0xE3, 0x73, 0x88, 0x48, 0xBB, 0xA6, + 0xEB, 0x4E, 0xEA, 0xB3, 0x88, 0xFD, 0xD8, 0xB3, 0x88, 0x43, 0xFF, 0x59, 0xC4, 0x61, 0xB8, 0xF9, 0xE5, 0xA6, 0xD7, 0xE9, 0x1C, 0xCB, 0xB0, 0x2E, 0xF9, 0x3C, + 0xE2, 0x56, 0x78, 0x5A, 0x80, 0x34, 0x25, 0x79, 0xEA, 0xFB, 0x3C, 0x45, 0x36, 0x89, 0xDE, 0x4C, 0xA7, 0x0F, 0x8D, 0x23, 0x5E, 0xA6, 0x2E, 0xCF, 0x52, 0xA7, + 0xBB, 0xEF, 0x87, 0x46, 0xA9, 0x71, 0x6F, 0xEB, 0x99, 0x51, 0xDA, 0x24, 0x19, 0x0D, 0x07, 0x99, 0xC1, 0x90, 0x82, 0x30, 0xA7, 0x7F, 0xBD, 0x4D, 0xA7, 0x9F, + 0x55, 0x70, 0xFA, 0xD9, 0x86, 0xD3, 0xEF, 0xD1, 0xDB, 0x7D, 0xC2, 0xFF, 0x6E, 0x1E, 0xEF, 0xF3, 0x55, 0xC0, 0xEB, 0x85, 0x7C, 0x75, 0x3A, 0x5B, 0xF5, 0xFB, + 0x5C, 0x27, 0x09, 0x8C, 0xE1, 0xF5, 0x36, 0x9D, 0x24, 0xC5, 0x74, 0x4B, 0xD9, 0x29, 0x0F, 0x3B, 0xA3, 0xFD, 0x8C, 0x4B, 0x34, 0x9B, 0x8A, 0x2A, 0x94, 0xF7, + 0x8E, 0x8F, 0x88, 0xF5, 0xFA, 0x3C, 0x75, 0xDA, 0x86, 0x7A, 0xE4, 0x9F, 0x4F, 0x4F, 0x6D, 0xB2, 0x9D, 0xC4, 0x6C, 0x09, 0x79, 0x70, 0xE1, 0xC4, 0xEC, 0xFD, + 0xDB, 0xB7, 0xC5, 0x72, 0xB1, 0x68, 0x2F, 0x0F, 0x24, 0x17, 0xCB, 0x2C, 0x8D, 0xDC, 0x2E, 0xE1, 0x06, 0x52, 0x5D, 0xCA, 0x74, 0x43, 0xF0, 0xDA, 0xE8, 0x25, + 0x3D, 0x56, 0x22, 0x12, 0x2B, 0x64, 0xBC, 0xD2, 0x33, 0x3F, 0x0A, 0x18, 0xA9, 0x9D, 0x84, 0x24, 0x24, 0x75, 0x23, 0x89, 0x2B, 0xA3, 0x5E, 0x12, 0x61, 0x4F, + 0x9E, 0xA9, 0xCA, 0x3E, 0x41, 0x9B, 0xE4, 0xA5, 0xC2, 0x4B, 0x87, 0x94, 0x56, 0x1B, 0x87, 0xAD, 0x8D, 0xDE, 0x3B, 0x44, 0x79, 0x65, 0x5C, 0xCB, 0xF3, 0x16, + 0xD9, 0x37, 0x14, 0x20, 0x91, 0x93, 0x72, 0x72, 0x43, 0x8F, 0x70, 0x93, 0x10, 0xAE, 0xBD, 0xC9, 0xEE, 0xAE, 0x11, 0x60, 0x85, 0xB4, 0xAB, 0x5B, 0x0D, 0x43, + 0xAF, 0x36, 0xEA, 0x55, 0xC3, 0xD0, 0xAF, 0x8D, 0xFA, 0xD5, 0x30, 0x0C, 0x40, 0x0E, 0xED, 0x41, 0x35, 0x1C, 0xC3, 0xDA, 0x68, 0x58, 0x0D, 0xC3, 0x11, 0xC8, + 0xB2, 0x2A, 0x15, 0x90, 0xB9, 0x1C, 0x17, 0xC0, 0x90, 0xBF, 0xE7, 0x89, 0xB5, 0xAA, 0xEE, 0x3C, 0x8B, 0x95, 0x59, 0xDA, 0x79, 0x38, 0x6C, 0x6D, 0xF4, 0x6E, + 0x65, 0x7A, 0xC6, 0xD2, 0x34, 0x60, 0xDA, 0xDE, 0xE8, 0x2B, 0x2D, 0xA5, 0x3B, 0xE8, 0x36, 0xF7, 0x98, 0x61, 0xFA, 0x74, 0xC8, 0xBD, 0xDA, 0xA6, 0xE7, 0x27, + 0x61, 0xEA, 0x71, 0xF4, 0x11, 0xE3, 0x07, 0x11, 0xCE, 0x1C, 0xDB, 0xF6, 0x4A, 0xAB, 0xC3, 0x07, 0x86, 0x34, 0x1F, 0x8E, 0x4A, 0x47, 0xB3, 0x10, 0x4D, 0x19, + 0x43, 0x4F, 0xD9, 0xF3, 0x58, 0x31, 0x9C, 0xA9, 0xC5, 0xC2, 0xD9, 0xFE, 0xDC, 0xC7, 0xBD, 0x2D, 0x9F, 0x32, 0x70, 0x58, 0x98, 0xAD, 0xDE, 0xC2, 0x0C, 0x71, + 0x81, 0x0A, 0x53, 0x1A, 0x1D, 0x70, 0x1F, 0x75, 0xB0, 0x4F, 0xEF, 0xF1, 0xC9, 0x28, 0xF8, 0x62, 0xA8, 0xA8, 0xF7, 0x3C, 0x0C, 0xE7, 0xA1, 0xFA, 0x20, 0xA6, + 0x3E, 0x28, 0xAF, 0x11, 0x1F, 0x1A, 0xF2, 0x01, 0x7C, 0x3D, 0x57, 0x25, 0x3F, 0x8A, 0x20, 0x2B, 0xE7, 0x48, 0xD5, 0x9D, 0x46, 0xE8, 0x8A, 0x15, 0xF3, 0x82, + 0x6E, 0xE5, 0x51, 0xBD, 0xF7, 0x10, 0xC7, 0xC2, 0x25, 0xBE, 0xD1, 0x8D, 0xC8, 0xED, 0xB6, 0xA2, 0xC8, 0xA2, 0x49, 0x24, 0x83, 0xE5, 0x46, 0xC3, 0x76, 0x99, + 0xEE, 0x35, 0xF3, 0xF7, 0x09, 0xD8, 0x5C, 0xF6, 0x29, 0xBE, 0x28, 0x17, 0x61, 0x4E, 0x34, 0x11, 0x08, 0x78, 0x7D, 0x70, 0xB3, 0x00, 0x24, 0xAC, 0xF4, 0x34, + 0x80, 0x03, 0x73, 0x15, 0x06, 0x91, 0xB8, 0xA7, 0x16, 0x88, 0xC4, 0xD1, 0x19, 0x41, 0x80, 0xAF, 0xE4, 0x68, 0x77, 0xEF, 0xE9, 0x7F, 0x5F, 0x1C, 0x3C, 0x2A, + 0xBA, 0x7E, 0xC1, 0x34, 0x58, 0x80, 0x01, 0xDF, 0x17, 0xA6, 0x16, 0x49, 0xE8, 0xB7, 0x17, 0x3E, 0x22, 0x2B, 0x81, 0xD4, 0xE0, 0xE8, 0xD0, 0x4D, 0x3C, 0x36, + 0xCF, 0x2F, 0xB0, 0xFC, 0x97, 0xDA, 0x64, 0x3B, 0xD5, 0x9D, 0xB5, 0x61, 0x15, 0xAF, 0xEE, 0xFC, 0x6C, 0x58, 0xBA, 0xBD, 0x2E, 0x56, 0xE0, 0x89, 0x76, 0xF4, + 0x17, 0x28, 0xF0, 0xD0, 0xF4, 0x00, 0x97, 0x6C, 0x5B, 0x0E, 0x91, 0x7B, 0xBD, 0x45, 0x52, 0xC8, 0x0C, 0xFA, 0x06, 0x17, 0x58, 0x01, 0x85, 0xAB, 0xD0, 0x05, + 0xE0, 0x5D, 0x67, 0x6A, 0xBF, 0x9C, 0x46, 0x73, 0x35, 0x4E, 0x81, 0x5C, 0xAE, 0xD6, 0x17, 0x94, 0x9B, 0xEF, 0xBD, 0x82, 0xFE, 0xEB, 0x26, 0x3F, 0xB7, 0xF7, + 0xCE, 0xCF, 0x36, 0x06, 0x20, 0x62, 0xE9, 0xA5, 0x2D, 0x0B, 0x61, 0x43, 0xBB, 0xBA, 0xB4, 0xF4, 0xBD, 0x5A, 0x15, 0xEB, 0xBD, 0xB4, 0x0E, 0xBA, 0xC3, 0x6E, + 0xEF, 0x61, 0x99, 0x15, 0x32, 0x54, 0xC1, 0xA8, 0xD4, 0x93, 0xC1, 0x03, 0x9A, 0xD2, 0xD8, 0xD3, 0x29, 0x5B, 0xD7, 0x2C, 0x67, 0x5A, 0x1C, 0xFC, 0x86, 0x3E, + 0xB4, 0xE7, 0x92, 0xFD, 0xC6, 0xAB, 0xA0, 0xF3, 0x82, 0xA5, 0x99, 0x88, 0x2E, 0x86, 0x0F, 0xCB, 0xB4, 0x38, 0x47, 0xB2, 0xD6, 0x25, 0xE0, 0xA8, 0xFF, 0x70, + 0x4C, 0xCB, 0xB3, 0x3D, 0xCD, 0x2C, 0x6D, 0x59, 0x0C, 0x1A, 0x0C, 0xEB, 0x23, 0x1E, 0x28, 0x57, 0xC0, 0xE7, 0x5E, 0x8D, 0xCB, 0xEF, 0xBF, 0x7C, 0xE0, 0x3A, + 0xEE, 0x6F, 0x49, 0x19, 0x15, 0x58, 0xFA, 0x75, 0x93, 0xA5, 0x4A, 0xA1, 0x6B, 0xB8, 0xA5, 0x45, 0xF2, 0xAD, 0x84, 0xAE, 0x95, 0x87, 0x57, 0x4B, 0x87, 0x2E, + 0x06, 0x8E, 0xA1, 0x8B, 0x1E, 0xED, 0xDF, 0xC4, 0x02, 0x0A, 0xCA, 0xDB, 0xD8, 0xE0, 0x64, 0x9B, 0x5B, 0x60, 0xB6, 0x11, 0xC1, 0x18, 0x4F, 0x95, 0x8C, 0x6C, + 0x5B, 0x7E, 0x53, 0xD9, 0xC8, 0x26, 0x9A, 0xF4, 0x2B, 0x9F, 0x28, 0xB2, 0x68, 0x36, 0xCF, 0x60, 0x61, 0x0E, 0xC7, 0x0E, 0xF6, 0x5A, 0xB1, 0xF1, 0x3B, 0xDF, + 0xFA, 0x42, 0x6D, 0xC0, 0xD5, 0x43, 0xAA, 0xCF, 0x8C, 0x0D, 0xCB, 0x2A, 0xAB, 0x26, 0x0E, 0x5B, 0x1B, 0xBD, 0x64, 0x07, 0xFB, 0x5D, 0x52, 0xE7, 0x9D, 0x6F, + 0x7F, 0x3D, 0xDD, 0xE7, 0x6A, 0xDF, 0x6A, 0x4A, 0x14, 0x31, 0x9C, 0xE0, 0x73, 0x04, 0x35, 0xBE, 0x47, 0x35, 0xFC, 0x3C, 0xC1, 0xC3, 0x29, 0x69, 0xCC, 0xB4, + 0x05, 0x3E, 0xCB, 0x58, 0xB4, 0xA8, 0xF1, 0x1A, 0xC1, 0x8A, 0xD5, 0x34, 0xE2, 0x3D, 0x3D, 0xEC, 0xAA, 0xC6, 0x28, 0xFE, 0x12, 0x3B, 0x20, 0xBC, 0x35, 0x36, + 0x34, 0x17, 0x9F, 0xFB, 0x85, 0x63, 0xE5, 0x25, 0x1C, 0x2B, 0xEF, 0xCD, 0x55, 0xF0, 0x16, 0x4E, 0x91, 0x43, 0x44, 0xF7, 0xB3, 0x85, 0x18, 0xD2, 0x1E, 0x1F, + 0xA0, 0xDB, 0xF8, 0xF8, 0xF3, 0x1E, 0x70, 0x8C, 0xBB, 0xD7, 0x06, 0xFD, 0xE3, 0x4E, 0x4D, 0x61, 0x59, 0x31, 0x7F, 0xCA, 0xD7, 0xFD, 0x42, 0xB7, 0xB5, 0xA9, + 0x01, 0x81, 0x22, 0x07, 0x88, 0xD2, 0x1B, 0x10, 0x48, 0xED, 0xB7, 0xCA, 0x6E, 0xB3, 0x4D, 0x89, 0xA8, 0xBE, 0x38, 0x3A, 0x42, 0x43, 0x88, 0xBD, 0x76, 0x8F, + 0xB5, 0x8F, 0x3F, 0xAF, 0xDC, 0x1D, 0x88, 0x5E, 0x87, 0x28, 0x16, 0x84, 0x2A, 0x14, 0x04, 0xEE, 0xEE, 0xDB, 0x2E, 0x4F, 0x5D, 0x9F, 0x27, 0x55, 0x8E, 0xA7, + 0x6E, 0x05, 0x9E, 0xBA, 0x7B, 0xE2, 0xA9, 0xE7, 0xF3, 0xD4, 0x95, 0xE3, 0xA9, 0x57, 0x81, 0xA7, 0xDE, 0x9E, 0x78, 0xEA, 0xFB, 0x3C, 0xF5, 0xE4, 0x78, 0xEA, + 0x57, 0xE0, 0xA9, 0xBF, 0x27, 0x9E, 0x06, 0x3E, 0x4F, 0x7D, 0x39, 0x9E, 0x06, 0x15, 0x78, 0x1A, 0xEC, 0x89, 0xA7, 0xA1, 0xCF, 0xD3, 0x40, 0x8E, 0xA7, 0x61, + 0x05, 0x9E, 0x86, 0x7B, 0xE2, 0xE9, 0xC8, 0xE7, 0x69, 0x28, 0xC7, 0xD3, 0x51, 0x05, 0x9E, 0x8E, 0xF6, 0xC4, 0xD3, 0xB1, 0xCF, 0xD3, 0x91, 0x1C, 0x4F, 0xC7, + 0x15, 0x78, 0x3A, 0xDE, 0x13, 0x4F, 0x27, 0x3E, 0x4F, 0xC7, 0x72, 0x3C, 0x9D, 0x54, 0xE0, 0xE9, 0x64, 0x4F, 0x3C, 0xE1, 0x6E, 0x2A, 0xC6, 0xD4, 0x89, 0xE4, + 0xA0, 0xDB, 0xA9, 0xC0, 0x95, 0xB6, 0x2F, 0xAE, 0x82, 0x54, 0x42, 0x95, 0xCD, 0x25, 0xAA, 0x24, 0x13, 0xE3, 0x7D, 0xB1, 0x15, 0x66, 0x13, 0x92, 0xE9, 0x84, + 0x5A, 0x25, 0x9F, 0x98, 0xEC, 0x8B, 0xAD, 0x20, 0xA1, 0x50, 0x25, 0x33, 0x0A, 0xB5, 0x4A, 0x4A, 0xA1, 0xEF, 0x8B, 0xAD, 0x20, 0xA7, 0x50, 0x25, 0x93, 0x0A, + 0xB5, 0x4A, 0x56, 0x41, 0xF6, 0xC5, 0x56, 0x90, 0x56, 0xA8, 0x92, 0x79, 0x85, 0x5A, 0x25, 0xB1, 0x98, 0xEE, 0x8B, 0xAD, 0x20, 0xB3, 0x50, 0x25, 0x53, 0x0B, + 0xB5, 0x42, 0x6E, 0x71, 0x22, 0x9E, 0x88, 0x6D, 0x95, 0x2D, 0xE2, 0xF1, 0x29, 0x72, 0x38, 0x69, 0x93, 0x7A, 0xE0, 0x88, 0x03, 0xE1, 0x13, 0x71, 0x4C, 0x20, + 0x17, 0xB6, 0x35, 0x35, 0x66, 0x41, 0x91, 0xE1, 0xC1, 0x3C, 0x1B, 0xE3, 0x46, 0xDE, 0xFF, 0x29, 0x5D, 0x68, 0xB8, 0x7A, 0x75, 0x59, 0xAC, 0xCC, 0x10, 0xED, + 0xE5, 0x2F, 0x54, 0x64, 0x00, 0xB2, 0xBB, 0xD1, 0x97, 0x91, 0x4B, 0xD5, 0x15, 0x28, 0x50, 0x91, 0x8A, 0xC2, 0x20, 0x5A, 0x51, 0x18, 0x4A, 0x57, 0x14, 0x18, + 0x71, 0xBB, 0xA9, 0x25, 0x00, 0xEE, 0x1E, 0x7B, 0x83, 0xBA, 0x3C, 0xD3, 0xBD, 0xF2, 0x4C, 0x0F, 0x8A, 0x30, 0xDD, 0x2B, 0xC3, 0x74, 0x89, 0x67, 0x5A, 0x25, + 0xE5, 0x04, 0xF4, 0x7E, 0x67, 0xDC, 0x10, 0x5D, 0xF9, 0x55, 0x5E, 0x54, 0x6A, 0x79, 0x51, 0x1D, 0x15, 0x11, 0x95, 0xBA, 0x43, 0xFB, 0x18, 0xF8, 0x7C, 0xFF, + 0x24, 0xCF, 0xF7, 0xA0, 0x3C, 0xDF, 0xBD, 0x22, 0x7C, 0x0F, 0x76, 0xC8, 0x77, 0xDF, 0xE7, 0xFB, 0x93, 0x3C, 0xDF, 0xFD, 0xF2, 0x7C, 0xF7, 0x8B, 0xF0, 0xDD, + 0xDF, 0x21, 0xDF, 0xF8, 0xB5, 0xDA, 0x9F, 0x3E, 0x29, 0x1F, 0xE7, 0x0E, 0x71, 0xE7, 0xF9, 0x95, 0x38, 0x06, 0x51, 0x76, 0x6C, 0x1F, 0xEC, 0x61, 0xEE, 0x86, + 0x14, 0xF6, 0xA2, 0x3C, 0xE5, 0xE6, 0xCD, 0x0C, 0x42, 0xE6, 0x13, 0x25, 0x62, 0x9E, 0xC4, 0x33, 0x37, 0x55, 0x96, 0xA9, 0xDD, 0xC5, 0xB0, 0xE3, 0xDA, 0xE8, + 0xCD, 0xAA, 0xC0, 0xF8, 0x76, 0x5C, 0xDE, 0x9E, 0xE5, 0x2B, 0xE6, 0x8C, 0xAE, 0x9D, 0xD9, 0xF3, 0x09, 0xE5, 0x19, 0xF2, 0x32, 0x57, 0x42, 0xED, 0xE5, 0xAB, + 0x10, 0x83, 0x3D, 0x54, 0xC9, 0x31, 0xD2, 0x1F, 0x31, 0x76, 0x7E, 0x42, 0x86, 0x14, 0xC8, 0x58, 0x0A, 0x0C, 0x46, 0x47, 0x05, 0xB5, 0x79, 0x5C, 0x32, 0x3A, + 0x21, 0x8D, 0x3B, 0x53, 0x27, 0x4E, 0x3D, 0x50, 0x00, 0x9F, 0x4A, 0x08, 0x60, 0x58, 0x5E, 0x00, 0x85, 0x32, 0x17, 0xA4, 0x71, 0x77, 0x02, 0xE8, 0x30, 0x01, + 0x5C, 0x85, 0xEF, 0xC0, 0xCD, 0x30, 0xE8, 0x0A, 0x15, 0xA8, 0xC1, 0x1E, 0xD6, 0x48, 0x30, 0xD2, 0xAA, 0xBE, 0x45, 0x03, 0x47, 0xC5, 0x14, 0xDA, 0x2D, 0x9A, + 0x5F, 0x89, 0x8B, 0x9F, 0x12, 0xF9, 0xF7, 0x2E, 0x13, 0xAC, 0x6E, 0xC7, 0xB7, 0xE8, 0xE2, 0x02, 0xE8, 0x94, 0x17, 0x80, 0x5A, 0x48, 0x00, 0x9D, 0x87, 0x95, + 0x8C, 0x0F, 0x37, 0x3F, 0x5B, 0x9A, 0x2F, 0xAD, 0xA2, 0xEE, 0x1F, 0x19, 0xCD, 0xBA, 0x45, 0x84, 0xB5, 0x53, 0xEF, 0xEF, 0x85, 0x9C, 0x2B, 0xBF, 0x2A, 0xF1, + 0xAD, 0xAF, 0x59, 0x71, 0xA0, 0x7C, 0x11, 0x70, 0xB0, 0x87, 0xF5, 0x2A, 0xA4, 0xF0, 0x44, 0xC0, 0x59, 0xC1, 0x00, 0x7F, 0x52, 0xDE, 0x1D, 0x0A, 0x69, 0x18, + 0x69, 0xDD, 0x9D, 0x8A, 0x07, 0x31, 0x41, 0xB0, 0x4F, 0x26, 0xCB, 0xA8, 0xB8, 0x7C, 0xE5, 0x70, 0xB0, 0x87, 0xA5, 0x2E, 0xA4, 0xF0, 0x58, 0xC0, 0x59, 0x41, + 0x15, 0x17, 0x4D, 0x49, 0x8F, 0x4B, 0x4E, 0x2D, 0xD5, 0x5D, 0xE6, 0xA4, 0x58, 0xED, 0x8E, 0x08, 0x22, 0xFA, 0x3E, 0xFB, 0x2C, 0x05, 0x97, 0xAF, 0x78, 0x0F, + 0x2A, 0xAE, 0xCF, 0xEE, 0x2E, 0x92, 0x1F, 0x89, 0x3E, 0x76, 0x9C, 0x6F, 0x07, 0x45, 0x73, 0xD9, 0x4E, 0xC9, 0x81, 0x6F, 0xA7, 0xA9, 0x2C, 0xF4, 0x0E, 0x59, + 0xCF, 0x26, 0xF7, 0x19, 0x26, 0x50, 0x7E, 0xE5, 0x6D, 0xB0, 0x87, 0xED, 0x21, 0x48, 0x61, 0xB7, 0x36, 0xFA, 0x54, 0x90, 0xA9, 0x2A, 0xF5, 0x83, 0xD2, 0xFB, + 0x43, 0xF6, 0x57, 0x7A, 0x9F, 0x2C, 0x6E, 0x8A, 0x97, 0xDE, 0x2F, 0xDE, 0xFD, 0x52, 0xAC, 0xF4, 0x1E, 0xED, 0x65, 0x7F, 0xA5, 0xF7, 0x72, 0x36, 0x53, 0x68, + 0xA3, 0x2C, 0x30, 0x86, 0xAF, 0x42, 0x9A, 0x18, 0x2E, 0xED, 0x12, 0x04, 0xA3, 0xBC, 0xF7, 0x4F, 0x03, 0x11, 0x45, 0x9E, 0x51, 0x8E, 0xB7, 0xCF, 0xB2, 0x9E, + 0x5E, 0x46, 0x58, 0x28, 0xF5, 0x0C, 0x2F, 0xBE, 0x50, 0x67, 0xC8, 0x3F, 0xF9, 0x53, 0xE1, 0x59, 0xE0, 0xB4, 0x77, 0x8D, 0xB4, 0x8F, 0x0A, 0xE2, 0xDE, 0xF9, + 0x2B, 0x06, 0x46, 0x09, 0x45, 0xA9, 0x54, 0x3F, 0x2A, 0x9E, 0x4B, 0xD7, 0xC9, 0x29, 0x58, 0x91, 0x68, 0xDE, 0x8B, 0x96, 0x5A, 0xE4, 0xA3, 0x39, 0x23, 0x6F, + 0x37, 0xD1, 0x1C, 0x71, 0xC7, 0x78, 0x2F, 0x90, 0xD5, 0x30, 0xD8, 0x62, 0x02, 0x10, 0x6F, 0xA2, 0x90, 0x10, 0x40, 0x9A, 0x04, 0xB6, 0x22, 0x82, 0x2E, 0x95, + 0x40, 0x37, 0xA1, 0xFD, 0x94, 0xC0, 0x4F, 0xDB, 0x97, 0x8D, 0xFB, 0xBD, 0x3D, 0xD4, 0x26, 0x50, 0x5C, 0x31, 0x8E, 0x0A, 0xEA, 0xB4, 0xD8, 0xE2, 0x60, 0x4C, + 0xA7, 0xC5, 0x8C, 0x7A, 0x67, 0xAB, 0x83, 0x80, 0xBC, 0x47, 0x05, 0xD0, 0x93, 0x56, 0x69, 0xF9, 0x69, 0x66, 0x6F, 0x0F, 0xF9, 0x09, 0x4A, 0x2B, 0xC6, 0x51, + 0x41, 0x95, 0x16, 0x5B, 0xFA, 0x8C, 0xA9, 0x54, 0x7E, 0x7E, 0xC9, 0x89, 0xDC, 0x99, 0x4A, 0xFB, 0x54, 0x00, 0x7D, 0x69, 0x95, 0x96, 0x9F, 0x75, 0xF4, 0xF6, + 0xB0, 0x7B, 0x17, 0xA5, 0x15, 0xE3, 0xA8, 0xA0, 0x4A, 0x8B, 0x2D, 0xD9, 0xC5, 0x54, 0x2A, 0x3F, 0x9F, 0xE4, 0x44, 0xEE, 0x4C, 0xA5, 0x03, 0x2A, 0x80, 0x81, + 0xB4, 0x4A, 0xCB, 0x57, 0x0A, 0x7A, 0x7B, 0x28, 0x06, 0xA1, 0xB4, 0x62, 0x1C, 0x15, 0x54, 0x69, 0xB1, 0xD5, 0xE7, 0x98, 0x4A, 0xE5, 0xD7, 0x39, 0x38, 0x91, + 0x3B, 0x53, 0xE9, 0x90, 0x0A, 0x60, 0x28, 0xAD, 0xD2, 0xF2, 0xFB, 0xAB, 0x7A, 0x7B, 0xD8, 0xBB, 0x8D, 0xD2, 0x8A, 0x71, 0x54, 0x50, 0xA5, 0xC5, 0x4A, 0xB7, + 0x31, 0x95, 0xCA, 0xAF, 0xDC, 0x70, 0x22, 0x77, 0xA6, 0xD2, 0x23, 0x2A, 0x80, 0x23, 0x69, 0x95, 0x96, 0xDF, 0xBA, 0xDE, 0xDB, 0x43, 0x3D, 0x0F, 0xA5, 0x15, + 0xE3, 0xA8, 0xA0, 0x4A, 0x8B, 0x55, 0x70, 0x62, 0x2A, 0x95, 0xDF, 0x3B, 0xC5, 0x89, 0xDC, 0x99, 0x4A, 0x8F, 0xA9, 0x00, 0x8E, 0xA5, 0x55, 0x5A, 0x7E, 0xE7, + 0x7E, 0x6F, 0x0F, 0x3B, 0xF7, 0x51, 0x5A, 0x31, 0x8E, 0x0A, 0xAA, 0xB4, 0x58, 0x6D, 0x36, 0xA6, 0x52, 0xF9, 0xED, 0x4E, 0x9C, 0xC8, 0x9D, 0xA9, 0xF4, 0x84, + 0x0A, 0xE0, 0x44, 0x5A, 0xA5, 0xE5, 0xB7, 0x0C, 0xF4, 0xF6, 0xB0, 0xF9, 0x05, 0xA5, 0xD5, 0x89, 0x72, 0x54, 0x50, 0xA5, 0xC5, 0x16, 0x18, 0x7B, 0x29, 0x5B, + 0x5F, 0x24, 0x54, 0x9A, 0xB6, 0xC0, 0xF8, 0x00, 0xEA, 0x77, 0xDA, 0x7A, 0x5C, 0xE2, 0x83, 0x3F, 0x2F, 0x7E, 0x7E, 0x99, 0x5E, 0xD8, 0x4F, 0xAD, 0xE2, 0xC5, + 0xFA, 0x7A, 0xE8, 0x65, 0xBC, 0xA8, 0xBC, 0x90, 0x70, 0x35, 0xF8, 0x14, 0xF9, 0x06, 0xF3, 0xD9, 0x96, 0xC6, 0x80, 0x0B, 0x58, 0x5A, 0xAF, 0xDF, 0x11, 0x27, + 0x2D, 0x39, 0x96, 0xC6, 0xA9, 0xDC, 0x4D, 0xF0, 0x40, 0xE4, 0x30, 0x17, 0x47, 0xDE, 0x3F, 0x48, 0xAD, 0xE9, 0x30, 0x80, 0x78, 0xF8, 0xE8, 0x77, 0x4E, 0x24, + 0xE3, 0x07, 0xC8, 0x20, 0x6D, 0x63, 0xFC, 0x16, 0x03, 0x08, 0xD2, 0xD8, 0x63, 0x4C, 0xBD, 0x96, 0x66, 0x2A, 0x59, 0x05, 0x28, 0xC4, 0x54, 0x5A, 0x65, 0x67, + 0xCB, 0x4C, 0xF5, 0x19, 0x53, 0x19, 0x4E, 0x9A, 0x60, 0x2A, 0x39, 0x0F, 0x2E, 0xC4, 0x54, 0xDA, 0x44, 0x38, 0x64, 0xEA, 0x21, 0x04, 0x3A, 0x32, 0xA1, 0xDF, + 0x28, 0x2F, 0x1C, 0xEA, 0x2E, 0x2F, 0x0E, 0x5F, 0xBC, 0xBE, 0x50, 0xE8, 0x92, 0xA6, 0x6D, 0x16, 0x8C, 0x78, 0xF1, 0x4E, 0xFF, 0x52, 0x31, 0x8F, 0x92, 0x1E, + 0x89, 0x7A, 0xE1, 0x87, 0xE4, 0xF3, 0x02, 0x1E, 0x87, 0x2C, 0x12, 0xF2, 0x06, 0x9D, 0x5E, 0x99, 0x0A, 0x61, 0x40, 0xE4, 0x8E, 0x82, 0x1E, 0x45, 0xDF, 0x0D, + 0x65, 0x70, 0x59, 0x4C, 0x06, 0x85, 0xAA, 0xA4, 0x71, 0x19, 0x14, 0x08, 0xFB, 0x3E, 0x91, 0xBB, 0x94, 0x01, 0x46, 0xC9, 0xCB, 0x0B, 0xE5, 0xFD, 0x3F, 0x94, + 0xCB, 0x9B, 0xA5, 0xED, 0xAE, 0x1C, 0x92, 0x1B, 0x55, 0x38, 0x5C, 0xE2, 0x4B, 0xF2, 0x83, 0x41, 0x4F, 0x36, 0xB0, 0x0C, 0xD2, 0x87, 0x80, 0x69, 0x67, 0x8B, + 0xF1, 0x92, 0x12, 0xDA, 0x0F, 0x18, 0xFC, 0x40, 0x40, 0xD3, 0x52, 0x71, 0x93, 0x03, 0xC6, 0x39, 0x54, 0x3B, 0xB8, 0xBD, 0x5A, 0x92, 0x41, 0x71, 0x46, 0xD9, + 0xDB, 0xEA, 0x70, 0x40, 0xA9, 0x1C, 0x04, 0xEC, 0x7D, 0xFA, 0x78, 0x25, 0xC7, 0x58, 0xB2, 0x8E, 0x56, 0x4C, 0x75, 0x69, 0x8F, 0x8C, 0x16, 0x1C, 0x14, 0xC4, + 0x8D, 0xCE, 0x0E, 0x21, 0xC6, 0x6E, 0xCA, 0x26, 0x45, 0x64, 0x67, 0x53, 0x63, 0x06, 0x06, 0x2B, 0x96, 0x25, 0x95, 0x21, 0x7B, 0xAB, 0x29, 0x7E, 0x12, 0xB4, + 0x35, 0x81, 0x30, 0x0F, 0xBA, 0x47, 0xEF, 0xF2, 0x25, 0xBB, 0xD0, 0x66, 0x24, 0xBC, 0xAE, 0xB0, 0x20, 0x9E, 0x15, 0x9C, 0x35, 0x86, 0x50, 0xBB, 0x26, 0xFC, + 0xFB, 0xA5, 0xCA, 0xDC, 0x21, 0xD3, 0xF3, 0xDA, 0x37, 0x01, 0x4E, 0xFE, 0xF8, 0x1D, 0x36, 0xA9, 0x29, 0xBA, 0xBD, 0xB6, 0x4C, 0x5B, 0xC3, 0xC0, 0xAF, 0x2D, + 0x3D, 0xA0, 0xB4, 0xFD, 0xE7, 0x12, 0xDF, 0x70, 0xA5, 0xE1, 0xD3, 0x5A, 0x5A, 0x46, 0x3F, 0x11, 0xF5, 0x4F, 0x4C, 0xDB, 0xF5, 0xA7, 0x6D, 0x78, 0x18, 0x7C, + 0xEF, 0xF4, 0x7F, 0xFE, 0x3B, 0x6F, 0xAB, 0x80, 0xB1, 0x98, 0x45, 0x04, 0x50, 0x53, 0x5C, 0x67, 0x72, 0x5E, 0x03, 0x4A, 0x1D, 0xDB, 0x75, 0x6D, 0xC7, 0x98, + 0x19, 0x29, 0x63, 0x73, 0x9A, 0xB4, 0x0F, 0x45, 0xE2, 0x4E, 0x34, 0x16, 0x0C, 0xFB, 0x67, 0xEE, 0xC4, 0x31, 0x96, 0xDE, 0xE8, 0x91, 0x6E, 0x4F, 0x56, 0x0B, + 0x62, 0x79, 0x6D, 0x4D, 0xD7, 0x2F, 0xAF, 0xE1, 0xE0, 0x2D, 0x7E, 0x8B, 0x0F, 0x24, 0xDF, 0xA8, 0xBF, 0xFA, 0xE7, 0x3B, 0x1C, 0x86, 0xF1, 0x1A, 0xC8, 0x8B, + 0xE8, 0xF5, 0x03, 0x65, 0xBA, 0xB2, 0xD8, 0x48, 0xD8, 0x20, 0xD8, 0xB6, 0xA9, 0x7C, 0x05, 0x8C, 0xD7, 0x9A, 0xA3, 0x8C, 0x35, 0x97, 0xBC, 0xB1, 0x5D, 0x4F, + 0x39, 0x57, 0x02, 0x8C, 0xA6, 0x3D, 0xA1, 0xFB, 0x36, 0xDA, 0x8C, 0x2F, 0xDE, 0x92, 0x31, 0xFE, 0x93, 0x63, 0x42, 0xD3, 0x00, 0xEA, 0xA9, 0x52, 0x3F, 0x3D, + 0x56, 0xEB, 0x68, 0x7F, 0x41, 0x17, 0x53, 0x02, 0x61, 0x1E, 0xDA, 0x35, 0x56, 0x8E, 0x79, 0xA0, 0x4C, 0xC6, 0xCD, 0xAF, 0x94, 0x7A, 0x7A, 0x19, 0xAF, 0x35, + 0x39, 0x33, 0x6D, 0x6F, 0x4E, 0xAC, 0x46, 0x48, 0x99, 0x43, 0xDC, 0xA5, 0x6D, 0xB9, 0x84, 0x11, 0xC7, 0x7E, 0xC6, 0x34, 0xBC, 0xDE, 0x76, 0x3D, 0xCD, 0x5B, + 0xB9, 0xCA, 0xE3, 0xF3, 0x73, 0xA5, 0xDB, 0xE9, 0x44, 0x9B, 0x29, 0xD0, 0x4D, 0xB2, 0xDD, 0x81, 0x92, 0xB8, 0xF0, 0x91, 0xDC, 0x78, 0xCD, 0x67, 0x01, 0xCC, + 0x9D, 0x42, 0x4C, 0x97, 0xC4, 0x90, 0x04, 0x00, 0xF8, 0xDE, 0xB8, 0x46, 0x33, 0x4E, 0x60, 0x43, 0xD7, 0x3C, 0xAD, 0xF9, 0x35, 0xA6, 0x2F, 0xE8, 0x15, 0x28, + 0x39, 0x50, 0xE8, 0xAD, 0x67, 0x91, 0x5B, 0x77, 0xCD, 0x36, 0xC8, 0x10, 0xF8, 0x0D, 0xA0, 0x89, 0xE3, 0xC4, 0x29, 0xA6, 0xD0, 0x2D, 0xF5, 0x40, 0xC1, 0x3B, + 0x71, 0xD8, 0x08, 0x91, 0x8F, 0xFC, 0x6B, 0xBE, 0xD0, 0xB2, 0xD1, 0x0A, 0x50, 0x32, 0x74, 0x77, 0x31, 0x15, 0x41, 0xC0, 0xF9, 0x40, 0x66, 0x20, 0xB1, 0xD9, + 0x01, 0x8F, 0x3F, 0x07, 0x34, 0xF8, 0x1C, 0xB0, 0xB8, 0x15, 0xD1, 0xDA, 0xE1, 0x21, 0xB8, 0xB4, 0x6B, 0x9B, 0x04, 0xAC, 0x62, 0xD6, 0xA8, 0xF3, 0x6F, 0xBD, + 0x82, 0x45, 0xD5, 0x3B, 0x37, 0xF5, 0xA7, 0x80, 0xA0, 0xED, 0xD9, 0x57, 0x9E, 0x63, 0x58, 0xB3, 0x86, 0x3A, 0x6C, 0x86, 0xD8, 0xE8, 0x6D, 0x44, 0x99, 0xB8, + 0x4F, 0xAF, 0xD3, 0x4E, 0x92, 0x37, 0x1A, 0xFC, 0xFA, 0xD3, 0x7A, 0xB3, 0xCE, 0x89, 0xA7, 0xE7, 0x60, 0x6E, 0x0D, 0x76, 0xF0, 0x84, 0xD2, 0xD8, 0x54, 0xCE, + 0xCE, 0x78, 0x37, 0xAC, 0x15, 0x5E, 0x84, 0x46, 0xF4, 0x4F, 0xE2, 0x56, 0x60, 0x8A, 0x7F, 0x7C, 0xFB, 0xD5, 0xB7, 0xD9, 0xBB, 0x43, 0xA0, 0xFA, 0x39, 0x86, + 0xE0, 0x6F, 0xBF, 0xC2, 0xFF, 0x77, 0x4F, 0x68, 0xD4, 0xFD, 0xF6, 0x2B, 0xFE, 0xB9, 0x7B, 0x02, 0x3D, 0xC1, 0x31, 0xED, 0xEF, 0xEE, 0x0F, 0x2A, 0x87, 0x4D, + 0xE9, 0xCD, 0x52, 0xA5, 0x17, 0x88, 0xAD, 0x30, 0x4D, 0xB3, 0x0C, 0xA2, 0xFE, 0x08, 0xFD, 0xB7, 0x31, 0xB1, 0x75, 0x50, 0x8F, 0x07, 0x96, 0xEC, 0x2B, 0xDD, + 0x04, 0x95, 0xF8, 0x82, 0xEA, 0xF8, 0x4A, 0x37, 0xA6, 0xB4, 0xA5, 0xC2, 0x5D, 0x25, 0x34, 0x10, 0xBF, 0xE5, 0x52, 0x73, 0x5C, 0xF2, 0xBD, 0xE5, 0x35, 0xBC, + 0x98, 0x53, 0xA4, 0x48, 0x7C, 0x34, 0x8A, 0xB1, 0x80, 0x3F, 0x80, 0x83, 0x76, 0x75, 0xAE, 0xB4, 0xC0, 0xD8, 0xF8, 0xDF, 0x84, 0xD9, 0xBC, 0x2E, 0x64, 0x36, + 0x0D, 0x2A, 0xB6, 0xA0, 0xCF, 0x66, 0x11, 0x13, 0x02, 0xB2, 0x22, 0x06, 0x44, 0x1D, 0x22, 0x14, 0x19, 0xBB, 0x98, 0xE2, 0x10, 0xBF, 0x4C, 0xCC, 0x2F, 0x8D, + 0x1B, 0xF8, 0x2F, 0x19, 0xB3, 0x36, 0x74, 0x85, 0x8D, 0x9E, 0xE3, 0x7F, 0xA0, 0x20, 0xFC, 0x93, 0x6A, 0x28, 0x80, 0xF5, 0xBD, 0x69, 0x36, 0xD8, 0x07, 0xE6, + 0xC0, 0x46, 0x56, 0x10, 0x0F, 0xDD, 0x5B, 0x8C, 0x4C, 0xB6, 0xED, 0x7D, 0x3E, 0x50, 0x96, 0x0E, 0x10, 0x46, 0xBF, 0xA5, 0x02, 0xC7, 0x80, 0x88, 0x58, 0xEC, + 0x6F, 0x2E, 0x05, 0x4B, 0xD3, 0x7C, 0xCE, 0xB0, 0x02, 0x09, 0xEC, 0x00, 0x4C, 0x66, 0x85, 0xA6, 0x0B, 0xFF, 0xDF, 0x3D, 0x81, 0x4E, 0xE0, 0x10, 0xFE, 0xBF, + 0x7B, 0x82, 0x5D, 0xA1, 0x51, 0x61, 0x8F, 0x77, 0x4F, 0xA0, 0x47, 0x38, 0x81, 0xFF, 0xA1, 0x0D, 0xF6, 0x8B, 0xAD, 0xF0, 0x2F, 0xDC, 0xA1, 0xFD, 0xE3, 0x4D, + 0x7A, 0xC0, 0x2E, 0xF0, 0xD3, 0x2C, 0x06, 0xD9, 0xDB, 0xF5, 0x1B, 0xF4, 0x6D, 0xE7, 0x9F, 0x6F, 0x80, 0x1D, 0x7A, 0x70, 0x0B, 0x31, 0xC8, 0xD2, 0xF1, 0x1C, + 0xFF, 0xDC, 0xFA, 0x0A, 0xC6, 0x0B, 0xFC, 0x08, 0xAE, 0xD1, 0x37, 0xC2, 0xE2, 0x25, 0x76, 0x80, 0xAD, 0xE8, 0xFB, 0x3B, 0x69, 0x2B, 0x76, 0x04, 0xD7, 0xF8, + 0x5B, 0x1F, 0x0F, 0x14, 0xFE, 0x5E, 0xC1, 0x5C, 0xE1, 0x84, 0xEF, 0xFD, 0x7B, 0xEE, 0xDE, 0x20, 0x83, 0x8C, 0x34, 0x94, 0x4A, 0x70, 0x76, 0x7B, 0xF7, 0x84, + 0xE0, 0x3D, 0x4A, 0x24, 0x1C, 0xDF, 0xF2, 0x63, 0xB8, 0x0E, 0xF4, 0xE1, 0x1D, 0x9F, 0x60, 0x7A, 0xE1, 0x36, 0xBC, 0x00, 0x2D, 0x3C, 0xBC, 0xCF, 0x89, 0x87, + 0xB3, 0xDB, 0xE0, 0x0C, 0xA1, 0x29, 0x2C, 0x67, 0x03, 0x4E, 0x6F, 0xC3, 0x53, 0xB8, 0x8B, 0xBC, 0xA0, 0x02, 0x38, 0x4F, 0x77, 0x4F, 0x38, 0x4F, 0xA8, 0x45, + 0x76, 0x14, 0x17, 0x35, 0xFC, 0x8F, 0x7E, 0xE4, 0xF1, 0x80, 0xFD, 0xC9, 0xF7, 0x4E, 0x62, 0x36, 0x95, 0xF3, 0x11, 0x8F, 0xFB, 0x18, 0x00, 0xC0, 0xA3, 0xE0, + 0x3A, 0x31, 0xDB, 0x9A, 0x07, 0x0E, 0x01, 0x79, 0x13, 0x71, 0xDB, 0x18, 0x51, 0x02, 0x37, 0xDF, 0xB8, 0xD5, 0xB6, 0xC0, 0x2D, 0x28, 0xC2, 0xE6, 0x29, 0x0F, + 0x1B, 0x88, 0x88, 0x71, 0xB9, 0x81, 0x8B, 0x5D, 0x4E, 0x43, 0xC7, 0xEE, 0xA6, 0x60, 0xE4, 0xA1, 0x30, 0x0E, 0x81, 0x17, 0xD3, 0xB0, 0xD1, 0x51, 0x23, 0x82, + 0xAB, 0x3B, 0x18, 0x84, 0xD8, 0x12, 0x91, 0x8E, 0xCD, 0x29, 0x51, 0x20, 0x6D, 0xCC, 0xCF, 0xC3, 0xA1, 0x70, 0x02, 0xE6, 0xA0, 0xD4, 0xFD, 0x09, 0x65, 0xFD, + 0x74, 0x23, 0xC2, 0x01, 0x04, 0x2F, 0x20, 0x28, 0xCF, 0x19, 0x8D, 0xA7, 0x61, 0xF8, 0x54, 0x94, 0x31, 0x24, 0x3B, 0x5F, 0x9E, 0xC5, 0x90, 0xD1, 0xD4, 0x3F, + 0xC0, 0xC4, 0xAE, 0x61, 0xA2, 0x90, 0xB8, 0xC4, 0xB6, 0xDB, 0xB5, 0x6C, 0x8B, 0x88, 0x7B, 0x8D, 0xC5, 0x4B, 0xDE, 0x11, 0x3F, 0xD3, 0xC9, 0x54, 0x5B, 0x99, + 0x5E, 0x08, 0xE6, 0x10, 0x48, 0x74, 0x2D, 0x1E, 0xB6, 0x58, 0x92, 0x9F, 0x3B, 0x74, 0x67, 0x0C, 0x15, 0xFE, 0xA8, 0xF0, 0x38, 0x39, 0x2A, 0x80, 0x55, 0x3A, + 0x5E, 0xA3, 0x7E, 0xE9, 0x38, 0xB6, 0xF3, 0x5B, 0xFD, 0x29, 0x36, 0x7A, 0x5A, 0xFF, 0xFD, 0x54, 0xA1, 0xF1, 0xB4, 0x19, 0x0F, 0xEE, 0x91, 0xF0, 0x79, 0x78, + 0xA8, 0xBC, 0xF0, 0x3C, 0x0D, 0x14, 0x80, 0x35, 0x96, 0x39, 0xCA, 0x47, 0xD1, 0x78, 0x12, 0x68, 0x3B, 0x68, 0x94, 0xEC, 0x7B, 0xF7, 0x20, 0x11, 0x4C, 0x2C, + 0x5D, 0x00, 0xF1, 0x93, 0x4C, 0x8A, 0xAA, 0xFD, 0xAF, 0x15, 0x71, 0x6E, 0xAF, 0xA8, 0xC0, 0x6C, 0xE7, 0x05, 0x84, 0xCA, 0x7A, 0x3B, 0x9C, 0x27, 0xD5, 0x59, + 0xCE, 0xD3, 0x06, 0x54, 0x97, 0xD0, 0x07, 0xE8, 0x38, 0xB4, 0x79, 0xC6, 0x4D, 0xA0, 0x77, 0x18, 0xE7, 0xCE, 0xB9, 0x32, 0x92, 0x49, 0x16, 0xB4, 0xB0, 0xAD, + 0x2F, 0xE4, 0x76, 0xB5, 0x04, 0xF1, 0x87, 0x69, 0x53, 0x22, 0x91, 0xE3, 0xD2, 0x21, 0x6D, 0x68, 0x79, 0xC1, 0x07, 0x4E, 0xB5, 0x27, 0x68, 0x14, 0xAA, 0x80, + 0x5A, 0x27, 0x7A, 0xE2, 0xB3, 0x8D, 0x46, 0x77, 0x8F, 0xC4, 0x67, 0x82, 0x94, 0x93, 0x13, 0xC8, 0x85, 0x07, 0xAE, 0x4D, 0x1D, 0x3B, 0xD1, 0x43, 0x22, 0x1D, + 0x84, 0x64, 0x30, 0x8C, 0x0C, 0xAB, 0x25, 0x24, 0x9F, 0x24, 0x1E, 0x1C, 0x02, 0x5B, 0xF0, 0x6F, 0x2E, 0x6C, 0x8F, 0x24, 0x22, 0x86, 0x61, 0x19, 0x9E, 0xA1, + 0x99, 0x9F, 0x42, 0x6B, 0xDC, 0xA9, 0xFB, 0x0B, 0x7C, 0xBC, 0x80, 0xFF, 0x6F, 0xE4, 0x7C, 0x72, 0x79, 0xCA, 0x86, 0x85, 0x04, 0xF1, 0x20, 0xB4, 0x92, 0xA8, + 0x1C, 0x62, 0x61, 0x81, 0xDF, 0xF7, 0x7B, 0x7A, 0xFC, 0x98, 0x1E, 0x3D, 0x0A, 0x94, 0xE6, 0x47, 0x8F, 0x73, 0x25, 0xBC, 0x91, 0x50, 0xF0, 0x26, 0xEE, 0x04, + 0x0E, 0x1F, 0x79, 0x04, 0x43, 0x22, 0xF0, 0x2F, 0x21, 0xBD, 0x41, 0x5B, 0xF8, 0xFF, 0xA8, 0xFF, 0x80, 0xA2, 0xFE, 0xEE, 0x42, 0x7C, 0x86, 0x6D, 0x27, 0x3C, + 0x80, 0xC1, 0x89, 0xF3, 0xE9, 0xA7, 0x90, 0x68, 0x8B, 0x93, 0xE4, 0x20, 0x74, 0x07, 0x93, 0x7D, 0x98, 0xCC, 0x5C, 0xB2, 0xF0, 0xFC, 0xF2, 0xF6, 0x7B, 0xBD, + 0x51, 0x0F, 0xDE, 0x68, 0x54, 0x6F, 0x62, 0x5C, 0x32, 0x8D, 0xC9, 0x97, 0x20, 0x2C, 0x85, 0x96, 0x07, 0x29, 0x0D, 0x66, 0xFF, 0x38, 0xB1, 0x36, 0x26, 0xDC, + 0x54, 0x5F, 0x7D, 0x78, 0xF1, 0xEE, 0xF3, 0x8B, 0x8F, 0x1F, 0x3F, 0x28, 0x2B, 0xB0, 0x59, 0x75, 0xF8, 0x19, 0xD3, 0x16, 0x98, 0x04, 0x38, 0x9F, 0x81, 0x3E, + 0xF7, 0x33, 0x45, 0xDA, 0xF9, 0xED, 0xF7, 0xDF, 0xBA, 0xBF, 0x03, 0xE8, 0xD7, 0xFF, 0xB2, 0xEA, 0x8C, 0x11, 0x44, 0xF5, 0x14, 0x70, 0xE1, 0xF1, 0xD7, 0xFA, + 0x53, 0xDF, 0xE0, 0x1B, 0xE9, 0x14, 0x06, 0xAF, 0xD7, 0xAD, 0x37, 0x81, 0xD5, 0xBB, 0x03, 0x40, 0xC5, 0xD2, 0x41, 0x18, 0x73, 0x1A, 0x58, 0xAA, 0x30, 0xA0, + 0x03, 0xF5, 0x19, 0xFC, 0x39, 0x53, 0xD4, 0x23, 0xF8, 0xFB, 0xF4, 0x69, 0x68, 0x22, 0x25, 0xBB, 0xAB, 0x3F, 0x35, 0x68, 0x67, 0x30, 0x3B, 0x69, 0x18, 0x67, + 0x20, 0xC9, 0xE7, 0xF5, 0x83, 0xFA, 0x69, 0xBD, 0x0E, 0xD7, 0xFC, 0xEE, 0xEF, 0x62, 0xEC, 0xDC, 0x3D, 0x0B, 0x38, 0x64, 0xA3, 0x2B, 0xDC, 0x08, 0xC5, 0x1F, + 0xCD, 0xEA, 0x5E, 0xB2, 0x2A, 0xD7, 0x79, 0xBA, 0x4E, 0xD8, 0xDB, 0xAC, 0x67, 0x74, 0x40, 0x8C, 0xC2, 0x64, 0x28, 0x88, 0x85, 0x86, 0xC0, 0xD7, 0x52, 0x51, + 0xD3, 0xE1, 0x56, 0xD7, 0x1D, 0xD0, 0x36, 0xB5, 0x96, 0xE6, 0x86, 0x0B, 0xCB, 0xE1, 0xC0, 0xC6, 0x02, 0x1C, 0x1B, 0xD3, 0xCD, 0x4C, 0x24, 0xB4, 0x75, 0x04, + 0xCB, 0x46, 0xC2, 0xD3, 0xB9, 0xBF, 0x5C, 0x87, 0x69, 0x6C, 0x26, 0xA9, 0xB1, 0x59, 0x44, 0x63, 0xB3, 0xED, 0x6A, 0x8C, 0xA3, 0xAE, 0xAC, 0x35, 0x1F, 0x4F, + 0x8E, 0xE6, 0x72, 0xE1, 0xB9, 0xD2, 0xB8, 0xB6, 0x66, 0x22, 0x6D, 0x95, 0x51, 0x13, 0x8B, 0x5D, 0x30, 0x29, 0x22, 0xCE, 0x9B, 0x8F, 0xEF, 0xDE, 0x62, 0xB4, + 0x11, 0xAB, 0x2C, 0xD0, 0x58, 0x32, 0xB9, 0x12, 0x60, 0xC0, 0xA0, 0x18, 0xAB, 0x7C, 0x24, 0xC2, 0xA6, 0x12, 0x56, 0x10, 0x72, 0x0C, 0x81, 0x17, 0x0C, 0xE4, + 0x7C, 0x17, 0x8B, 0x04, 0xBE, 0xF3, 0x86, 0x50, 0x19, 0xB6, 0x80, 0x00, 0x52, 0x4A, 0x64, 0x98, 0x37, 0x1C, 0x26, 0x52, 0xCB, 0xD8, 0xBB, 0x8B, 0x50, 0x7F, + 0x75, 0x65, 0x83, 0x9A, 0x3F, 0x55, 0x0F, 0x63, 0x9B, 0x9B, 0x2B, 0x1D, 0x3E, 0xA1, 0x97, 0x12, 0x10, 0xFF, 0xAA, 0x9C, 0xC0, 0xC0, 0x79, 0x21, 0xA0, 0x00, + 0x96, 0x5B, 0x01, 0x16, 0x5A, 0x40, 0x90, 0xC2, 0x41, 0xBF, 0x44, 0x96, 0x82, 0x41, 0x8E, 0x0A, 0xFA, 0xE9, 0x2F, 0x01, 0x06, 0xBF, 0x54, 0x21, 0x85, 0xC4, + 0xFF, 0x6C, 0x55, 0x3A, 0x1E, 0x39, 0x62, 0xFC, 0x8F, 0x45, 0x09, 0xF0, 0xF0, 0xD2, 0x88, 0x14, 0x1A, 0xFE, 0xA1, 0xA3, 0x54, 0x2C, 0x72, 0xC4, 0xF0, 0x6F, + 0x0B, 0x89, 0x78, 0xE2, 0xA5, 0x18, 0x39, 0x9E, 0xF8, 0x27, 0x71, 0xD2, 0xF1, 0x48, 0xCA, 0x86, 0x7F, 0x86, 0x46, 0x64, 0x75, 0xAC, 0xF2, 0x93, 0xE9, 0x18, + 0xAC, 0x09, 0x00, 0xF3, 0x54, 0xF5, 0xB9, 0x1A, 0xCD, 0xAC, 0x79, 0xA1, 0x28, 0x0B, 0x03, 0x6F, 0x92, 0xC4, 0xE0, 0x47, 0x87, 0x7B, 0x28, 0xD9, 0xDD, 0x47, + 0x14, 0x7A, 0x6F, 0x9A, 0x72, 0x51, 0x68, 0x69, 0x9A, 0x7E, 0xF8, 0x09, 0x60, 0x52, 0xC2, 0x0F, 0x5D, 0x32, 0xA3, 0xF5, 0xD6, 0x4C, 0xF9, 0xD3, 0x16, 0x0C, + 0xEF, 0xA6, 0x12, 0x11, 0xC7, 0x62, 0x65, 0x4A, 0x59, 0x12, 0xB4, 0xE3, 0x68, 0xA2, 0x86, 0x44, 0x97, 0xE3, 0x6E, 0x5D, 0xB9, 0xD0, 0x75, 0xEB, 0xA6, 0x60, + 0xA0, 0x25, 0x61, 0xB9, 0xDC, 0x0C, 0x5A, 0xA6, 0x20, 0x59, 0x3A, 0x72, 0xE9, 0x1D, 0xB4, 0x4B, 0x63, 0x04, 0xEB, 0xCF, 0x72, 0xAC, 0xF8, 0x5F, 0x1D, 0x17, + 0xD1, 0x41, 0xAB, 0xD6, 0x59, 0x4A, 0xE1, 0x5F, 0x77, 0x4E, 0xD3, 0xC8, 0x52, 0x76, 0xC8, 0xF5, 0xBF, 0x7F, 0x2C, 0x18, 0x76, 0x4B, 0x16, 0xFB, 0xF7, 0xEF, + 0x1C, 0xDA, 0x35, 0x91, 0x70, 0x8D, 0x60, 0x65, 0x9E, 0x67, 0x77, 0x21, 0x58, 0x86, 0x77, 0x4C, 0x34, 0xEB, 0x5A, 0x8B, 0x79, 0xC7, 0x04, 0xA6, 0xBF, 0x1E, + 0xE1, 0xA8, 0x1B, 0x35, 0xD6, 0xA0, 0xC6, 0x69, 0x64, 0x67, 0x6D, 0xBA, 0x65, 0x0D, 0x0B, 0x21, 0x06, 0x59, 0xB3, 0x93, 0xD8, 0xED, 0x39, 0xA1, 0xAF, 0x09, + 0xE1, 0xF7, 0xD9, 0x19, 0x6B, 0x10, 0xF4, 0x32, 0xB6, 0xF5, 0xDB, 0xB6, 0xB6, 0x5C, 0x42, 0xF0, 0xBA, 0x98, 0x1B, 0xA6, 0xDE, 0x60, 0xA0, 0x11, 0x13, 0xC1, + 0xBD, 0x09, 0x84, 0xAE, 0x5A, 0x71, 0xAC, 0xC0, 0xF0, 0x05, 0xBB, 0xD6, 0xA8, 0x77, 0x75, 0x7F, 0xCD, 0x88, 0x37, 0x6B, 0xEB, 0x8E, 0xB6, 0xFE, 0x1E, 0x37, + 0x35, 0x34, 0xB0, 0xD3, 0x83, 0xCE, 0x41, 0x87, 0x37, 0xF0, 0x9C, 0xDB, 0x20, 0xCB, 0x44, 0xBC, 0xB8, 0xF8, 0xFB, 0xD3, 0x87, 0xB7, 0x21, 0x5E, 0xCF, 0x7E, + 0xC5, 0x2E, 0x35, 0xEA, 0x74, 0x57, 0xC4, 0xE1, 0x9F, 0x4B, 0x9C, 0x0A, 0xF8, 0x4A, 0x89, 0x88, 0x11, 0x37, 0x3C, 0xA0, 0xA8, 0x58, 0xF3, 0x67, 0x51, 0xA4, + 0x70, 0xD9, 0x22, 0x6B, 0x05, 0x30, 0x91, 0x86, 0x08, 0xD4, 0xDF, 0x0E, 0x81, 0xE0, 0xC8, 0xC9, 0x77, 0x2B, 0xD3, 0xFC, 0x95, 0x68, 0x0E, 0xE8, 0xE3, 0xA9, + 0xD2, 0xA8, 0x75, 0x6A, 0x4F, 0x1B, 0xF4, 0xFA, 0x3B, 0x60, 0x67, 0xDE, 0x68, 0x3E, 0x55, 0x9B, 0xCD, 0xB6, 0x0B, 0x3A, 0x23, 0x8D, 0x56, 0xD7, 0x6F, 0x02, + 0x7F, 0x68, 0x1B, 0xD6, 0x49, 0xFA, 0xFD, 0x37, 0xF6, 0xCA, 0x71, 0xB3, 0x1A, 0xBC, 0x33, 0x2C, 0x2C, 0xE2, 0x64, 0x35, 0xB9, 0x22, 0x20, 0x58, 0x7D, 0xA3, + 0x49, 0x8D, 0xEE, 0xE2, 0xE0, 0xE5, 0x0F, 0x85, 0x2E, 0x6E, 0x2B, 0x8D, 0x68, 0x45, 0x87, 0x2F, 0x23, 0x12, 0x34, 0xF4, 0x86, 0xEF, 0xF1, 0x77, 0x51, 0xE3, + 0x00, 0x97, 0x05, 0x03, 0xF8, 0x01, 0x3C, 0xA0, 0xED, 0x90, 0x85, 0x7D, 0x4D, 0x36, 0xF4, 0x8F, 0xCD, 0x03, 0xE3, 0x9F, 0x1B, 0x3A, 0x2B, 0xD3, 0x84, 0x76, + 0x8B, 0x55, 0x21, 0xDC, 0x20, 0x82, 0x5B, 0x28, 0x70, 0x3F, 0x45, 0xA3, 0xCE, 0x76, 0xAF, 0xD0, 0x51, 0xE1, 0x2E, 0x74, 0x9B, 0xB9, 0xBD, 0xCE, 0x82, 0x64, + 0xBD, 0x27, 0x80, 0x03, 0x68, 0xDD, 0x70, 0xB5, 0xB1, 0x99, 0xDF, 0x35, 0x6F, 0xA7, 0xF3, 0xF2, 0x37, 0x34, 0xF0, 0xAF, 0x00, 0xA8, 0xE7, 0xD0, 0x3A, 0x61, + 0x04, 0x2D, 0xB1, 0xF2, 0xB0, 0xFA, 0x64, 0x65, 0x22, 0x9E, 0x6A, 0x30, 0x79, 0x8A, 0x63, 0x66, 0xC5, 0xE3, 0x02, 0x65, 0xE5, 0xE8, 0x65, 0x80, 0x88, 0x9F, + 0x82, 0x39, 0x83, 0x85, 0x2A, 0xCF, 0x29, 0x0B, 0xCA, 0x69, 0xEC, 0x6E, 0x90, 0xDB, 0x44, 0x0B, 0xA6, 0x8F, 0x58, 0xFC, 0xFB, 0x0B, 0x56, 0x70, 0x03, 0xCA, + 0x63, 0x12, 0x78, 0xF2, 0x24, 0x8E, 0x0D, 0xB7, 0xB1, 0xB0, 0x01, 0x24, 0xE8, 0x8D, 0xB5, 0x67, 0xAF, 0x0F, 0x0F, 0x57, 0x16, 0x38, 0x49, 0x30, 0x14, 0x3C, + 0x8E, 0x09, 0x3E, 0x32, 0x46, 0x00, 0x21, 0x86, 0x4E, 0x05, 0x84, 0xBB, 0xD2, 0x6A, 0x1B, 0xEB, 0xFD, 0xCF, 0xA9, 0xD5, 0x37, 0x08, 0xDF, 0x94, 0xD8, 0x04, + 0xF9, 0xA3, 0x31, 0x87, 0x17, 0xE2, 0x73, 0xE8, 0x04, 0xC6, 0x59, 0x0C, 0x23, 0x32, 0x96, 0xA0, 0x1B, 0x7F, 0x87, 0x87, 0x14, 0xE3, 0x4C, 0x33, 0xAC, 0x0B, + 0x62, 0x60, 0xFA, 0xD7, 0x8C, 0xDC, 0xA5, 0xDD, 0x03, 0x22, 0xDC, 0x30, 0x18, 0x59, 0xCE, 0xD8, 0x5C, 0x0F, 0x39, 0x3C, 0xA4, 0x4D, 0x53, 0xD0, 0xD0, 0x2E, + 0x36, 0xD1, 0x64, 0x52, 0xBF, 0x1E, 0x7F, 0x46, 0x6C, 0x22, 0xA1, 0x50, 0x74, 0xEB, 0x31, 0x8A, 0x83, 0xF6, 0x0A, 0x87, 0xF1, 0x81, 0xD3, 0x77, 0x88, 0x60, + 0xC1, 0x3B, 0xAA, 0x21, 0x56, 0x90, 0x0F, 0xA7, 0xA5, 0xA1, 0x09, 0x6C, 0xA9, 0x3E, 0xAD, 0x62, 0x71, 0x3A, 0x68, 0x12, 0xAD, 0x22, 0x57, 0x2C, 0x4D, 0x67, + 0xE0, 0x64, 0x5B, 0xEE, 0x92, 0x48, 0x57, 0xE3, 0x85, 0xE1, 0x09, 0x10, 0xD6, 0xD5, 0xBA, 0x10, 0x57, 0x4A, 0x95, 0x3B, 0xEA, 0x1F, 0x2C, 0xC6, 0xD0, 0x05, + 0x40, 0x40, 0x14, 0x5B, 0xC0, 0x9F, 0xB0, 0x3D, 0xE4, 0xCF, 0x61, 0x1C, 0xC4, 0x65, 0x79, 0xD4, 0x65, 0x62, 0x63, 0x0E, 0x43, 0xC1, 0x76, 0x94, 0x51, 0x14, + 0xF1, 0x3D, 0x65, 0xFE, 0x3E, 0xAE, 0xF8, 0x7A, 0x61, 0x74, 0x47, 0xCA, 0x1F, 0x0E, 0x01, 0x38, 0x17, 0x67, 0x99, 0xCA, 0xB7, 0x5F, 0x29, 0x8A, 0x3B, 0x65, + 0x0A, 0xFE, 0xE9, 0xCE, 0x89, 0x4E, 0x67, 0x44, 0xDE, 0xCA, 0x3D, 0x55, 0x70, 0x33, 0x4E, 0x6C, 0x0F, 0xD9, 0xDD, 0x1F, 0x81, 0x85, 0x04, 0xE1, 0x3D, 0x5C, + 0x05, 0xE5, 0x6C, 0x6F, 0xAC, 0x86, 0x3E, 0x92, 0x58, 0xF7, 0xE4, 0xB0, 0x52, 0x6B, 0x9F, 0xA2, 0x25, 0xC3, 0x64, 0xEC, 0xE0, 0x99, 0xDF, 0x23, 0x99, 0xBE, + 0xE9, 0x5E, 0x48, 0x99, 0x2E, 0x05, 0x39, 0x1F, 0xFE, 0x58, 0x70, 0x31, 0x23, 0x23, 0x71, 0xC2, 0x8F, 0x9A, 0x5C, 0x4E, 0x60, 0x22, 0xBA, 0x1F, 0x05, 0x99, + 0x11, 0xE1, 0x9A, 0x30, 0xD3, 0x63, 0xCC, 0x04, 0x98, 0xB4, 0xB9, 0xB0, 0xF3, 0x37, 0x0A, 0xF2, 0xC5, 0x90, 0x40, 0x59, 0x7F, 0xBA, 0x90, 0x4D, 0x07, 0x22, + 0x10, 0xE1, 0xC0, 0x0E, 0x22, 0x08, 0x62, 0x22, 0x2A, 0xA4, 0xA2, 0x0C, 0x99, 0xB1, 0x5F, 0x64, 0x18, 0xA5, 0x63, 0x28, 0xED, 0xF9, 0x37, 0x6A, 0xD5, 0xBF, + 0x1F, 0xB0, 0x71, 0x37, 0x12, 0xCA, 0x9A, 0x45, 0x08, 0xDA, 0x58, 0x2B, 0xCF, 0x25, 0x66, 0x6B, 0xAB, 0x86, 0xFE, 0x0F, 0x82, 0x2D, 0xC5, 0x77, 0xAE, 0x74, + 0x36, 0x56, 0xCB, 0xA3, 0x4B, 0x5B, 0x01, 0x83, 0x02, 0xD9, 0x44, 0x96, 0xBA, 0x7D, 0xF1, 0x08, 0x97, 0xB5, 0x52, 0xC5, 0xC5, 0xEC, 0x8B, 0x45, 0x14, 0x4C, + 0xD6, 0x33, 0x67, 0x35, 0x74, 0x1B, 0x2B, 0x95, 0x57, 0x08, 0x70, 0x11, 0x6C, 0x5B, 0xCE, 0x85, 0x0C, 0xB7, 0x38, 0x47, 0x70, 0xD0, 0x59, 0x92, 0xDC, 0xAA, + 0x00, 0x9F, 0x50, 0x45, 0x40, 0x11, 0x6B, 0x3E, 0xAC, 0xFF, 0xEC, 0x4A, 0x92, 0x7C, 0xEA, 0xBB, 0xF9, 0xE0, 0xD1, 0xED, 0xCE, 0xF5, 0x88, 0xB8, 0x5C, 0xCF, + 0x5E, 0x5E, 0xD1, 0xAB, 0x09, 0xBF, 0x5E, 0xD3, 0x22, 0x51, 0x1B, 0xEF, 0xFB, 0xF3, 0x8F, 0x28, 0xA9, 0xF1, 0xBA, 0xFA, 0x15, 0x56, 0x90, 0x14, 0x86, 0xA7, + 0x9E, 0x98, 0x6B, 0xE2, 0x2D, 0x61, 0x0F, 0x74, 0x2E, 0xE7, 0x3A, 0x13, 0x16, 0xFE, 0x83, 0xED, 0xC5, 0xE8, 0xFC, 0x78, 0xF8, 0x07, 0xEB, 0x13, 0x87, 0xE8, + 0x98, 0x8A, 0x9A, 0xB9, 0xB4, 0xD8, 0xCB, 0x24, 0x29, 0x61, 0x88, 0x66, 0xEE, 0xE2, 0x62, 0xDC, 0x67, 0x43, 0x1D, 0x06, 0xA0, 0x88, 0xFA, 0x52, 0xA2, 0x5C, + 0x28, 0x26, 0x1E, 0x56, 0xE2, 0xC4, 0x47, 0xC6, 0x2E, 0xB6, 0x37, 0xFD, 0xF9, 0xE7, 0xC9, 0x18, 0x86, 0x2B, 0x9C, 0x58, 0x81, 0xF3, 0xAC, 0x1B, 0xCD, 0xBB, + 0x2C, 0x76, 0x98, 0xB8, 0x42, 0x45, 0xCA, 0x12, 0x41, 0xE3, 0xAE, 0x18, 0x5B, 0x4C, 0x3E, 0x62, 0x74, 0x51, 0xFB, 0xBB, 0xB4, 0xFC, 0x89, 0x40, 0x9A, 0x60, + 0xCF, 0x37, 0x45, 0xCB, 0x72, 0xC1, 0x18, 0x82, 0x30, 0xA2, 0x6E, 0x10, 0x9B, 0x48, 0xF6, 0x22, 0x76, 0xE1, 0x37, 0x88, 0x68, 0xEB, 0x62, 0x05, 0xF0, 0x0B, + 0x5F, 0x5B, 0xEC, 0x1A, 0x26, 0x7D, 0x81, 0x59, 0x41, 0x12, 0x98, 0x65, 0xEE, 0x70, 0x3B, 0xE2, 0x23, 0x3C, 0x63, 0xCC, 0x01, 0xA0, 0x69, 0x22, 0x7B, 0x36, + 0x84, 0xC2, 0xB2, 0x3D, 0xAC, 0x9E, 0x12, 0xC9, 0x45, 0x33, 0x9D, 0x1B, 0x9A, 0x4D, 0x58, 0xB3, 0x08, 0x12, 0xC0, 0xBB, 0x39, 0x56, 0x47, 0xA7, 0x4B, 0x7C, + 0xC4, 0x86, 0x76, 0xCD, 0x40, 0xA4, 0x08, 0xC4, 0xD3, 0xC0, 0x50, 0xA0, 0xE9, 0x09, 0xF6, 0x66, 0x72, 0x9D, 0x90, 0x75, 0x7A, 0x52, 0xBD, 0x99, 0x50, 0x47, + 0xB5, 0xE0, 0x3F, 0xC4, 0x14, 0x0A, 0x92, 0x64, 0x4B, 0x9D, 0x44, 0xA5, 0xEE, 0x4F, 0x2E, 0x72, 0x20, 0x3E, 0xD3, 0x3C, 0x20, 0x2A, 0x32, 0x22, 0x29, 0x32, + 0xC2, 0x45, 0x86, 0x00, 0x61, 0xD6, 0x9C, 0x3F, 0xD3, 0x09, 0x42, 0xC2, 0xCF, 0x2F, 0x43, 0xCE, 0xD6, 0xE3, 0x4C, 0x3A, 0xF9, 0x2C, 0x22, 0xC2, 0x5E, 0x36, + 0x00, 0xB4, 0x5F, 0xC0, 0xA0, 0x19, 0x65, 0x6B, 0x3D, 0x96, 0x63, 0xCB, 0x9F, 0x85, 0x20, 0x40, 0xC8, 0x96, 0x78, 0xAE, 0xE2, 0xB3, 0xF2, 0x8A, 0x78, 0xFC, + 0x51, 0x47, 0xCD, 0xD2, 0x95, 0xA9, 0xA3, 0x2D, 0x08, 0x7E, 0xD9, 0x3E, 0x20, 0x36, 0xB8, 0x92, 0x45, 0x73, 0xD0, 0x88, 0x0D, 0x10, 0xC1, 0xA9, 0x14, 0xD9, + 0x41, 0xEB, 0xD0, 0x8C, 0x43, 0x04, 0x6C, 0xBE, 0x30, 0x52, 0x06, 0xC9, 0xB9, 0x2E, 0x1B, 0xF7, 0x75, 0x4A, 0x7D, 0x62, 0x74, 0x8F, 0x36, 0x70, 0xC8, 0xC4, + 0x9E, 0x59, 0x80, 0x29, 0xD6, 0x86, 0x99, 0x2A, 0x1B, 0xFC, 0xD9, 0xEF, 0xEC, 0xD0, 0x7F, 0x3C, 0x86, 0x9D, 0x61, 0x51, 0x70, 0xF4, 0xE8, 0xEC, 0x70, 0xEE, + 0x2D, 0xCC, 0xD1, 0xA3, 0xFF, 0x05, 0xDF, 0x19, 0xEB, 0xA3, 0xC1, 0x08, 0x01, 0x00 + }; From efb3393aa1355f83d5ae29950d308c1838e7a1a3 Mon Sep 17 00:00:00 2001 From: "Limor \"Ladyada\" Fried" Date: Tue, 7 Jan 2025 05:05:19 -0500 Subject: [PATCH 19/60] Add two more Adafruit Boards (Sparkle Motion & Sparkle Motion mini) (#10809) * add sparkle motion board (w/esp32 mini module) * add mini version! --- boards.txt | 268 ++++++++++++++++++ .../pins_arduino.h | 42 +++ .../pins_arduino.h | 42 +++ 3 files changed, 352 insertions(+) create mode 100644 variants/adafruit_sparklemotion_esp32/pins_arduino.h create mode 100644 variants/adafruit_sparklemotionmini_esp32/pins_arduino.h diff --git a/boards.txt b/boards.txt index 26ec251ecfe..df8a4f3fe38 100644 --- a/boards.txt +++ b/boards.txt @@ -16900,6 +16900,274 @@ adafruit_qualia_s3_rgb666.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) adafruit_qualia_s3_rgb666.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR adafruit_qualia_s3_rgb666.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api_zczr -lesp_zb_cli_command -lzboss_stack.zczr -lzboss_port +############################################################## +# Adafruit Sparkle Motion w/ESP32 + +sparklemotion.name=Adafruit Sparkle Motion (ESP32) + +sparklemotion.bootloader.tool=esptool_py +sparklemotion.bootloader.tool.default=esptool_py + +sparklemotion.upload.tool=esptool_py +sparklemotion.upload.tool.default=esptool_py +sparklemotion.upload.tool.network=esp_ota + +sparklemotion.upload.maximum_size=1310720 +sparklemotion.upload.maximum_data_size=327680 +sparklemotion.upload.flags= +sparklemotion.upload.extra_flags= + +sparklemotion.serial.disableDTR=true +sparklemotion.serial.disableRTS=true + +sparklemotion.build.tarch=xtensa +sparklemotion.build.bootloader_addr=0x1000 +sparklemotion.build.target=esp32 +sparklemotion.build.mcu=esp32 +sparklemotion.build.core=esp32 +sparklemotion.build.variant=adafruit_sparklemotion_esp32 +sparklemotion.build.board=SPARKLEMOTION_ESP32 + +sparklemotion.build.f_cpu=240000000L +sparklemotion.build.flash_size=4MB +sparklemotion.build.flash_freq=80m +sparklemotion.build.flash_mode=dio +sparklemotion.build.boot=dio +sparklemotion.build.partitions=default +sparklemotion.build.defines= +sparklemotion.build.loop_core= +sparklemotion.build.event_core= + +sparklemotion.menu.LoopCore.1=Core 1 +sparklemotion.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +sparklemotion.menu.LoopCore.0=Core 0 +sparklemotion.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +sparklemotion.menu.EventsCore.1=Core 1 +sparklemotion.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +sparklemotion.menu.EventsCore.0=Core 0 +sparklemotion.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +sparklemotion.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +sparklemotion.menu.PartitionScheme.default.build.partitions=default +sparklemotion.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +sparklemotion.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +sparklemotion.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +sparklemotion.menu.PartitionScheme.minimal.build.partitions=minimal +sparklemotion.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +sparklemotion.menu.PartitionScheme.no_ota.build.partitions=no_ota +sparklemotion.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +sparklemotion.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +sparklemotion.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +sparklemotion.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +sparklemotion.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +sparklemotion.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +sparklemotion.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +sparklemotion.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +sparklemotion.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +sparklemotion.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +sparklemotion.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +sparklemotion.menu.PartitionScheme.huge_app.build.partitions=huge_app +sparklemotion.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +sparklemotion.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +sparklemotion.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +sparklemotion.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 + +sparklemotion.menu.CPUFreq.240=240MHz (WiFi/BT) +sparklemotion.menu.CPUFreq.240.build.f_cpu=240000000L +sparklemotion.menu.CPUFreq.160=160MHz (WiFi/BT) +sparklemotion.menu.CPUFreq.160.build.f_cpu=160000000L +sparklemotion.menu.CPUFreq.80=80MHz (WiFi/BT) +sparklemotion.menu.CPUFreq.80.build.f_cpu=80000000L +sparklemotion.menu.CPUFreq.40=40MHz +sparklemotion.menu.CPUFreq.40.build.f_cpu=40000000L +sparklemotion.menu.CPUFreq.20=20MHz +sparklemotion.menu.CPUFreq.20.build.f_cpu=20000000L +sparklemotion.menu.CPUFreq.10=10MHz +sparklemotion.menu.CPUFreq.10.build.f_cpu=10000000L + +sparklemotion.menu.FlashFreq.80=80MHz +sparklemotion.menu.FlashFreq.80.build.flash_freq=80m +sparklemotion.menu.FlashFreq.40=40MHz +sparklemotion.menu.FlashFreq.40.build.flash_freq=40m + +sparklemotion.menu.FlashSize.4M=4MB (32Mb) +sparklemotion.menu.FlashSize.4M.build.flash_size=4MB + +sparklemotion.menu.UploadSpeed.921600=921600 +sparklemotion.menu.UploadSpeed.921600.upload.speed=921600 +sparklemotion.menu.UploadSpeed.115200=115200 +sparklemotion.menu.UploadSpeed.115200.upload.speed=115200 +sparklemotion.menu.UploadSpeed.256000.windows=256000 +sparklemotion.menu.UploadSpeed.256000.upload.speed=256000 +sparklemotion.menu.UploadSpeed.230400.windows.upload.speed=256000 +sparklemotion.menu.UploadSpeed.230400=230400 +sparklemotion.menu.UploadSpeed.230400.upload.speed=230400 +sparklemotion.menu.UploadSpeed.460800.linux=460800 +sparklemotion.menu.UploadSpeed.460800.macosx=460800 +sparklemotion.menu.UploadSpeed.460800.upload.speed=460800 +sparklemotion.menu.UploadSpeed.512000.windows=512000 +sparklemotion.menu.UploadSpeed.512000.upload.speed=512000 + +sparklemotion.menu.DebugLevel.none=None +sparklemotion.menu.DebugLevel.none.build.code_debug=0 +sparklemotion.menu.DebugLevel.error=Error +sparklemotion.menu.DebugLevel.error.build.code_debug=1 +sparklemotion.menu.DebugLevel.warn=Warn +sparklemotion.menu.DebugLevel.warn.build.code_debug=2 +sparklemotion.menu.DebugLevel.info=Info +sparklemotion.menu.DebugLevel.info.build.code_debug=3 +sparklemotion.menu.DebugLevel.debug=Debug +sparklemotion.menu.DebugLevel.debug.build.code_debug=4 +sparklemotion.menu.DebugLevel.verbose=Verbose +sparklemotion.menu.DebugLevel.verbose.build.code_debug=5 + +sparklemotion.menu.EraseFlash.none=Disabled +sparklemotion.menu.EraseFlash.none.upload.erase_cmd= +sparklemotion.menu.EraseFlash.all=Enabled +sparklemotion.menu.EraseFlash.all.upload.erase_cmd=-e + +sparklemotion.menu.ZigbeeMode.default=Disabled +sparklemotion.menu.ZigbeeMode.default.build.zigbee_mode= +sparklemotion.menu.ZigbeeMode.default.build.zigbee_libs= +sparklemotion.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +sparklemotion.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +sparklemotion.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api_zczr -lesp_zb_cli_command -lzboss_stack.zczr -lzboss_port + +############################################################## +# Adafruit Sparkle Motion Mini w/ESP32 + +sparklemotionmini.name=Adafruit Sparkle Motion Mini (ESP32) + +sparklemotionmini.bootloader.tool=esptool_py +sparklemotionmini.bootloader.tool.default=esptool_py + +sparklemotionmini.upload.tool=esptool_py +sparklemotionmini.upload.tool.default=esptool_py +sparklemotionmini.upload.tool.network=esp_ota + +sparklemotionmini.upload.maximum_size=1310720 +sparklemotionmini.upload.maximum_data_size=327680 +sparklemotionmini.upload.flags= +sparklemotionmini.upload.extra_flags= + +sparklemotionmini.serial.disableDTR=true +sparklemotionmini.serial.disableRTS=true + +sparklemotionmini.build.tarch=xtensa +sparklemotionmini.build.bootloader_addr=0x1000 +sparklemotionmini.build.target=esp32 +sparklemotionmini.build.mcu=esp32 +sparklemotionmini.build.core=esp32 +sparklemotionmini.build.variant=adafruit_sparklemotionmini_esp32 +sparklemotionmini.build.board=SPARKLEMOTIONMINI_ESP32 + +sparklemotionmini.build.f_cpu=240000000L +sparklemotionmini.build.flash_size=4MB +sparklemotionmini.build.flash_freq=80m +sparklemotionmini.build.flash_mode=dio +sparklemotionmini.build.boot=dio +sparklemotionmini.build.partitions=default +sparklemotionmini.build.defines= +sparklemotionmini.build.loop_core= +sparklemotionmini.build.event_core= + +sparklemotionmini.menu.LoopCore.1=Core 1 +sparklemotionmini.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +sparklemotionmini.menu.LoopCore.0=Core 0 +sparklemotionmini.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +sparklemotionmini.menu.EventsCore.1=Core 1 +sparklemotionmini.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +sparklemotionmini.menu.EventsCore.0=Core 0 +sparklemotionmini.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +sparklemotionmini.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +sparklemotionmini.menu.PartitionScheme.default.build.partitions=default +sparklemotionmini.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +sparklemotionmini.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +sparklemotionmini.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +sparklemotionmini.menu.PartitionScheme.minimal.build.partitions=minimal +sparklemotionmini.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +sparklemotionmini.menu.PartitionScheme.no_ota.build.partitions=no_ota +sparklemotionmini.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +sparklemotionmini.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +sparklemotionmini.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +sparklemotionmini.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +sparklemotionmini.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +sparklemotionmini.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +sparklemotionmini.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +sparklemotionmini.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +sparklemotionmini.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +sparklemotionmini.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +sparklemotionmini.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +sparklemotionmini.menu.PartitionScheme.huge_app.build.partitions=huge_app +sparklemotionmini.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +sparklemotionmini.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +sparklemotionmini.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +sparklemotionmini.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 + +sparklemotionmini.menu.CPUFreq.240=240MHz (WiFi/BT) +sparklemotionmini.menu.CPUFreq.240.build.f_cpu=240000000L +sparklemotionmini.menu.CPUFreq.160=160MHz (WiFi/BT) +sparklemotionmini.menu.CPUFreq.160.build.f_cpu=160000000L +sparklemotionmini.menu.CPUFreq.80=80MHz (WiFi/BT) +sparklemotionmini.menu.CPUFreq.80.build.f_cpu=80000000L +sparklemotionmini.menu.CPUFreq.40=40MHz +sparklemotionmini.menu.CPUFreq.40.build.f_cpu=40000000L +sparklemotionmini.menu.CPUFreq.20=20MHz +sparklemotionmini.menu.CPUFreq.20.build.f_cpu=20000000L +sparklemotionmini.menu.CPUFreq.10=10MHz +sparklemotionmini.menu.CPUFreq.10.build.f_cpu=10000000L + +sparklemotionmini.menu.FlashFreq.80=80MHz +sparklemotionmini.menu.FlashFreq.80.build.flash_freq=80m +sparklemotionmini.menu.FlashFreq.40=40MHz +sparklemotionmini.menu.FlashFreq.40.build.flash_freq=40m + +sparklemotionmini.menu.FlashSize.4M=4MB (32Mb) +sparklemotionmini.menu.FlashSize.4M.build.flash_size=4MB + +sparklemotionmini.menu.UploadSpeed.921600=921600 +sparklemotionmini.menu.UploadSpeed.921600.upload.speed=921600 +sparklemotionmini.menu.UploadSpeed.115200=115200 +sparklemotionmini.menu.UploadSpeed.115200.upload.speed=115200 +sparklemotionmini.menu.UploadSpeed.256000.windows=256000 +sparklemotionmini.menu.UploadSpeed.256000.upload.speed=256000 +sparklemotionmini.menu.UploadSpeed.230400.windows.upload.speed=256000 +sparklemotionmini.menu.UploadSpeed.230400=230400 +sparklemotionmini.menu.UploadSpeed.230400.upload.speed=230400 +sparklemotionmini.menu.UploadSpeed.460800.linux=460800 +sparklemotionmini.menu.UploadSpeed.460800.macosx=460800 +sparklemotionmini.menu.UploadSpeed.460800.upload.speed=460800 +sparklemotionmini.menu.UploadSpeed.512000.windows=512000 +sparklemotionmini.menu.UploadSpeed.512000.upload.speed=512000 + +sparklemotionmini.menu.DebugLevel.none=None +sparklemotionmini.menu.DebugLevel.none.build.code_debug=0 +sparklemotionmini.menu.DebugLevel.error=Error +sparklemotionmini.menu.DebugLevel.error.build.code_debug=1 +sparklemotionmini.menu.DebugLevel.warn=Warn +sparklemotionmini.menu.DebugLevel.warn.build.code_debug=2 +sparklemotionmini.menu.DebugLevel.info=Info +sparklemotionmini.menu.DebugLevel.info.build.code_debug=3 +sparklemotionmini.menu.DebugLevel.debug=Debug +sparklemotionmini.menu.DebugLevel.debug.build.code_debug=4 +sparklemotionmini.menu.DebugLevel.verbose=Verbose +sparklemotionmini.menu.DebugLevel.verbose.build.code_debug=5 + +sparklemotionmini.menu.EraseFlash.none=Disabled +sparklemotionmini.menu.EraseFlash.none.upload.erase_cmd= +sparklemotionmini.menu.EraseFlash.all=Enabled +sparklemotionmini.menu.EraseFlash.all.upload.erase_cmd=-e + +sparklemotionmini.menu.ZigbeeMode.default=Disabled +sparklemotionmini.menu.ZigbeeMode.default.build.zigbee_mode= +sparklemotionmini.menu.ZigbeeMode.default.build.zigbee_libs= +sparklemotionmini.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +sparklemotionmini.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +sparklemotionmini.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api_zczr -lesp_zb_cli_command -lzboss_stack.zczr -lzboss_port + ############################################################## nodemcu-32s.name=NodeMCU-32S diff --git a/variants/adafruit_sparklemotion_esp32/pins_arduino.h b/variants/adafruit_sparklemotion_esp32/pins_arduino.h new file mode 100644 index 00000000000..99043a9758c --- /dev/null +++ b/variants/adafruit_sparklemotion_esp32/pins_arduino.h @@ -0,0 +1,42 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +// User LED +static const uint8_t LED_BUILTIN = 4; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN + +// Neopixel +static const uint8_t PIN_NEOPIXEL = 2; +// RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API rgbLedWrite() and digitalWrite() for blinking +#define RGB_BUILTIN (PIN_NEOPIXEL + SOC_GPIO_PIN_COUNT) +#define RGB_BRIGHTNESS 64 + +static const uint8_t TX = 9; +static const uint8_t RX = 10; + +#define TX1 TX +#define RX1 RX + +static const uint8_t SDA = 14; +static const uint8_t SCL = 13; + +static const uint8_t SS = 23; +static const uint8_t MOSI = 9; +static const uint8_t MISO = 10; +static const uint8_t SCK = 18; + +static const uint8_t A0 = 27; + +// internal switch +static const uint8_t BUTTON = 0; + +static const uint8_t T0 = 27; + +static const uint8_t DAC1 = 25; +static const uint8_t DAC2 = 26; + +#endif /* Pins_Arduino_h */ diff --git a/variants/adafruit_sparklemotionmini_esp32/pins_arduino.h b/variants/adafruit_sparklemotionmini_esp32/pins_arduino.h new file mode 100644 index 00000000000..2af307de209 --- /dev/null +++ b/variants/adafruit_sparklemotionmini_esp32/pins_arduino.h @@ -0,0 +1,42 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +// User LED +static const uint8_t LED_BUILTIN = 12; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN + +// Neopixel +static const uint8_t PIN_NEOPIXEL = 18; +// RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API rgbLedWrite() and digitalWrite() for blinking +#define RGB_BUILTIN (PIN_NEOPIXEL + SOC_GPIO_PIN_COUNT) +#define RGB_BRIGHTNESS 64 + +static const uint8_t TX = 25; +static const uint8_t RX = 26; + +#define TX1 TX +#define RX1 RX + +static const uint8_t SDA = 19; +static const uint8_t SCL = 22; + +static const uint8_t SS = 14; +static const uint8_t MOSI = 25; +static const uint8_t MISO = 26; +static const uint8_t SCK = 27; + +static const uint8_t A0 = 13; + +// internal switch +static const uint8_t BUTTON = 0; + +static const uint8_t T0 = 13; + +static const uint8_t DAC1 = 25; +static const uint8_t DAC2 = 26; + +#endif /* Pins_Arduino_h */ From 7cfe0adc871f2f1a221a86710d61d25730315bf5 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 7 Jan 2025 15:50:38 +0200 Subject: [PATCH 20/60] IDF release/v5.3 (#10816) * IDF release/v5.3 cfea4f7c * Update core version --- cores/esp32/esp_arduino_version.h | 2 +- libraries/ArduinoOTA/library.properties | 2 +- libraries/AsyncUDP/library.properties | 2 +- libraries/BLE/library.properties | 2 +- libraries/BluetoothSerial/library.properties | 2 +- libraries/DNSServer/library.properties | 2 +- libraries/EEPROM/library.properties | 2 +- libraries/ESP32/library.properties | 2 +- libraries/ESP_I2S/library.properties | 2 +- libraries/ESP_NOW/library.properties | 2 +- libraries/ESP_SR/library.properties | 2 +- libraries/ESPmDNS/library.properties | 2 +- libraries/Ethernet/library.properties | 2 +- libraries/FFat/library.properties | 2 +- libraries/FS/library.properties | 2 +- libraries/HTTPClient/library.properties | 2 +- libraries/HTTPUpdate/library.properties | 2 +- libraries/HTTPUpdateServer/library.properties | 2 +- libraries/Insights/library.properties | 2 +- libraries/LittleFS/library.properties | 2 +- libraries/Matter/library.properties | 2 +- libraries/NetBIOS/library.properties | 2 +- libraries/Network/library.properties | 2 +- .../NetworkClientSecure/library.properties | 2 +- libraries/OpenThread/library.properties | 2 +- libraries/PPP/library.properties | 2 +- libraries/Preferences/library.properties | 2 +- libraries/RainMaker/library.properties | 2 +- libraries/SD/library.properties | 2 +- libraries/SD_MMC/library.properties | 2 +- libraries/SPI/library.properties | 2 +- libraries/SPIFFS/library.properties | 2 +- libraries/SimpleBLE/library.properties | 2 +- libraries/TFLiteMicro/library.properties | 2 +- libraries/Ticker/library.properties | 2 +- libraries/USB/library.properties | 2 +- libraries/Update/library.properties | 2 +- libraries/WebServer/library.properties | 2 +- libraries/WiFi/library.properties | 2 +- libraries/WiFiProv/library.properties | 2 +- libraries/Wire/library.properties | 2 +- libraries/Zigbee/library.properties | 2 +- package.json | 2 +- package/package_esp32_index.template.json | 68 +++++++++---------- platform.txt | 2 +- 45 files changed, 78 insertions(+), 78 deletions(-) diff --git a/cores/esp32/esp_arduino_version.h b/cores/esp32/esp_arduino_version.h index c2921b95fb8..dece36df1b8 100644 --- a/cores/esp32/esp_arduino_version.h +++ b/cores/esp32/esp_arduino_version.h @@ -23,7 +23,7 @@ extern "C" { /** Minor version number (x.X.x) */ #define ESP_ARDUINO_VERSION_MINOR 1 /** Patch version number (x.x.X) */ -#define ESP_ARDUINO_VERSION_PATCH 0 +#define ESP_ARDUINO_VERSION_PATCH 1 /** * Macro to convert ARDUINO version number into an integer diff --git a/libraries/ArduinoOTA/library.properties b/libraries/ArduinoOTA/library.properties index 54ad6eafb21..524564ff699 100644 --- a/libraries/ArduinoOTA/library.properties +++ b/libraries/ArduinoOTA/library.properties @@ -1,5 +1,5 @@ name=ArduinoOTA -version=3.1.0 +version=3.1.1 author=Ivan Grokhotkov and Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables Over The Air upgrades, via wifi and espota.py UDP request/TCP download. diff --git a/libraries/AsyncUDP/library.properties b/libraries/AsyncUDP/library.properties index 24457a39b90..5b1afd68273 100644 --- a/libraries/AsyncUDP/library.properties +++ b/libraries/AsyncUDP/library.properties @@ -1,5 +1,5 @@ name=ESP32 Async UDP -version=3.1.0 +version=3.1.1 author=Me-No-Dev maintainer=Me-No-Dev sentence=Async UDP Library for ESP32 diff --git a/libraries/BLE/library.properties b/libraries/BLE/library.properties index 32ad36b5f44..afda29db686 100644 --- a/libraries/BLE/library.properties +++ b/libraries/BLE/library.properties @@ -1,5 +1,5 @@ name=BLE -version=3.1.0 +version=3.1.1 author=Neil Kolban maintainer=Dariusz Krempa sentence=BLE functions for ESP32 diff --git a/libraries/BluetoothSerial/library.properties b/libraries/BluetoothSerial/library.properties index 6db3a01bb19..cf0dab07aa7 100644 --- a/libraries/BluetoothSerial/library.properties +++ b/libraries/BluetoothSerial/library.properties @@ -1,5 +1,5 @@ name=BluetoothSerial -version=3.1.0 +version=3.1.1 author=Evandro Copercini maintainer=Evandro Copercini sentence=Simple UART to Classical Bluetooth bridge for ESP32 diff --git a/libraries/DNSServer/library.properties b/libraries/DNSServer/library.properties index 077d9237f21..8a6d1786625 100644 --- a/libraries/DNSServer/library.properties +++ b/libraries/DNSServer/library.properties @@ -1,5 +1,5 @@ name=DNSServer -version=3.1.0 +version=3.1.1 author=Kristijan Novoselić maintainer=Kristijan Novoselić, sentence=A simple DNS server for ESP32. diff --git a/libraries/EEPROM/library.properties b/libraries/EEPROM/library.properties index bf75d618898..d66116947c7 100644 --- a/libraries/EEPROM/library.properties +++ b/libraries/EEPROM/library.properties @@ -1,5 +1,5 @@ name=EEPROM -version=3.1.0 +version=3.1.1 author=Ivan Grokhotkov maintainer=Paolo Becchi sentence=Enables reading and writing data a sequential, addressable FLASH storage diff --git a/libraries/ESP32/library.properties b/libraries/ESP32/library.properties index 1bb1ed21750..160b392f39e 100644 --- a/libraries/ESP32/library.properties +++ b/libraries/ESP32/library.properties @@ -1,5 +1,5 @@ name=ESP32 -version=3.1.0 +version=3.1.1 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 sketches examples diff --git a/libraries/ESP_I2S/library.properties b/libraries/ESP_I2S/library.properties index 807f7daeb9d..3105266d708 100644 --- a/libraries/ESP_I2S/library.properties +++ b/libraries/ESP_I2S/library.properties @@ -1,5 +1,5 @@ name=ESP_I2S -version=3.1.0 +version=3.1.1 author=me-no-dev maintainer=me-no-dev sentence=Library for ESP I2S communication diff --git a/libraries/ESP_NOW/library.properties b/libraries/ESP_NOW/library.properties index 42876f47f95..ba52e080046 100644 --- a/libraries/ESP_NOW/library.properties +++ b/libraries/ESP_NOW/library.properties @@ -1,5 +1,5 @@ name=ESP_NOW -version=3.1.0 +version=3.1.1 author=me-no-dev maintainer=P-R-O-C-H-Y sentence=Library for ESP_NOW diff --git a/libraries/ESP_SR/library.properties b/libraries/ESP_SR/library.properties index a81cff2fe9d..b52c0552cbb 100644 --- a/libraries/ESP_SR/library.properties +++ b/libraries/ESP_SR/library.properties @@ -1,5 +1,5 @@ name=ESP_SR -version=3.1.0 +version=3.1.1 author=me-no-dev maintainer=me-no-dev sentence=Library for ESP Sound Recognition diff --git a/libraries/ESPmDNS/library.properties b/libraries/ESPmDNS/library.properties index 4f65114fd0b..fe06ddb6851 100644 --- a/libraries/ESPmDNS/library.properties +++ b/libraries/ESPmDNS/library.properties @@ -1,5 +1,5 @@ name=ESPmDNS -version=3.1.0 +version=3.1.1 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 mDNS Library diff --git a/libraries/Ethernet/library.properties b/libraries/Ethernet/library.properties index 70aa24ec4aa..e8075d15f11 100644 --- a/libraries/Ethernet/library.properties +++ b/libraries/Ethernet/library.properties @@ -1,5 +1,5 @@ name=Ethernet -version=3.1.0 +version=3.1.1 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables network connection (local and Internet) using the ESP32 Ethernet. diff --git a/libraries/FFat/library.properties b/libraries/FFat/library.properties index dae11e43aaf..e75ef4206a6 100644 --- a/libraries/FFat/library.properties +++ b/libraries/FFat/library.properties @@ -1,5 +1,5 @@ name=FFat -version=3.1.0 +version=3.1.1 author=Hristo Gochkov, Ivan Grokhtkov, Larry Bernstone maintainer=Hristo Gochkov sentence=ESP32 FAT on Flash File System diff --git a/libraries/FS/library.properties b/libraries/FS/library.properties index 009383ab0c8..b339f57356f 100644 --- a/libraries/FS/library.properties +++ b/libraries/FS/library.properties @@ -1,5 +1,5 @@ name=FS -version=3.1.0 +version=3.1.1 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 File System diff --git a/libraries/HTTPClient/library.properties b/libraries/HTTPClient/library.properties index 203b8929d30..df11cb033b8 100644 --- a/libraries/HTTPClient/library.properties +++ b/libraries/HTTPClient/library.properties @@ -1,5 +1,5 @@ name=HTTPClient -version=3.1.0 +version=3.1.1 author=Markus Sattler maintainer=Markus Sattler sentence=HTTP Client for ESP32 diff --git a/libraries/HTTPUpdate/library.properties b/libraries/HTTPUpdate/library.properties index ac903dd71cf..dc02780c8d2 100644 --- a/libraries/HTTPUpdate/library.properties +++ b/libraries/HTTPUpdate/library.properties @@ -1,5 +1,5 @@ name=HTTPUpdate -version=3.1.0 +version=3.1.1 author=Markus Sattler maintainer=Markus Sattler sentence=Http Update for ESP32 diff --git a/libraries/HTTPUpdateServer/library.properties b/libraries/HTTPUpdateServer/library.properties index b26bd2cad7b..6cdcc1b34ea 100644 --- a/libraries/HTTPUpdateServer/library.properties +++ b/libraries/HTTPUpdateServer/library.properties @@ -1,5 +1,5 @@ name=HTTPUpdateServer -version=3.1.0 +version=3.1.1 author=Hristo Kapanakov maintainer= sentence=Simple HTTP Update server based on the WebServer diff --git a/libraries/Insights/library.properties b/libraries/Insights/library.properties index af213a1e70d..39b1f80c876 100644 --- a/libraries/Insights/library.properties +++ b/libraries/Insights/library.properties @@ -1,5 +1,5 @@ name=ESP Insights -version=3.1.0 +version=3.1.1 author=Sanket Wadekar maintainer=Sanket Wadekar sentence=ESP Insights diff --git a/libraries/LittleFS/library.properties b/libraries/LittleFS/library.properties index 86c4c43eeca..9f665385dce 100644 --- a/libraries/LittleFS/library.properties +++ b/libraries/LittleFS/library.properties @@ -1,5 +1,5 @@ name=LittleFS -version=3.1.0 +version=3.1.1 author= maintainer= sentence=LittleFS for esp32 diff --git a/libraries/Matter/library.properties b/libraries/Matter/library.properties index e08c750fb59..14a7f48c6de 100644 --- a/libraries/Matter/library.properties +++ b/libraries/Matter/library.properties @@ -1,5 +1,5 @@ name=Matter -version=3.1.0 +version=3.1.1 author=Rodrigo Garcia | GitHub @SuGlider maintainer=Rodrigo Garcia sentence=Library for supporting Matter environment on ESP32. diff --git a/libraries/NetBIOS/library.properties b/libraries/NetBIOS/library.properties index 42555781f75..f28405b7f64 100644 --- a/libraries/NetBIOS/library.properties +++ b/libraries/NetBIOS/library.properties @@ -1,5 +1,5 @@ name=NetBIOS -version=3.1.0 +version=3.1.1 author=Pablo@xpablo.cz maintainer=Hristo Gochkov sentence=Enables NBNS (NetBIOS) name resolution. diff --git a/libraries/Network/library.properties b/libraries/Network/library.properties index 8a1567e68a9..d3a9f329306 100644 --- a/libraries/Network/library.properties +++ b/libraries/Network/library.properties @@ -1,5 +1,5 @@ name=Networking -version=3.1.0 +version=3.1.1 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=General network management library. diff --git a/libraries/NetworkClientSecure/library.properties b/libraries/NetworkClientSecure/library.properties index d810e852524..6d79dc61095 100644 --- a/libraries/NetworkClientSecure/library.properties +++ b/libraries/NetworkClientSecure/library.properties @@ -1,5 +1,5 @@ name=NetworkClientSecure -version=3.1.0 +version=3.1.1 author=Evandro Luis Copercini maintainer=Github Community sentence=Enables secure network connection (local and Internet) using the ESP32 built-in WiFi. diff --git a/libraries/OpenThread/library.properties b/libraries/OpenThread/library.properties index 19d37749a92..a50a803dfa6 100644 --- a/libraries/OpenThread/library.properties +++ b/libraries/OpenThread/library.properties @@ -1,5 +1,5 @@ name=OpenThread -version=3.1.0 +version=3.1.1 author=Rodrigo Garcia | GitHub @SuGlider maintainer=Rodrigo Garcia sentence=Library for OpenThread Network on ESP32. diff --git a/libraries/PPP/library.properties b/libraries/PPP/library.properties index e2b290cc893..fefd89d4904 100644 --- a/libraries/PPP/library.properties +++ b/libraries/PPP/library.properties @@ -1,5 +1,5 @@ name=PPP -version=3.1.0 +version=3.1.1 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables network connection using GSM Modem. diff --git a/libraries/Preferences/library.properties b/libraries/Preferences/library.properties index 90cd20d75c3..842ade94003 100644 --- a/libraries/Preferences/library.properties +++ b/libraries/Preferences/library.properties @@ -1,5 +1,5 @@ name=Preferences -version=3.1.0 +version=3.1.1 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Provides friendly access to ESP32's Non-Volatile Storage diff --git a/libraries/RainMaker/library.properties b/libraries/RainMaker/library.properties index 0c3e6e26697..2f49258e78d 100644 --- a/libraries/RainMaker/library.properties +++ b/libraries/RainMaker/library.properties @@ -1,5 +1,5 @@ name=ESP RainMaker -version=3.1.0 +version=3.1.1 author=Sweety Mhaiske maintainer=Hristo Gochkov sentence=ESP RainMaker Support diff --git a/libraries/SD/library.properties b/libraries/SD/library.properties index 3fd1a3f8a3e..117de77ca36 100644 --- a/libraries/SD/library.properties +++ b/libraries/SD/library.properties @@ -1,5 +1,5 @@ name=SD -version=3.1.0 +version=3.1.1 author=Arduino, SparkFun maintainer=Arduino sentence=Enables reading and writing on SD cards. For all Arduino boards. diff --git a/libraries/SD_MMC/library.properties b/libraries/SD_MMC/library.properties index 94586e8e1ec..72c12c61d5c 100644 --- a/libraries/SD_MMC/library.properties +++ b/libraries/SD_MMC/library.properties @@ -1,5 +1,5 @@ name=SD_MMC -version=3.1.0 +version=3.1.1 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 SDMMC File System diff --git a/libraries/SPI/library.properties b/libraries/SPI/library.properties index a7a7204db62..8c567f00d40 100644 --- a/libraries/SPI/library.properties +++ b/libraries/SPI/library.properties @@ -1,5 +1,5 @@ name=SPI -version=3.1.0 +version=3.1.1 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables the communication with devices that use the Serial Peripheral Interface (SPI) Bus. For all Arduino boards, BUT Arduino DUE. diff --git a/libraries/SPIFFS/library.properties b/libraries/SPIFFS/library.properties index 132ff5fb7f1..68cb55ca124 100644 --- a/libraries/SPIFFS/library.properties +++ b/libraries/SPIFFS/library.properties @@ -1,5 +1,5 @@ name=SPIFFS -version=3.1.0 +version=3.1.1 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 SPIFFS File System diff --git a/libraries/SimpleBLE/library.properties b/libraries/SimpleBLE/library.properties index ae28a21c0df..a5541dd9750 100644 --- a/libraries/SimpleBLE/library.properties +++ b/libraries/SimpleBLE/library.properties @@ -1,5 +1,5 @@ name=SimpleBLE -version=3.1.0 +version=3.1.1 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Provides really simple BLE advertizer with just on and off diff --git a/libraries/TFLiteMicro/library.properties b/libraries/TFLiteMicro/library.properties index cde7d21bec1..ef9b474611f 100644 --- a/libraries/TFLiteMicro/library.properties +++ b/libraries/TFLiteMicro/library.properties @@ -1,5 +1,5 @@ name=TFLite Micro -version=3.1.0 +version=3.1.1 author=Sanket Wadekar maintainer=Sanket Wadekar sentence=TensorFlow Lite for Microcontrollers diff --git a/libraries/Ticker/library.properties b/libraries/Ticker/library.properties index d6e9829c6ef..8d123806109 100644 --- a/libraries/Ticker/library.properties +++ b/libraries/Ticker/library.properties @@ -1,5 +1,5 @@ name=Ticker -version=3.1.0 +version=3.1.1 author=Bert Melis maintainer=Hristo Gochkov sentence=Allows to call functions with a given interval. diff --git a/libraries/USB/library.properties b/libraries/USB/library.properties index 752acf3a803..17d4e25cae2 100644 --- a/libraries/USB/library.properties +++ b/libraries/USB/library.properties @@ -1,5 +1,5 @@ name=USB -version=3.1.0 +version=3.1.1 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=ESP32S2 USB Library diff --git a/libraries/Update/library.properties b/libraries/Update/library.properties index 9d480986b1e..5af58e380e1 100644 --- a/libraries/Update/library.properties +++ b/libraries/Update/library.properties @@ -1,5 +1,5 @@ name=Update -version=3.1.0 +version=3.1.1 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=ESP32 Sketch Update Library diff --git a/libraries/WebServer/library.properties b/libraries/WebServer/library.properties index 89e35e4713d..c225d6ebd95 100644 --- a/libraries/WebServer/library.properties +++ b/libraries/WebServer/library.properties @@ -1,5 +1,5 @@ name=WebServer -version=3.1.0 +version=3.1.1 author=Ivan Grokhotkov maintainer=Ivan Grokhtkov sentence=Simple web server library diff --git a/libraries/WiFi/library.properties b/libraries/WiFi/library.properties index 925616ea561..a82ae45a2d2 100644 --- a/libraries/WiFi/library.properties +++ b/libraries/WiFi/library.properties @@ -1,5 +1,5 @@ name=WiFi -version=3.1.0 +version=3.1.1 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables network connection (local and Internet) using the ESP32 built-in WiFi. diff --git a/libraries/WiFiProv/library.properties b/libraries/WiFiProv/library.properties index 20e27fc5097..4cf8aa56b39 100644 --- a/libraries/WiFiProv/library.properties +++ b/libraries/WiFiProv/library.properties @@ -1,5 +1,5 @@ name=WiFiProv -version=3.1.0 +version=3.1.1 author=Switi Mhaiske maintainer=Hristo Gochkov sentence=Enables provisioning. diff --git a/libraries/Wire/library.properties b/libraries/Wire/library.properties index a2d79eee81b..04beb2053bb 100644 --- a/libraries/Wire/library.properties +++ b/libraries/Wire/library.properties @@ -1,5 +1,5 @@ name=Wire -version=3.1.0 +version=3.1.1 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Allows the communication between devices or sensors connected via Two Wire Interface Bus. For esp8266 boards. diff --git a/libraries/Zigbee/library.properties b/libraries/Zigbee/library.properties index 2b1f041fc94..87e0cd63e9f 100644 --- a/libraries/Zigbee/library.properties +++ b/libraries/Zigbee/library.properties @@ -1,5 +1,5 @@ name=Zigbee -version=3.1.0 +version=3.1.1 author=P-R-O-C-H-Y maintainer=Jan Procházka sentence=Enables zigbee connection with the ESP32 diff --git a/package.json b/package.json index e13a446bd98..742b1154d63 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "framework-arduinoespressif32", - "version": "3.1.0", + "version": "3.1.1", "description": "Arduino Wiring-based Framework for the Espressif ESP32, ESP32-P4, ESP32-S and ESP32-C series of SoCs", "keywords": [ "framework", diff --git a/package/package_esp32_index.template.json b/package/package_esp32_index.template.json index 310c28fad7d..ad031a4d155 100644 --- a/package/package_esp32_index.template.json +++ b/package/package_esp32_index.template.json @@ -42,7 +42,7 @@ { "packager": "esp32", "name": "esp32-arduino-libs", - "version": "idf-release_v5.3-083aad99-v2" + "version": "idf-release_v5.3-cfea4f7c-v1" }, { "packager": "esp32", @@ -95,63 +95,63 @@ "tools": [ { "name": "esp32-arduino-libs", - "version": "idf-release_v5.3-083aad99-v2", + "version": "idf-release_v5.3-cfea4f7c-v1", "systems": [ { "host": "i686-mingw32", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "checksum": "SHA-256:4c6cae2e34df7e85a149615a6c18169777faf9460f708fc51d93616a117973c2", - "size": "341175680" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", + "size": "341118284" }, { "host": "x86_64-mingw32", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "checksum": "SHA-256:4c6cae2e34df7e85a149615a6c18169777faf9460f708fc51d93616a117973c2", - "size": "341175680" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", + "size": "341118284" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "checksum": "SHA-256:4c6cae2e34df7e85a149615a6c18169777faf9460f708fc51d93616a117973c2", - "size": "341175680" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", + "size": "341118284" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "checksum": "SHA-256:4c6cae2e34df7e85a149615a6c18169777faf9460f708fc51d93616a117973c2", - "size": "341175680" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", + "size": "341118284" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "checksum": "SHA-256:4c6cae2e34df7e85a149615a6c18169777faf9460f708fc51d93616a117973c2", - "size": "341175680" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", + "size": "341118284" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "checksum": "SHA-256:4c6cae2e34df7e85a149615a6c18169777faf9460f708fc51d93616a117973c2", - "size": "341175680" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", + "size": "341118284" }, { "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "checksum": "SHA-256:4c6cae2e34df7e85a149615a6c18169777faf9460f708fc51d93616a117973c2", - "size": "341175680" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", + "size": "341118284" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-083aad99-v2.zip", - "checksum": "SHA-256:4c6cae2e34df7e85a149615a6c18169777faf9460f708fc51d93616a117973c2", - "size": "341175680" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", + "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", + "size": "341118284" } ] }, diff --git a/platform.txt b/platform.txt index 44353ab3bf4..db4ab69dfd5 100644 --- a/platform.txt +++ b/platform.txt @@ -1,5 +1,5 @@ name=ESP32 Arduino -version=3.1.0 +version=3.1.1 tools.esp32-arduino-libs.path={runtime.platform.path}/tools/esp32-arduino-libs tools.esp32-arduino-libs.path.windows={runtime.platform.path}\tools\esp32-arduino-libs From e305a483b05a1fa50fe24565b1755c7c886a8677 Mon Sep 17 00:00:00 2001 From: vortigont Date: Tue, 7 Jan 2025 22:51:14 +0900 Subject: [PATCH 21/60] feat(board): Add custom board Huidu HD-WF2/HD-WF4 - esp32-s3 HUB75 driver boards (#10779) * feat(board): Add custom boards Huidu HD-WF2/HD-WF4 - esp32-s3 HUB75 driver boards * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Me No Dev --- boards.txt | 306 +++++++++++++++++++++++++++ variants/huidu_hd_wf2/pins_arduino.h | 76 +++++++ variants/huidu_hd_wf4/pins_arduino.h | 106 ++++++++++ 3 files changed, 488 insertions(+) create mode 100644 variants/huidu_hd_wf2/pins_arduino.h create mode 100644 variants/huidu_hd_wf4/pins_arduino.h diff --git a/boards.txt b/boards.txt index df8a4f3fe38..6f9b0a2ddf6 100644 --- a/boards.txt +++ b/boards.txt @@ -48615,3 +48615,309 @@ yb_esp32s3_eth.menu.EraseFlash.all=Enabled yb_esp32s3_eth.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## +# Huidu HD-WF2 - esp32-s3 HUB75 driver board +# https://www.hdwell.com/Product/index46.html + +huidu_hd_wf2.name=Huidu HD-WF2 + +huidu_hd_wf2.bootloader.tool=esptool_py +huidu_hd_wf2.bootloader.tool.default=esptool_py + +huidu_hd_wf2.upload.tool=esptool_py +huidu_hd_wf2.upload.tool.default=esptool_py +huidu_hd_wf2.upload.tool.network=esp_ota + +huidu_hd_wf2.upload.maximum_size=1310720 +huidu_hd_wf2.upload.maximum_data_size=327680 +huidu_hd_wf2.upload.flags= +huidu_hd_wf2.upload.extra_flags= +huidu_hd_wf2.upload.use_1200bps_touch=true +huidu_hd_wf2.upload.wait_for_upload_port=true + +huidu_hd_wf2.serial.disableDTR=false +huidu_hd_wf2.serial.disableRTS=false + +huidu_hd_wf2.build.tarch=xtensa +huidu_hd_wf2.build.bootloader_addr=0x0 +huidu_hd_wf2.build.target=esp32s3 +huidu_hd_wf2.build.mcu=esp32s3 +huidu_hd_wf2.build.core=esp32 +huidu_hd_wf2.build.variant=huidu_hd_wf2 +huidu_hd_wf2.build.board=HUIDU_HD_WF2 + +huidu_hd_wf2.build.usb_mode=0 +huidu_hd_wf2.build.cdc_on_boot=1 +huidu_hd_wf2.build.msc_on_boot=0 +huidu_hd_wf2.build.dfu_on_boot=0 +huidu_hd_wf2.build.f_cpu=240000000L +huidu_hd_wf2.build.flash_size=8MB +huidu_hd_wf2.build.flash_freq=80m +huidu_hd_wf2.build.flash_mode=qio +huidu_hd_wf2.build.boot=qio +huidu_hd_wf2.build.partitions=default +huidu_hd_wf2.build.defines= +huidu_hd_wf2.build.loop_core= +huidu_hd_wf2.build.event_core= +huidu_hd_wf2.build.flash_type=qio +huidu_hd_wf2.build.psram_type=qspi +huidu_hd_wf2.build.memory_type={build.flash_type}_{build.psram_type} + +huidu_hd_wf2.menu.FlashSize.8M=8MB (64Mb) +huidu_hd_wf2.menu.FlashSize.8M.build.flash_size=8MB + +huidu_hd_wf2.menu.LoopCore.1=Core 1 +huidu_hd_wf2.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +huidu_hd_wf2.menu.LoopCore.0=Core 0 +huidu_hd_wf2.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +huidu_hd_wf2.menu.EventsCore.1=Core 1 +huidu_hd_wf2.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +huidu_hd_wf2.menu.EventsCore.0=Core 0 +huidu_hd_wf2.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +huidu_hd_wf2.menu.USBMode.hwcdc=Hardware CDC and JTAG +huidu_hd_wf2.menu.USBMode.hwcdc.build.usb_mode=1 +huidu_hd_wf2.menu.USBMode.default=USB-OTG (TinyUSB) +huidu_hd_wf2.menu.USBMode.default.build.usb_mode=0 + +huidu_hd_wf2.menu.CDCOnBoot.default=Enabled +huidu_hd_wf2.menu.CDCOnBoot.default.build.cdc_on_boot=1 +huidu_hd_wf2.menu.CDCOnBoot.cdc=Disabled +huidu_hd_wf2.menu.CDCOnBoot.cdc.build.cdc_on_boot=0 + +huidu_hd_wf2.menu.MSCOnBoot.default=Disabled +huidu_hd_wf2.menu.MSCOnBoot.default.build.msc_on_boot=0 +huidu_hd_wf2.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +huidu_hd_wf2.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +huidu_hd_wf2.menu.DFUOnBoot.default=Disabled +huidu_hd_wf2.menu.DFUOnBoot.default.build.dfu_on_boot=0 +huidu_hd_wf2.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +huidu_hd_wf2.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +huidu_hd_wf2.menu.UploadMode.default=UART0 / Hardware CDC +huidu_hd_wf2.menu.UploadMode.default.upload.use_1200bps_touch=false +huidu_hd_wf2.menu.UploadMode.default.upload.wait_for_upload_port=false +huidu_hd_wf2.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +huidu_hd_wf2.menu.UploadMode.cdc.upload.use_1200bps_touch=true +huidu_hd_wf2.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +huidu_hd_wf2.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) +huidu_hd_wf2.menu.PartitionScheme.default_8MB.build.partitions=default_8MB +huidu_hd_wf2.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 +huidu_hd_wf2.menu.PartitionScheme.large_spiffs_8MB=Large SPIFFS (1.3MB APP/5.5MiB SPIFFS) +huidu_hd_wf2.menu.PartitionScheme.large_spiffs_8MB.build.partitions=large_spiffs_8MB +huidu_hd_wf2.menu.PartitionScheme.large_spiffs_8MB.upload.maximum_size=1310720 +huidu_hd_wf2.menu.PartitionScheme.default_ffat_8MB=8MiB fatfs (3MB APP/1.5MB FATFS) +huidu_hd_wf2.menu.PartitionScheme.default_ffat_8MB.build.partitions=default_ffat_8MB +huidu_hd_wf2.menu.PartitionScheme.default_ffat_8MB.upload.maximum_size=3342336 + +huidu_hd_wf2.menu.CPUFreq.240=240MHz (WiFi) +huidu_hd_wf2.menu.CPUFreq.240.build.f_cpu=240000000L +huidu_hd_wf2.menu.CPUFreq.160=160MHz (WiFi) +huidu_hd_wf2.menu.CPUFreq.160.build.f_cpu=160000000L +huidu_hd_wf2.menu.CPUFreq.80=80MHz (WiFi) +huidu_hd_wf2.menu.CPUFreq.80.build.f_cpu=80000000L +huidu_hd_wf2.menu.CPUFreq.40=40MHz +huidu_hd_wf2.menu.CPUFreq.40.build.f_cpu=40000000L + +huidu_hd_wf2.menu.PSRAM.enabled=QSPI Flash fix +huidu_hd_wf2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +huidu_hd_wf2.menu.PSRAM.enabled.build.psram_type=qspi + +huidu_hd_wf2.menu.FlashMode.qio=QIO 80MHz +huidu_hd_wf2.menu.FlashMode.qio.build.flash_mode=dio +huidu_hd_wf2.menu.FlashMode.qio.build.boot=qio +huidu_hd_wf2.menu.FlashMode.qio.build.boot_freq=80m +huidu_hd_wf2.menu.FlashMode.qio.build.flash_freq=80m +huidu_hd_wf2.menu.FlashMode.qio120=QIO 120MHz +huidu_hd_wf2.menu.FlashMode.qio120.build.flash_mode=dio +huidu_hd_wf2.menu.FlashMode.qio120.build.boot=qio +huidu_hd_wf2.menu.FlashMode.qio120.build.boot_freq=120m +huidu_hd_wf2.menu.FlashMode.qio120.build.flash_freq=80m +huidu_hd_wf2.menu.FlashMode.dio=DIO 80MHz +huidu_hd_wf2.menu.FlashMode.dio.build.flash_mode=dio +huidu_hd_wf2.menu.FlashMode.dio.build.boot=dio +huidu_hd_wf2.menu.FlashMode.dio.build.boot_freq=80m +huidu_hd_wf2.menu.FlashMode.dio.build.flash_freq=80m + +huidu_hd_wf2.menu.UploadSpeed.921600=921600 +huidu_hd_wf2.menu.UploadSpeed.921600.upload.speed=921600 +huidu_hd_wf2.menu.UploadSpeed.115200=115200 +huidu_hd_wf2.menu.UploadSpeed.115200.upload.speed=115200 +huidu_hd_wf2.menu.UploadSpeed.460800.linux=460800 +huidu_hd_wf2.menu.UploadSpeed.460800.macosx=460800 +huidu_hd_wf2.menu.UploadSpeed.460800.upload.speed=460800 + +huidu_hd_wf2.menu.DebugLevel.none=None +huidu_hd_wf2.menu.DebugLevel.none.build.code_debug=0 +huidu_hd_wf2.menu.DebugLevel.error=Error +huidu_hd_wf2.menu.DebugLevel.error.build.code_debug=1 +huidu_hd_wf2.menu.DebugLevel.warn=Warn +huidu_hd_wf2.menu.DebugLevel.warn.build.code_debug=2 +huidu_hd_wf2.menu.DebugLevel.info=Info +huidu_hd_wf2.menu.DebugLevel.info.build.code_debug=3 +huidu_hd_wf2.menu.DebugLevel.debug=Debug +huidu_hd_wf2.menu.DebugLevel.debug.build.code_debug=4 +huidu_hd_wf2.menu.DebugLevel.verbose=Verbose +huidu_hd_wf2.menu.DebugLevel.verbose.build.code_debug=5 + +huidu_hd_wf2.menu.EraseFlash.none=Disabled +huidu_hd_wf2.menu.EraseFlash.none.upload.erase_cmd= +huidu_hd_wf2.menu.EraseFlash.all=Enabled +huidu_hd_wf2.menu.EraseFlash.all.upload.erase_cmd=-e + +############################################################## +# Huidu HD-WF4 - esp32-s3 HUB75 driver board +# https://www.hdwell.com/Product/index46.html + +huidu_hd_wf4.name=Huidu HD-WF4 + +huidu_hd_wf4.bootloader.tool=esptool_py +huidu_hd_wf4.bootloader.tool.default=esptool_py + +huidu_hd_wf4.upload.tool=esptool_py +huidu_hd_wf4.upload.tool.default=esptool_py +huidu_hd_wf4.upload.tool.network=esp_ota + +huidu_hd_wf4.upload.maximum_size=1310720 +huidu_hd_wf4.upload.maximum_data_size=327680 +huidu_hd_wf4.upload.flags= +huidu_hd_wf4.upload.extra_flags= +huidu_hd_wf4.upload.use_1200bps_touch=true +huidu_hd_wf4.upload.wait_for_upload_port=true + +huidu_hd_wf4.serial.disableDTR=false +huidu_hd_wf4.serial.disableRTS=false + +huidu_hd_wf4.build.tarch=xtensa +huidu_hd_wf4.build.bootloader_addr=0x0 +huidu_hd_wf4.build.target=esp32s3 +huidu_hd_wf4.build.mcu=esp32s3 +huidu_hd_wf4.build.core=esp32 +huidu_hd_wf4.build.variant=huidu_hd_wf4 +huidu_hd_wf4.build.board=HUIDU_HD_WF4 + +huidu_hd_wf4.build.usb_mode=0 +huidu_hd_wf4.build.cdc_on_boot=1 +huidu_hd_wf4.build.msc_on_boot=0 +huidu_hd_wf4.build.dfu_on_boot=0 +huidu_hd_wf4.build.f_cpu=240000000L +huidu_hd_wf4.build.flash_size=8MB +huidu_hd_wf4.build.flash_freq=80m +huidu_hd_wf4.build.flash_mode=qio +huidu_hd_wf4.build.boot=qio +huidu_hd_wf4.build.partitions=default +huidu_hd_wf4.build.defines= +huidu_hd_wf4.build.loop_core= +huidu_hd_wf4.build.event_core= +huidu_hd_wf4.build.flash_type=qio +huidu_hd_wf4.build.psram_type=qspi +huidu_hd_wf4.build.memory_type={build.flash_type}_{build.psram_type} + +huidu_hd_wf4.menu.FlashSize.8M=8MB (64Mb) +huidu_hd_wf4.menu.FlashSize.8M.build.flash_size=8MB + +huidu_hd_wf4.menu.LoopCore.1=Core 1 +huidu_hd_wf4.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +huidu_hd_wf4.menu.LoopCore.0=Core 0 +huidu_hd_wf4.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +huidu_hd_wf4.menu.EventsCore.1=Core 1 +huidu_hd_wf4.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +huidu_hd_wf4.menu.EventsCore.0=Core 0 +huidu_hd_wf4.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +huidu_hd_wf4.menu.USBMode.hwcdc=Hardware CDC and JTAG +huidu_hd_wf4.menu.USBMode.hwcdc.build.usb_mode=1 +huidu_hd_wf4.menu.USBMode.default=USB-OTG (TinyUSB) +huidu_hd_wf4.menu.USBMode.default.build.usb_mode=0 + +huidu_hd_wf4.menu.CDCOnBoot.default=Enabled +huidu_hd_wf4.menu.CDCOnBoot.default.build.cdc_on_boot=1 +huidu_hd_wf4.menu.CDCOnBoot.cdc=Disabled +huidu_hd_wf4.menu.CDCOnBoot.cdc.build.cdc_on_boot=0 + +huidu_hd_wf4.menu.MSCOnBoot.default=Disabled +huidu_hd_wf4.menu.MSCOnBoot.default.build.msc_on_boot=0 +huidu_hd_wf4.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +huidu_hd_wf4.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +huidu_hd_wf4.menu.DFUOnBoot.default=Disabled +huidu_hd_wf4.menu.DFUOnBoot.default.build.dfu_on_boot=0 +huidu_hd_wf4.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +huidu_hd_wf4.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +huidu_hd_wf4.menu.UploadMode.default=UART0 / Hardware CDC +huidu_hd_wf4.menu.UploadMode.default.upload.use_1200bps_touch=false +huidu_hd_wf4.menu.UploadMode.default.upload.wait_for_upload_port=false +huidu_hd_wf4.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +huidu_hd_wf4.menu.UploadMode.cdc.upload.use_1200bps_touch=true +huidu_hd_wf4.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +huidu_hd_wf4.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) +huidu_hd_wf4.menu.PartitionScheme.default_8MB.build.partitions=default_8MB +huidu_hd_wf4.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 +huidu_hd_wf4.menu.PartitionScheme.large_spiffs_8MB=Large SPIFFS (1.3MB APP/5.5MiB SPIFFS) +huidu_hd_wf4.menu.PartitionScheme.large_spiffs_8MB.build.partitions=large_spiffs_8MB +huidu_hd_wf4.menu.PartitionScheme.large_spiffs_8MB.upload.maximum_size=1310720 +huidu_hd_wf4.menu.PartitionScheme.default_ffat_8MB=8MiB fatfs (3MB APP/1.5MB FATFS) +huidu_hd_wf4.menu.PartitionScheme.default_ffat_8MB.build.partitions=default_ffat_8MB +huidu_hd_wf4.menu.PartitionScheme.default_ffat_8MB.upload.maximum_size=3342336 + +huidu_hd_wf4.menu.CPUFreq.240=240MHz (WiFi) +huidu_hd_wf4.menu.CPUFreq.240.build.f_cpu=240000000L +huidu_hd_wf4.menu.CPUFreq.160=160MHz (WiFi) +huidu_hd_wf4.menu.CPUFreq.160.build.f_cpu=160000000L +huidu_hd_wf4.menu.CPUFreq.80=80MHz (WiFi) +huidu_hd_wf4.menu.CPUFreq.80.build.f_cpu=80000000L +huidu_hd_wf4.menu.CPUFreq.40=40MHz +huidu_hd_wf4.menu.CPUFreq.40.build.f_cpu=40000000L + +huidu_hd_wf4.menu.PSRAM.enabled=QSPI Flash fix +huidu_hd_wf4.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +huidu_hd_wf4.menu.PSRAM.enabled.build.psram_type=qspi + +huidu_hd_wf4.menu.FlashMode.qio=QIO 80MHz +huidu_hd_wf4.menu.FlashMode.qio.build.flash_mode=dio +huidu_hd_wf4.menu.FlashMode.qio.build.boot=qio +huidu_hd_wf4.menu.FlashMode.qio.build.boot_freq=80m +huidu_hd_wf4.menu.FlashMode.qio.build.flash_freq=80m +huidu_hd_wf4.menu.FlashMode.qio120=QIO 120MHz +huidu_hd_wf4.menu.FlashMode.qio120.build.flash_mode=dio +huidu_hd_wf4.menu.FlashMode.qio120.build.boot=qio +huidu_hd_wf4.menu.FlashMode.qio120.build.boot_freq=120m +huidu_hd_wf4.menu.FlashMode.qio120.build.flash_freq=80m +huidu_hd_wf4.menu.FlashMode.dio=DIO 80MHz +huidu_hd_wf4.menu.FlashMode.dio.build.flash_mode=dio +huidu_hd_wf4.menu.FlashMode.dio.build.boot=dio +huidu_hd_wf4.menu.FlashMode.dio.build.boot_freq=80m +huidu_hd_wf4.menu.FlashMode.dio.build.flash_freq=80m + +huidu_hd_wf4.menu.UploadSpeed.921600=921600 +huidu_hd_wf4.menu.UploadSpeed.921600.upload.speed=921600 +huidu_hd_wf4.menu.UploadSpeed.115200=115200 +huidu_hd_wf4.menu.UploadSpeed.115200.upload.speed=115200 +huidu_hd_wf4.menu.UploadSpeed.460800.linux=460800 +huidu_hd_wf4.menu.UploadSpeed.460800.macosx=460800 +huidu_hd_wf4.menu.UploadSpeed.460800.upload.speed=460800 + +huidu_hd_wf4.menu.DebugLevel.none=None +huidu_hd_wf4.menu.DebugLevel.none.build.code_debug=0 +huidu_hd_wf4.menu.DebugLevel.error=Error +huidu_hd_wf4.menu.DebugLevel.error.build.code_debug=1 +huidu_hd_wf4.menu.DebugLevel.warn=Warn +huidu_hd_wf4.menu.DebugLevel.warn.build.code_debug=2 +huidu_hd_wf4.menu.DebugLevel.info=Info +huidu_hd_wf4.menu.DebugLevel.info.build.code_debug=3 +huidu_hd_wf4.menu.DebugLevel.debug=Debug +huidu_hd_wf4.menu.DebugLevel.debug.build.code_debug=4 +huidu_hd_wf4.menu.DebugLevel.verbose=Verbose +huidu_hd_wf4.menu.DebugLevel.verbose.build.code_debug=5 + +huidu_hd_wf4.menu.EraseFlash.none=Disabled +huidu_hd_wf4.menu.EraseFlash.none.upload.erase_cmd= +huidu_hd_wf4.menu.EraseFlash.all=Enabled +huidu_hd_wf4.menu.EraseFlash.all.upload.erase_cmd=-e + +############################################################## diff --git a/variants/huidu_hd_wf2/pins_arduino.h b/variants/huidu_hd_wf2/pins_arduino.h new file mode 100644 index 00000000000..97d72dcc0f3 --- /dev/null +++ b/variants/huidu_hd_wf2/pins_arduino.h @@ -0,0 +1,76 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +// Huidu HD-WF2 - esp32-s3 HUB75 driver board +// https://www.hdwell.com/Product/index46.html +// https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA/discussions/667 + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +#define WF2_X1_R1_PIN 2 +#define WF2_X1_R2_PIN 3 +#define WF2_X1_G1_PIN 6 +#define WF2_X1_G2_PIN 7 +#define WF2_X1_B1_PIN 10 +#define WF2_X1_B2_PIN 11 +#define WF2_X1_E_PIN 21 + +#define WF2_X2_R1_PIN 4 +#define WF2_X2_R2_PIN 5 +#define WF2_X2_G1_PIN 8 +#define WF2_X2_G2_PIN 9 +#define WF2_X2_B1_PIN 12 +#define WF2_X2_B2_PIN 13 +#define WF2_X2_E_PIN -1 // Currently unknown, so X2 port will not work (yet) with 1/32 scan panels + +#define WF2_A_PIN 39 +#define WF2_B_PIN 38 +#define WF2_C_PIN 37 +#define WF2_D_PIN 36 +#define WF2_OE_PIN 35 +#define WF2_CLK_PIN 34 +#define WF2_LAT_PIN 33 + +#define WF2_BUTTON_TEST 17 // Test key button on PCB, 1=normal, 0=pressed +#define WF2_LED_RUN_PIN 40 // Status LED on PCB +#define WF2_BM8563_I2C_SDA 41 // RTC BM8563 I2C port +#define WF2_BM8563_I2C_SCL 42 +#define WF2_USB_DN_PIN 19 // USB D- +#define WF2_USB_DP_PIN 20 // USB D+ + +#define WF2_PCB1_PIN 45 // open pad on PCB +#define WF2_PCB2_PIN 46 // open pad on PCB + +#define LED_BUILTIN WF2_LED_RUN_PIN +#define BUILTIN_LED LED_BUILTIN // backward compatibility + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t SDA = WF2_BM8563_I2C_SDA; +static const uint8_t SCL = WF2_BM8563_I2C_SCL; + +static const uint8_t SS = 10; +static const uint8_t MOSI = 11; +static const uint8_t MISO = 13; +static const uint8_t SCK = 12; + +static const uint8_t T1 = WF2_X1_R1_PIN; +static const uint8_t T2 = WF2_X1_R2_PIN; +static const uint8_t T3 = WF2_X1_G1_PIN; +static const uint8_t T4 = WF2_X1_G2_PIN; +static const uint8_t T5 = WF2_X1_B1_PIN; +static const uint8_t T6 = WF2_X1_B2_PIN; +static const uint8_t T7 = WF2_A_PIN; +static const uint8_t T8 = WF2_B_PIN; +static const uint8_t T9 = WF2_C_PIN; +static const uint8_t T10 = WF2_D_PIN; +static const uint8_t T11 = WF2_X1_E_PIN; +static const uint8_t T12 = WF2_OE_PIN; +static const uint8_t T13 = WF2_CLK_PIN; +static const uint8_t T14 = WF2_LAT_PIN; + +#endif /* Pins_Arduino_h */ diff --git a/variants/huidu_hd_wf4/pins_arduino.h b/variants/huidu_hd_wf4/pins_arduino.h new file mode 100644 index 00000000000..cd96db6c445 --- /dev/null +++ b/variants/huidu_hd_wf4/pins_arduino.h @@ -0,0 +1,106 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +// Huidu HD-WF4 - esp32-s3 HUB75 driver board +// https://www.hdwell.com/Product/index46.html +// https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA/discussions/667 + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +#define WF4_A_PIN 39 +#define WF4_B_PIN 38 +#define WF4_C_PIN 37 +#define WF4_D_PIN 36 +#define WF4_E_PIN 21 +#define WF4_OE_PIN 35 +#define WF4_CLK_PIN 34 +#define WF4_LAT_PIN 33 + +// X1 +#define WF4_X1_R1_PIN 2 +#define WF4_X1_R2_PIN 3 +#define WF4_X1_G1_PIN 6 +#define WF4_X1_G2_PIN 7 +#define WF4_X1_B1_PIN 10 +#define WF4_X1_B2_PIN 11 +#define WF4_X1_CS_PIN 45 // CS gpio must be set HIGH to enable X1 output + +// X2 +#define WF4_X2_R1_PIN 4 +#define WF4_X2_R2_PIN 5 +#define WF4_X2_G1_PIN 8 +#define WF4_X2_G2_PIN 9 +#define WF4_X2_B1_PIN 12 +#define WF4_X2_B2_PIN 13 +#define WF4_X2_CS_PIN WF4_X1_CS_PIN // CS gpio must be set HIGH to enable X2 output + +// X3 +#define WF4_X3_R1_PIN 2 +#define WF4_X3_R2_PIN 3 +#define WF4_X3_G1_PIN 6 +#define WF4_X3_G2_PIN 7 +#define WF4_X3_B1_PIN 10 +#define WF4_X3_B2_PIN 11 +#define WF4_X3_CS_PIN 14 // CS gpio must be set HIGH to enable X3 output + +// X4 +#define WF4_X4_R1_PIN 4 +#define WF4_X4_R2_PIN 5 +#define WF4_X4_G1_PIN 8 +#define WF4_X4_G2_PIN 9 +#define WF4_X4_B1_PIN 12 +#define WF4_X4_B2_PIN 13 +#define WF4_X4_CS_PIN WF4_X3_CS_PIN // CS gpio must be set HIGH to enable X4 output + +//#define WF4_P1_PIN UART +#define WF4_P2_DATA_PIN 0 // GPIO0 boot +#define WF4_P5_DATA_PIN 16 // temperature +//#define WF4_P7_PIN VCC/GND +#define WF4_P11_DATA_PIN 15 // IR +#define WF4_P12_DATA1_PIN 47 +#define WF4_P12_DATA2_PIN 18 +#define WF4_S1_DATA_PIN 17 // Button +#define WF4_S2_DATA_PIN 48 +#define WF4_S3_DATA_PIN 26 +#define WF4_S4_DATA_PIN 46 + +#define WF4_BUTTON_TEST WF4_S1_PIN // Test key button on PCB, 1=normal, 0=pressed +#define WF4_LED_RUN_PIN 40 // Status LED on PCB +#define WF4_BM8563_I2C_SDA 41 // RTC BM8563 I2C port +#define WF4_BM8563_I2C_SCL 42 +#define WF4_USB_DM_PIN 19 +#define WF4_USB_DP_PIN 20 + +#define LED_BUILTIN WF4_LED_RUN_PIN +#define BUILTIN_LED LED_BUILTIN // backward compatibility + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t SDA = WF4_BM8563_I2C_SDA; +static const uint8_t SCL = WF4_BM8563_I2C_SCL; + +static const uint8_t SS = 10; +static const uint8_t MOSI = 11; +static const uint8_t MISO = 13; +static const uint8_t SCK = 12; + +static const uint8_t T1 = WF4_X1_R1_PIN; +static const uint8_t T2 = WF4_X1_R2_PIN; +static const uint8_t T3 = WF4_X1_G1_PIN; +static const uint8_t T4 = WF4_X1_G2_PIN; +static const uint8_t T5 = WF4_X1_B1_PIN; +static const uint8_t T6 = WF4_X1_B2_PIN; +static const uint8_t T7 = WF4_A_PIN; +static const uint8_t T8 = WF4_B_PIN; +static const uint8_t T9 = WF4_C_PIN; +static const uint8_t T10 = WF4_D_PIN; +static const uint8_t T11 = WF4_E_PIN; +static const uint8_t T12 = WF4_OE_PIN; +static const uint8_t T13 = WF4_CLK_PIN; +static const uint8_t T14 = WF4_LAT_PIN; + +#endif /* Pins_Arduino_h */ From 51ef2a1d29c738d5e826deca1248d71bf30c829e Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 7 Jan 2025 18:01:12 +0200 Subject: [PATCH 22/60] fix(platform): Improve firmware size calculation (#10820) * fix(platform): Improve firmware size calculation It will still have a couple of hundred of bytes difference * fix(build): Change partition used for UploadHugeFile --- libraries/WebServer/examples/UploadHugeFile/ci.json | 1 + platform.txt | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/WebServer/examples/UploadHugeFile/ci.json b/libraries/WebServer/examples/UploadHugeFile/ci.json index 618e46bd244..cbdd28f773d 100644 --- a/libraries/WebServer/examples/UploadHugeFile/ci.json +++ b/libraries/WebServer/examples/UploadHugeFile/ci.json @@ -1,4 +1,5 @@ { + "fqbn_append": "PartitionScheme=huge_app", "requires_any": [ "CONFIG_SOC_WIFI_SUPPORTED=y", "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" diff --git a/platform.txt b/platform.txt index db4ab69dfd5..cc9608a4f9d 100644 --- a/platform.txt +++ b/platform.txt @@ -185,8 +185,8 @@ recipe.output.save_file={build.project_name}.{build.variant}.bin ## Compute size recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf" -recipe.size.regex=^(?:\.iram0\.text|\.iram0\.vectors|\.dram0\.data|\.flash\.text|\.flash\.rodata|)\s+([0-9]+).* -recipe.size.regex.data=^(?:\.dram0\.data|\.dram0\.bss|\.noinit)\s+([0-9]+).* +recipe.size.regex=^(?:\.iram0\.text|\.iram0\.vectors|\.dram0\.data|\.dram1\.data|\.flash\.text|\.flash\.rodata|\.flash\.appdesc|\.flash\.init_array|\.eh_frame|)\s+([0-9]+).* +recipe.size.regex.data=^(?:\.dram0\.data|\.dram0\.bss|\.dram1\.data|\.dram1\.bss|\.noinit)\s+([0-9]+).* ## Required discoveries and monitors ## --------------------------------- From c23c78674995079a1c49ceae3b1e6ab9fdafb454 Mon Sep 17 00:00:00 2001 From: stefan-sherwood Date: Tue, 7 Jan 2025 16:53:43 -0500 Subject: [PATCH 23/60] ESPmDNS: Add method for querying discovered service name (#10819) * Add method for querying service name * Added implementation for getting service name * Update mDNS-SD_Extended example to include service name * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../examples/mDNS-SD_Extended/mDNS-SD_Extended.ino | 2 ++ libraries/ESPmDNS/src/ESPmDNS.cpp | 9 +++++++++ libraries/ESPmDNS/src/ESPmDNS.h | 1 + 3 files changed, 12 insertions(+) diff --git a/libraries/ESPmDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino b/libraries/ESPmDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino index d2e48c0580c..e630d668cea 100644 --- a/libraries/ESPmDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino +++ b/libraries/ESPmDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino @@ -68,6 +68,8 @@ void browseService(const char *service, const char *proto) { Serial.print(" "); Serial.print(i + 1); Serial.print(": "); + Serial.print(MDNS.instanceName(i)); + Serial.print(" - "); Serial.print(MDNS.hostname(i)); Serial.print(" ("); Serial.print(MDNS.address(i)); diff --git a/libraries/ESPmDNS/src/ESPmDNS.cpp b/libraries/ESPmDNS/src/ESPmDNS.cpp index 4c15ed3a5dd..5d422513bbd 100644 --- a/libraries/ESPmDNS/src/ESPmDNS.cpp +++ b/libraries/ESPmDNS/src/ESPmDNS.cpp @@ -299,6 +299,15 @@ String MDNSResponder::hostname(int idx) { return String(result->hostname); } +String MDNSResponder::instanceName(int idx) { + mdns_result_t *result = _getResult(idx); + if (!result) { + log_e("Result %d not found", idx); + return String(); + } + return String(result->instance_name); +} + IPAddress MDNSResponder::address(int idx) { mdns_result_t *result = _getResult(idx); if (!result) { diff --git a/libraries/ESPmDNS/src/ESPmDNS.h b/libraries/ESPmDNS/src/ESPmDNS.h index 0336f476efe..1fb90bbf454 100644 --- a/libraries/ESPmDNS/src/ESPmDNS.h +++ b/libraries/ESPmDNS/src/ESPmDNS.h @@ -110,6 +110,7 @@ class MDNSResponder { } String hostname(int idx); + String instanceName(int idx); IPAddress address(int idx); IPAddress addressV6(int idx); uint16_t port(int idx); From 2f423afc4eb481bcc1e31c795643f009d184bf4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:31:51 +0100 Subject: [PATCH 24/60] fix(zigbee): Fixes of timeout, example, warnings and bounding + add a 2MB part scheme for Zigbee ED (#10817) * feat(zigbee): Add 2MB partition scheme for ED * fix(example): Check if readings are not null * fix(zigbee): Add a 10s timeout for temp sensor settings read * fix(example): Remove duplicated read * fix(zigbee): Check if device is not bound before bounding again * fix(zigbee): Revert zigbee partitions name * fix(zigbee): Fix warnings of missing initializer for member * fix(zigbee): Fix typo in the comment Co-authored-by: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- boards.txt | 6 ++ .../Zigbee_On_Off_Switch.ino | 10 ++- libraries/Zigbee/src/ZigbeeCore.cpp | 15 +++- libraries/Zigbee/src/ZigbeeEP.cpp | 2 - libraries/Zigbee/src/ZigbeeEP.h | 2 + .../src/ep/ZigbeeCarbonDioxideSensor.cpp | 42 ++++------ libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp | 42 ++++------ .../Zigbee/src/ep/ZigbeePressureSensor.cpp | 42 ++++------ libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp | 81 +++++++------------ libraries/Zigbee/src/ep/ZigbeeThermostat.cpp | 2 +- tools/partitions/zigbee_2MB.csv | 7 ++ 11 files changed, 108 insertions(+), 143 deletions(-) create mode 100644 tools/partitions/zigbee_2MB.csv diff --git a/boards.txt b/boards.txt index 6f9b0a2ddf6..909874fc6d4 100644 --- a/boards.txt +++ b/boards.txt @@ -453,6 +453,9 @@ esp32h2.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 esp32h2.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs esp32h2.menu.PartitionScheme.zigbee.build.partitions=zigbee esp32h2.menu.PartitionScheme.zigbee.upload.maximum_size=1310720 +esp32h2.menu.PartitionScheme.zigbee_2MB=Zigbee 2MB with spiffs +esp32h2.menu.PartitionScheme.zigbee_2MB.build.partitions=zigbee_2MB +esp32h2.menu.PartitionScheme.zigbee_2MB.upload.maximum_size=1310720 esp32h2.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs esp32h2.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr esp32h2.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 @@ -645,6 +648,9 @@ esp32c6.menu.PartitionScheme.rainmaker_8MB.upload.maximum_size=4116480 esp32c6.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs esp32c6.menu.PartitionScheme.zigbee.build.partitions=zigbee esp32c6.menu.PartitionScheme.zigbee.upload.maximum_size=1310720 +esp32c6.menu.PartitionScheme.zigbee_2MB=Zigbee 2MB with spiffs +esp32c6.menu.PartitionScheme.zigbee_2MB.build.partitions=zigbee_2MB +esp32c6.menu.PartitionScheme.zigbee_2MB.upload.maximum_size=1310720 esp32c6.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs esp32c6.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr esp32c6.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 diff --git a/libraries/Zigbee/examples/Zigbee_On_Off_Switch/Zigbee_On_Off_Switch.ino b/libraries/Zigbee/examples/Zigbee_On_Off_Switch/Zigbee_On_Off_Switch.ino index 56d23cdc95f..0721371ce0e 100644 --- a/libraries/Zigbee/examples/Zigbee_On_Off_Switch/Zigbee_On_Off_Switch.ino +++ b/libraries/Zigbee/examples/Zigbee_On_Off_Switch/Zigbee_On_Off_Switch.ino @@ -143,8 +143,14 @@ void setup() { "IEEE Address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\r\n", device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3], device->ieee_addr[2], device->ieee_addr[1], device->ieee_addr[0] ); - Serial.printf("Light manufacturer: %s\r\n", zbSwitch.readManufacturer(device->endpoint, device->short_addr, device->ieee_addr)); - Serial.printf("Light model: %s\r\n", zbSwitch.readModel(device->endpoint, device->short_addr, device->ieee_addr)); + char *manufacturer = zbSwitch.readManufacturer(device->endpoint, device->short_addr, device->ieee_addr); + char *model = zbSwitch.readModel(device->endpoint, device->short_addr, device->ieee_addr); + if (manufacturer != nullptr) { + Serial.printf("Light manufacturer: %s\r\n", manufacturer); + } + if (model != nullptr) { + Serial.printf("Light model: %s\r\n", model); + } } Serial.println(); diff --git a/libraries/Zigbee/src/ZigbeeCore.cpp b/libraries/Zigbee/src/ZigbeeCore.cpp index cc0111f2644..0a3177919da 100644 --- a/libraries/Zigbee/src/ZigbeeCore.cpp +++ b/libraries/Zigbee/src/ZigbeeCore.cpp @@ -314,7 +314,20 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) { // for each endpoint in the list call the findEndpoint function if not bounded or allowed to bind multiple devices for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { if (!(*it)->bound() || (*it)->epAllowMultipleBinding()) { - (*it)->findEndpoint(&cmd_req); + // Check if the device is already bound + bool found = false; + // Get the list of devices bound to the EP + std::list bound_devices = (*it)->getBoundDevices(); + for (std::list::iterator device = bound_devices.begin(); device != bound_devices.end(); ++device) { + if (((*device)->short_addr == dev_annce_params->device_short_addr) || (memcmp((*device)->ieee_addr, dev_annce_params->ieee_addr, 8) == 0)) { + found = true; + log_d("Device already bound to endpoint %d", (*it)->getEndpoint()); + break; + } + } + if (!found) { + (*it)->findEndpoint(&cmd_req); + } } } } diff --git a/libraries/Zigbee/src/ZigbeeEP.cpp b/libraries/Zigbee/src/ZigbeeEP.cpp index dbfe8596268..61f2fa8d2de 100644 --- a/libraries/Zigbee/src/ZigbeeEP.cpp +++ b/libraries/Zigbee/src/ZigbeeEP.cpp @@ -7,8 +7,6 @@ #include "esp_zigbee_cluster.h" #include "zcl/esp_zigbee_zcl_power_config.h" -#define ZB_CMD_TIMEOUT 10000 // 10 seconds - bool ZigbeeEP::_is_bound = false; bool ZigbeeEP::_allow_multiple_binding = false; diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index 522c84620ff..a5e9efa4283 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -8,6 +8,8 @@ #include /* Useful defines */ +#define ZB_CMD_TIMEOUT 10000 // 10 seconds + #define ZB_ARRAY_LENTH(arr) (sizeof(arr) / sizeof(arr[0])) #define XYZ_TO_RGB(X, Y, Z, r, g, b) \ { \ diff --git a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp index 89454c057e3..eabc3c7c194 100644 --- a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp @@ -45,34 +45,20 @@ void ZigbeeCarbonDioxideSensor::setReporting(uint16_t min_interval, uint16_t max if (delta > 0) { log_e("Delta reporting is currently not supported by the carbon dioxide sensor"); } - // clang-format off - esp_zb_zcl_reporting_info_t reporting_info = { - .direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV, - .ep = _endpoint, - .cluster_id = ESP_ZB_ZCL_CLUSTER_ID_CARBON_DIOXIDE_MEASUREMENT, - .cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, - .attr_id = ESP_ZB_ZCL_ATTR_CARBON_DIOXIDE_MEASUREMENT_MEASURED_VALUE_ID, - .u = - { - .send_info = - { - .min_interval = min_interval, - .max_interval = max_interval, - .delta = - { - .u16 = delta, - }, - .def_min_interval = min_interval, - .def_max_interval = max_interval, - }, - }, - .dst = - { - .profile_id = ESP_ZB_AF_HA_PROFILE_ID, - }, - .manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC, - }; - // clang-format on + esp_zb_zcl_reporting_info_t reporting_info; + memset(&reporting_info, 0, sizeof(esp_zb_zcl_reporting_info_t)); + reporting_info.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV; + reporting_info.ep = _endpoint; + reporting_info.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_CARBON_DIOXIDE_MEASUREMENT; + reporting_info.cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE; + reporting_info.attr_id = ESP_ZB_ZCL_ATTR_CARBON_DIOXIDE_MEASUREMENT_MEASURED_VALUE_ID; + reporting_info.u.send_info.min_interval = min_interval; + reporting_info.u.send_info.max_interval = max_interval; + reporting_info.u.send_info.def_min_interval = min_interval; + reporting_info.u.send_info.def_max_interval = max_interval; + reporting_info.u.send_info.delta.u16 = delta; + reporting_info.dst.profile_id = ESP_ZB_AF_HA_PROFILE_ID; + reporting_info.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC; esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_update_reporting_info(&reporting_info); esp_zb_lock_release(); diff --git a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp index 6adf8172194..75196e78543 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp @@ -41,34 +41,20 @@ void ZigbeeFlowSensor::setTolerance(float tolerance) { } void ZigbeeFlowSensor::setReporting(uint16_t min_interval, uint16_t max_interval, float delta) { - // clang-format off - esp_zb_zcl_reporting_info_t reporting_info = { - .direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV, - .ep = _endpoint, - .cluster_id = ESP_ZB_ZCL_CLUSTER_ID_FLOW_MEASUREMENT, - .cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, - .attr_id = ESP_ZB_ZCL_ATTR_FLOW_MEASUREMENT_VALUE_ID, - .u = - { - .send_info = - { - .min_interval = min_interval, - .max_interval = max_interval, - .delta = - { - .u16 = (uint16_t)(delta * 10), // Convert delta to ZCL uint16_t - }, - .def_min_interval = min_interval, - .def_max_interval = max_interval, - }, - }, - .dst = - { - .profile_id = ESP_ZB_AF_HA_PROFILE_ID, - }, - .manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC, - }; - // clang-format on + esp_zb_zcl_reporting_info_t reporting_info; + memset(&reporting_info, 0, sizeof(esp_zb_zcl_reporting_info_t)); + reporting_info.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV; + reporting_info.ep = _endpoint; + reporting_info.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_FLOW_MEASUREMENT; + reporting_info.cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE; + reporting_info.attr_id = ESP_ZB_ZCL_ATTR_FLOW_MEASUREMENT_VALUE_ID; + reporting_info.u.send_info.min_interval = min_interval; + reporting_info.u.send_info.max_interval = max_interval; + reporting_info.u.send_info.def_min_interval = min_interval; + reporting_info.u.send_info.def_max_interval = max_interval; + reporting_info.u.send_info.delta.u16 = (uint16_t)(delta * 10); // Convert delta to ZCL uint16_t + reporting_info.dst.profile_id = ESP_ZB_AF_HA_PROFILE_ID; + reporting_info.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC; esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_update_reporting_info(&reporting_info); esp_zb_lock_release(); diff --git a/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp b/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp index 9415502215d..24b4efb127e 100644 --- a/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp @@ -38,34 +38,20 @@ void ZigbeePressureSensor::setTolerance(uint16_t tolerance) { } void ZigbeePressureSensor::setReporting(uint16_t min_interval, uint16_t max_interval, uint16_t delta) { - // clang-format off - esp_zb_zcl_reporting_info_t reporting_info = { - .direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV, - .ep = _endpoint, - .cluster_id = ESP_ZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT, - .cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, - .attr_id = ESP_ZB_ZCL_ATTR_PRESSURE_MEASUREMENT_VALUE_ID, - .u = - { - .send_info = - { - .min_interval = min_interval, - .max_interval = max_interval, - .delta = - { - .u16 = delta, // x hPa - }, - .def_min_interval = min_interval, - .def_max_interval = max_interval, - }, - }, - .dst = - { - .profile_id = ESP_ZB_AF_HA_PROFILE_ID, - }, - .manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC, - }; - // clang-format on + esp_zb_zcl_reporting_info_t reporting_info; + memset(&reporting_info, 0, sizeof(esp_zb_zcl_reporting_info_t)); + reporting_info.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV; + reporting_info.ep = _endpoint; + reporting_info.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT; + reporting_info.cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE; + reporting_info.attr_id = ESP_ZB_ZCL_ATTR_PRESSURE_MEASUREMENT_VALUE_ID; + reporting_info.u.send_info.min_interval = min_interval; + reporting_info.u.send_info.max_interval = max_interval; + reporting_info.u.send_info.def_min_interval = min_interval; + reporting_info.u.send_info.def_max_interval = max_interval; + reporting_info.u.send_info.delta.u16 = delta; // x hPa + reporting_info.dst.profile_id = ESP_ZB_AF_HA_PROFILE_ID; + reporting_info.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC; esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_update_reporting_info(&reporting_info); esp_zb_lock_release(); diff --git a/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp index 98a3793fbad..d419fb14adc 100644 --- a/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp @@ -35,33 +35,20 @@ void ZigbeeTempSensor::setTolerance(float tolerance) { } void ZigbeeTempSensor::setReporting(uint16_t min_interval, uint16_t max_interval, float delta) { - esp_zb_zcl_reporting_info_t reporting_info = { - .direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV, - .ep = _endpoint, - .cluster_id = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT, - .cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, - .attr_id = ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, - .u = - { - .send_info = - { - .min_interval = min_interval, - .max_interval = max_interval, - .delta = - { - .u16 = (uint16_t)(delta * 100), // Convert delta to ZCL uint16_t - }, - .def_min_interval = min_interval, - .def_max_interval = max_interval, - }, - }, - .dst = - { - .profile_id = ESP_ZB_AF_HA_PROFILE_ID, - }, - .manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC, - }; - esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_reporting_info_t reporting_info; + memset(&reporting_info, 0, sizeof(esp_zb_zcl_reporting_info_t)); + reporting_info.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV; + reporting_info.ep = _endpoint; + reporting_info.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; + reporting_info.cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE; + reporting_info.attr_id = ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID; + reporting_info.u.send_info.min_interval = min_interval; + reporting_info.u.send_info.max_interval = max_interval; + reporting_info.u.send_info.def_min_interval = min_interval; + reporting_info.u.send_info.def_max_interval = max_interval; + reporting_info.u.send_info.delta.u16 = (uint16_t)(delta * 100); // Convert delta to ZCL uint16_t + reporting_info.dst.profile_id = ESP_ZB_AF_HA_PROFILE_ID; + reporting_info.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC, esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_update_reporting_info(&reporting_info); esp_zb_lock_release(); } @@ -136,32 +123,20 @@ void ZigbeeTempSensor::reportHumidity() { } void ZigbeeTempSensor::setHumidityReporting(uint16_t min_interval, uint16_t max_interval, float delta) { - esp_zb_zcl_reporting_info_t reporting_info = { - .direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV, - .ep = _endpoint, - .cluster_id = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT, - .cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, - .attr_id = ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID, - .u = - { - .send_info = - { - .min_interval = min_interval, - .max_interval = max_interval, - .delta = - { - .u16 = (uint16_t)(delta * 100), // Convert delta to ZCL uint16_t - }, - .def_min_interval = min_interval, - .def_max_interval = max_interval, - }, - }, - .dst = - { - .profile_id = ESP_ZB_AF_HA_PROFILE_ID, - }, - .manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC, - }; + esp_zb_zcl_reporting_info_t reporting_info; + memset(&reporting_info, 0, sizeof(esp_zb_zcl_reporting_info_t)); + reporting_info.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV; + reporting_info.ep = _endpoint; + reporting_info.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + reporting_info.cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE; + reporting_info.attr_id = ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID; + reporting_info.u.send_info.min_interval = min_interval; + reporting_info.u.send_info.max_interval = max_interval; + reporting_info.u.send_info.def_min_interval = min_interval; + reporting_info.u.send_info.def_max_interval = max_interval; + reporting_info.u.send_info.delta.u16 = (uint16_t)(delta * 100); // Convert delta to ZCL uint16_t + reporting_info.dst.profile_id = ESP_ZB_AF_HA_PROFILE_ID; + reporting_info.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC; esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_update_reporting_info(&reporting_info); esp_zb_lock_release(); diff --git a/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp b/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp index 4610e087563..f8957f073a5 100644 --- a/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp @@ -166,7 +166,7 @@ void ZigbeeThermostat::getSensorSettings() { esp_zb_lock_release(); //Take semaphore to wait for response of all attributes - if (xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE) { + if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) { log_e("Error while reading attributes"); return; } else { diff --git a/tools/partitions/zigbee_2MB.csv b/tools/partitions/zigbee_2MB.csv new file mode 100644 index 00000000000..18adb6358bd --- /dev/null +++ b/tools/partitions/zigbee_2MB.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +factory, app, factory, 0x10000, 0x140000, +spiffs, data, spiffs, 0x150000,0x9B000, +zb_storage, data, fat, 0x1EB000,0x4000, +zb_fct, data, fat, 0x1EF000,0x1000, +coredump, data, coredump,0x1F0000,0x10000, From 814371726d15c059062473d293672f56b3905680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Fri, 10 Jan 2025 13:04:39 +0100 Subject: [PATCH 25/60] fix(zigbee): Fixes in handlers, destructors and co2 sensor delta reporting (#10834) * fix(zigbee): Fix co2 sensor delta in reporting * fix(zigbee): Declare default empty destructors * fix(zigbee): Return on error in zigbee handlers --- libraries/Zigbee/src/ZigbeeCore.cpp | 1 - libraries/Zigbee/src/ZigbeeCore.h | 2 +- libraries/Zigbee/src/ZigbeeEP.cpp | 2 -- libraries/Zigbee/src/ZigbeeEP.h | 2 +- libraries/Zigbee/src/ZigbeeHandlers.cpp | 10 ++++++++++ libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp | 4 +++- libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h | 2 +- libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h | 2 +- libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.h | 2 +- libraries/Zigbee/src/ep/ZigbeeDimmableLight.h | 2 +- libraries/Zigbee/src/ep/ZigbeeFlowSensor.h | 2 +- libraries/Zigbee/src/ep/ZigbeeLight.h | 2 +- libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h | 2 +- libraries/Zigbee/src/ep/ZigbeePressureSensor.h | 2 +- libraries/Zigbee/src/ep/ZigbeeSwitch.h | 2 +- libraries/Zigbee/src/ep/ZigbeeTempSensor.h | 2 +- libraries/Zigbee/src/ep/ZigbeeThermostat.h | 2 +- 17 files changed, 26 insertions(+), 17 deletions(-) diff --git a/libraries/Zigbee/src/ZigbeeCore.cpp b/libraries/Zigbee/src/ZigbeeCore.cpp index 0a3177919da..19f4d0872b8 100644 --- a/libraries/Zigbee/src/ZigbeeCore.cpp +++ b/libraries/Zigbee/src/ZigbeeCore.cpp @@ -29,7 +29,6 @@ ZigbeeCore::ZigbeeCore() { } } } -ZigbeeCore::~ZigbeeCore() {} //forward declaration static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message); diff --git a/libraries/Zigbee/src/ZigbeeCore.h b/libraries/Zigbee/src/ZigbeeCore.h index c72a60d81c4..02dce54e5ff 100644 --- a/libraries/Zigbee/src/ZigbeeCore.h +++ b/libraries/Zigbee/src/ZigbeeCore.h @@ -86,7 +86,7 @@ class ZigbeeCore { public: ZigbeeCore(); - ~ZigbeeCore(); + ~ZigbeeCore() {} std::list ep_objects; diff --git a/libraries/Zigbee/src/ZigbeeEP.cpp b/libraries/Zigbee/src/ZigbeeEP.cpp index 61f2fa8d2de..adc87540b83 100644 --- a/libraries/Zigbee/src/ZigbeeEP.cpp +++ b/libraries/Zigbee/src/ZigbeeEP.cpp @@ -27,8 +27,6 @@ ZigbeeEP::ZigbeeEP(uint8_t endpoint) { } } -ZigbeeEP::~ZigbeeEP() {} - void ZigbeeEP::setVersion(uint8_t version) { _ep_config.app_device_version = version; } diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index a5e9efa4283..d8ed900a7e6 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -55,7 +55,7 @@ typedef enum { class ZigbeeEP { public: ZigbeeEP(uint8_t endpoint = 10); - ~ZigbeeEP(); + ~ZigbeeEP() {} // Set ep config and cluster list void setEpConfig(esp_zb_endpoint_config_t ep_config, esp_zb_cluster_list_t *cluster_list) { diff --git a/libraries/Zigbee/src/ZigbeeHandlers.cpp b/libraries/Zigbee/src/ZigbeeHandlers.cpp index 881d7ca0c37..e387b7ce432 100644 --- a/libraries/Zigbee/src/ZigbeeHandlers.cpp +++ b/libraries/Zigbee/src/ZigbeeHandlers.cpp @@ -29,9 +29,11 @@ static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, static esp_err_t zb_attribute_set_handler(const esp_zb_zcl_set_attr_value_message_t *message) { if (!message) { log_e("Empty message"); + return ESP_FAIL; } if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { log_e("Received message: error status(%d)", message->info.status); + return ESP_ERR_INVALID_ARG; } log_v( @@ -55,9 +57,11 @@ static esp_err_t zb_attribute_set_handler(const esp_zb_zcl_set_attr_value_messag static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_message_t *message) { if (!message) { log_e("Empty message"); + return ESP_FAIL; } if (message->status != ESP_ZB_ZCL_STATUS_SUCCESS) { log_e("Received message: error status(%d)", message->status); + return ESP_ERR_INVALID_ARG; } log_v( "Received report from address(0x%x) src endpoint(%d) to dst endpoint(%d) cluster(0x%x)", message->src_address.u.short_addr, message->src_endpoint, @@ -75,9 +79,11 @@ static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_mes static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_resp_message_t *message) { if (!message) { log_e("Empty message"); + return ESP_FAIL; } if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { log_e("Received message: error status(%d)", message->info.status); + return ESP_ERR_INVALID_ARG; } log_v( "Read attribute response: from address(0x%x) src endpoint(%d) to dst endpoint(%d) cluster(0x%x)", message->info.src_address.u.short_addr, @@ -109,9 +115,11 @@ static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_re static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_report_resp_message_t *message) { if (!message) { log_e("Empty message"); + return ESP_FAIL; } if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { log_e("Received message: error status(%d)", message->info.status); + return ESP_ERR_INVALID_ARG; } esp_zb_zcl_config_report_resp_variable_t *variable = message->variables; while (variable) { @@ -127,9 +135,11 @@ static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_re static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message) { if (!message) { log_e("Empty message"); + return ESP_FAIL; } if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { log_e("Received message: error status(%d)", message->info.status); + return ESP_ERR_INVALID_ARG; } log_v( "Received default response: from address(0x%x), src_endpoint(%d) to dst_endpoint(%d), cluster(0x%x) with status 0x%x", diff --git a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp index eabc3c7c194..5def43f4199 100644 --- a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp @@ -56,9 +56,11 @@ void ZigbeeCarbonDioxideSensor::setReporting(uint16_t min_interval, uint16_t max reporting_info.u.send_info.max_interval = max_interval; reporting_info.u.send_info.def_min_interval = min_interval; reporting_info.u.send_info.def_max_interval = max_interval; - reporting_info.u.send_info.delta.u16 = delta; reporting_info.dst.profile_id = ESP_ZB_AF_HA_PROFILE_ID; reporting_info.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC; + float delta_f = delta / 1000000.0f; + memcpy(&reporting_info.u.send_info.delta.s32, &delta_f, sizeof(float)); + esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_update_reporting_info(&reporting_info); esp_zb_lock_release(); diff --git a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h index 71c353d4695..7744fd02f00 100644 --- a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h @@ -39,7 +39,7 @@ typedef struct zigbee_carbon_dioxide_sensor_cfg_s { class ZigbeeCarbonDioxideSensor : public ZigbeeEP { public: ZigbeeCarbonDioxideSensor(uint8_t endpoint); - ~ZigbeeCarbonDioxideSensor(); + ~ZigbeeCarbonDioxideSensor() {} // Set the carbon dioxide value in ppm void setCarbonDioxide(float carbon_dioxide); diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h index 9fa59dcfffc..dad267c7b39 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h @@ -12,7 +12,7 @@ class ZigbeeColorDimmableLight : public ZigbeeEP { public: ZigbeeColorDimmableLight(uint8_t endpoint); - ~ZigbeeColorDimmableLight(); + ~ZigbeeColorDimmableLight() {} void onLightChange(void (*callback)(bool, uint8_t, uint8_t, uint8_t, uint8_t)) { _on_light_change = callback; diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.h b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.h index 8e2a4d9e1a3..ca67fb4ba62 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.h +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.h @@ -12,7 +12,7 @@ class ZigbeeColorDimmerSwitch : public ZigbeeEP { public: ZigbeeColorDimmerSwitch(uint8_t endpoint); - ~ZigbeeColorDimmerSwitch(); + ~ZigbeeColorDimmerSwitch() {} // methods to control the color dimmable light void lightToggle(); diff --git a/libraries/Zigbee/src/ep/ZigbeeDimmableLight.h b/libraries/Zigbee/src/ep/ZigbeeDimmableLight.h index 034c34899b4..6f92c3315e4 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDimmableLight.h +++ b/libraries/Zigbee/src/ep/ZigbeeDimmableLight.h @@ -67,7 +67,7 @@ typedef struct zigbee_dimmable_light_cfg_s { class ZigbeeDimmableLight : public ZigbeeEP { public: ZigbeeDimmableLight(uint8_t endpoint); - ~ZigbeeDimmableLight(); + ~ZigbeeDimmableLight() {} void onLightChange(void (*callback)(bool, uint8_t)) { _on_light_change = callback; diff --git a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h index 3514e2fcc1b..7d5ec26f7ec 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h @@ -39,7 +39,7 @@ typedef struct zigbee_flow_sensor_cfg_s { class ZigbeeFlowSensor : public ZigbeeEP { public: ZigbeeFlowSensor(uint8_t endpoint); - ~ZigbeeFlowSensor(); + ~ZigbeeFlowSensor() {} // Set the flow value in 0,1 m3/h void setFlow(float value); diff --git a/libraries/Zigbee/src/ep/ZigbeeLight.h b/libraries/Zigbee/src/ep/ZigbeeLight.h index 9b8fc409d4a..8cf8c35f781 100644 --- a/libraries/Zigbee/src/ep/ZigbeeLight.h +++ b/libraries/Zigbee/src/ep/ZigbeeLight.h @@ -12,7 +12,7 @@ class ZigbeeLight : public ZigbeeEP { public: ZigbeeLight(uint8_t endpoint); - ~ZigbeeLight(); + ~ZigbeeLight() {} // Use to set a cb function to be called on light change void onLightChange(void (*callback)(bool)) { diff --git a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h index e4b25c063e5..40c5eddbbdd 100644 --- a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h @@ -39,7 +39,7 @@ typedef struct zigbee_occupancy_sensor_cfg_s { class ZigbeeOccupancySensor : public ZigbeeEP { public: ZigbeeOccupancySensor(uint8_t endpoint); - ~ZigbeeOccupancySensor(); + ~ZigbeeOccupancySensor() {} // Set the occupancy value. True for occupied, false for unoccupied void setOccupancy(bool occupied); diff --git a/libraries/Zigbee/src/ep/ZigbeePressureSensor.h b/libraries/Zigbee/src/ep/ZigbeePressureSensor.h index f088c033bb9..2d72ef04c06 100644 --- a/libraries/Zigbee/src/ep/ZigbeePressureSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeePressureSensor.h @@ -39,7 +39,7 @@ typedef struct zigbee_pressure_sensor_cfg_s { class ZigbeePressureSensor : public ZigbeeEP { public: ZigbeePressureSensor(uint8_t endpoint); - ~ZigbeePressureSensor(); + ~ZigbeePressureSensor() {} // Set the pressure value in 1 hPa void setPressure(int16_t value); diff --git a/libraries/Zigbee/src/ep/ZigbeeSwitch.h b/libraries/Zigbee/src/ep/ZigbeeSwitch.h index 62264641378..b638bfe823a 100644 --- a/libraries/Zigbee/src/ep/ZigbeeSwitch.h +++ b/libraries/Zigbee/src/ep/ZigbeeSwitch.h @@ -12,7 +12,7 @@ class ZigbeeSwitch : public ZigbeeEP { public: ZigbeeSwitch(uint8_t endpoint); - ~ZigbeeSwitch(); + ~ZigbeeSwitch() {} // methods to control the on/off light void lightToggle(); diff --git a/libraries/Zigbee/src/ep/ZigbeeTempSensor.h b/libraries/Zigbee/src/ep/ZigbeeTempSensor.h index 2951d4b7628..e610ff8d356 100644 --- a/libraries/Zigbee/src/ep/ZigbeeTempSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeTempSensor.h @@ -12,7 +12,7 @@ class ZigbeeTempSensor : public ZigbeeEP { public: ZigbeeTempSensor(uint8_t endpoint); - ~ZigbeeTempSensor(); + ~ZigbeeTempSensor() {} // Set the temperature value in 0,01°C void setTemperature(float value); diff --git a/libraries/Zigbee/src/ep/ZigbeeThermostat.h b/libraries/Zigbee/src/ep/ZigbeeThermostat.h index fe797ffd7b6..669ed9ab50a 100644 --- a/libraries/Zigbee/src/ep/ZigbeeThermostat.h +++ b/libraries/Zigbee/src/ep/ZigbeeThermostat.h @@ -32,7 +32,7 @@ class ZigbeeThermostat : public ZigbeeEP { public: ZigbeeThermostat(uint8_t endpoint); - ~ZigbeeThermostat(); + ~ZigbeeThermostat() {} void onTempRecieve(void (*callback)(float)) { _on_temp_recieve = callback; From a81e2d48f57c0a4c0b90c4a06596904d0905805a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Fri, 10 Jan 2025 13:05:16 +0100 Subject: [PATCH 26/60] fix(i2s): Check if pin is used before clearing bus (#10833) * fix(i2s): Check if pin is used before clearing bus * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- libraries/ESP_I2S/src/ESP_I2S.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/libraries/ESP_I2S/src/ESP_I2S.cpp b/libraries/ESP_I2S/src/ESP_I2S.cpp index 0ac1e176dc8..d0ceb0c4b4c 100644 --- a/libraries/ESP_I2S/src/ESP_I2S.cpp +++ b/libraries/ESP_I2S/src/ESP_I2S.cpp @@ -719,9 +719,15 @@ bool I2SClass::end() { #if SOC_I2S_SUPPORTS_TDM case I2S_MODE_TDM: #endif - perimanClearPinBus(_mclk); - perimanClearPinBus(_bclk); - perimanClearPinBus(_ws); + if (_mclk >= 0) { + perimanClearPinBus(_mclk); + } + if (_bclk >= 0) { + perimanClearPinBus(_bclk); + } + if (_ws >= 0) { + perimanClearPinBus(_ws); + } if (_dout >= 0) { perimanClearPinBus(_dout); } From 4c36c897686ddca4dc6c0778c48cd368713e4f00 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 13 Jan 2025 07:36:03 -0300 Subject: [PATCH 27/60] fix(matter): serial print instead of log_i (#10842) * fix(matter): serial print instead of log_i * fix(example): adds a white space to the message --- .../examples/MatterMinimum/MatterMinimum.ino | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libraries/Matter/examples/MatterMinimum/MatterMinimum.ino b/libraries/Matter/examples/MatterMinimum/MatterMinimum.ino index fa2599df6dd..db591ee2226 100644 --- a/libraries/Matter/examples/MatterMinimum/MatterMinimum.ino +++ b/libraries/Matter/examples/MatterMinimum/MatterMinimum.ino @@ -55,6 +55,8 @@ bool onOffLightCallback(bool state) { } void setup() { + Serial.begin(115200); + // Initialize the USER BUTTON (Boot button) that will be used to decommission the Matter Node pinMode(buttonPin, INPUT_PULLUP); // Initialize the LED GPIO @@ -63,9 +65,14 @@ void setup() { // Manually connect to WiFi WiFi.begin(ssid, password); // Wait for connection + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); while (WiFi.status() != WL_CONNECTED) { + Serial.print('.'); delay(500); } + Serial.println(); // Initialize at least one Matter EndPoint OnOffLight.begin(); @@ -77,11 +84,11 @@ void setup() { Matter.begin(); if (!Matter.isDeviceCommissioned()) { - log_i("Matter Node is not commissioned yet."); - log_i("Initiate the device discovery in your Matter environment."); - log_i("Commission it to your Matter hub with the manual pairing code or QR code"); - log_i("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str()); - log_i("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str()); + Serial.println("Matter Node is not commissioned yet."); + Serial.println("Initiate the device discovery in your Matter environment."); + Serial.println("Commission it to your Matter hub with the manual pairing code or QR code"); + Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str()); + Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str()); } } From 0773dd7619b1117bd59a068b69a76cdb22484f7c Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 13 Jan 2025 07:40:14 -0300 Subject: [PATCH 28/60] Bugfix/include order (#10841) * fix(arduino): include order pins_arduino first * fix(arduino): include order pins_arduino first * fix(arduino): include order pins_arduino first --- cores/esp32/Arduino.h | 9 ++++----- cores/esp32/FirmwareMSC.cpp | 2 +- cores/esp32/esp32-hal-gpio.h | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cores/esp32/Arduino.h b/cores/esp32/Arduino.h index ab7e497dcf6..d21089cc3fe 100644 --- a/cores/esp32/Arduino.h +++ b/cores/esp32/Arduino.h @@ -33,7 +33,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" -#include "esp32-hal.h" #include "esp8266-compat.h" #include "soc/gpio_reg.h" @@ -41,6 +40,10 @@ #include "binary.h" #include "extra_attr.h" +#include "pins_arduino.h" +#include "io_pin_remap.h" +#include "esp32-hal.h" + #define PI 3.1415926535897932384626433832795 #define HALF_PI 1.5707963267948966192313216916398 #define TWO_PI 6.283185307179586476925286766559 @@ -248,8 +251,4 @@ void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0); void noTone(uint8_t _pin); #endif /* __cplusplus */ - -#include "pins_arduino.h" -#include "io_pin_remap.h" - #endif /* _ESP32_CORE_ARDUINO_H_ */ diff --git a/cores/esp32/FirmwareMSC.cpp b/cores/esp32/FirmwareMSC.cpp index c408f52fcdb..2e944ad4df3 100644 --- a/cores/esp32/FirmwareMSC.cpp +++ b/cores/esp32/FirmwareMSC.cpp @@ -19,8 +19,8 @@ #include "esp_partition.h" #include "esp_ota_ops.h" #include "esp_image_format.h" -#include "esp32-hal.h" #include "pins_arduino.h" +#include "esp32-hal.h" #include "firmware_msc_fat.h" #include "spi_flash_mmap.h" diff --git a/cores/esp32/esp32-hal-gpio.h b/cores/esp32/esp32-hal-gpio.h index 4547d90cc56..9fce4368c22 100644 --- a/cores/esp32/esp32-hal-gpio.h +++ b/cores/esp32/esp32-hal-gpio.h @@ -24,9 +24,9 @@ extern "C" { #endif +#include "pins_arduino.h" #include "esp32-hal.h" #include "soc/soc_caps.h" -#include "pins_arduino.h" #include "driver/gpio.h" #if (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) From fcce93a02949c50ce7ab565884b933fb474b3437 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Mon, 13 Jan 2025 19:14:41 -0300 Subject: [PATCH 29/60] fix(example): Fix partition for PathArgServer --- libraries/WebServer/examples/PathArgServer/ci.json | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/WebServer/examples/PathArgServer/ci.json b/libraries/WebServer/examples/PathArgServer/ci.json index 618e46bd244..cbdd28f773d 100644 --- a/libraries/WebServer/examples/PathArgServer/ci.json +++ b/libraries/WebServer/examples/PathArgServer/ci.json @@ -1,4 +1,5 @@ { + "fqbn_append": "PartitionScheme=huge_app", "requires_any": [ "CONFIG_SOC_WIFI_SUPPORTED=y", "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" From 01d9726609cbb1425431dd26f5dda2ca4418b4e6 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 14 Jan 2025 20:46:31 -0300 Subject: [PATCH 30/60] docs(discord): Replace Gitter links with Discord (#10852) * docs(discord): Replace Gitter links with Discord * docs(links): Add Developer Portal link --- .github/ISSUE_TEMPLATE/config.yml | 6 +++--- README.md | 4 +++- docs/en/getting_started.rst | 8 +++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 03b3a76df1e..e879b09bec2 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,8 @@ blank_issues_enabled: false contact_links: - - name: Arduino ESP32 Gitter Channel - url: https://gitter.im/espressif/arduino-esp32 - about: Community channel for questions and help + - name: Arduino Core for Espressif Discord Server + url: https://discord.gg/8xY6e9crwv + about: Community Discord server for questions and help - name: ESP32 Forum - Arduino url: https://esp32.com/viewforum.php?f=19 about: Official Forum for questions diff --git a/README.md b/README.md index e610229fc60..0a9a6d0440c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ [![External Libraries Test](https://img.shields.io/github/actions/workflow/status/espressif/arduino-esp32/lib.yml?branch=master&event=schedule&label=External%20Libraries%20Test)](https://github.com/espressif/arduino-esp32/blob/gh-pages/LIBRARIES_TEST.md) [![Runtime Tests](https://github.com/espressif/arduino-esp32/blob/gh-pages/runtime-tests-results/badge.svg)](https://github.com/espressif/arduino-esp32/actions/workflows/tests_results.yml) -### Need help or have a question? Join the chat at [Gitter](https://gitter.im/espressif/arduino-esp32) or [open a new Discussion](https://github.com/espressif/arduino-esp32/discussions) +### Need help or have a question? Join the chat at [Discord](https://discord.gg/8xY6e9crwv) or [open a new Discussion](https://github.com/espressif/arduino-esp32/discussions) + +[![Discord invite](https://img.shields.io/discord/1327272229427216425?logo=discord&logoColor=white&logoSize=auto&label=Discord)](https://discord.gg/8xY6e9crwv) ## Contents diff --git a/docs/en/getting_started.rst b/docs/en/getting_started.rst index 8d312317bdf..cabb2ddc805 100644 --- a/docs/en/getting_started.rst +++ b/docs/en/getting_started.rst @@ -102,7 +102,8 @@ Here are some community channels where you may find information and ask for some - `ESP32 Forum`_: Official Espressif Forum. - `ESP32 Forum - Arduino`_: Official Espressif Forum for Arduino related discussions. - `ESP32 Forum - Hardware`_: Official Espressif Forum for Hardware related discussions. -- `Gitter`_ +- `Espressif Developer Portal`_: Official Espressif Developer Portal with tutorials, examples, workshops, and more. +- `Arduino Core for Espressif (Discord)`_: Official Espressif Discord channel for the Arduino Core. - `Espressif MCUs (Discord)`_ - `ESP32 on Reddit`_ @@ -148,12 +149,13 @@ Resources .. _Espressif Systems: https://www.espressif.com .. _Espressif Product Selector: https://products.espressif.com/ +.. _Espressif Developer Portal: https://developer.espressif.com/ .. _Arduino.cc: https://www.arduino.cc/en/Main/Software .. _Arduino Reference: https://www.arduino.cc/reference/en/ .. _ESP32 Forum: https://esp32.com .. _ESP32 Forum - Arduino: https://esp32.com/viewforum.php?f=19 .. _ESP32 Forum - Hardware: https://esp32.com/viewforum.php?f=12 -.. _Gitter: https://gitter.im/espressif/arduino-esp32 +.. _Arduino Core for Espressif (Discord): https://discord.gg/8xY6e9crwv .. _Adafruit (Discord): https://discord.gg/adafruit -.. _Espressif MCUs (Discord): https://discord.gg/nKxMTnkD +.. _Espressif MCUs (Discord): https://discord.com/invite/XqnZPbF .. _ESP32 on Reddit: https://www.reddit.com/r/esp32 From 2a2b81ad6fe6318badbe8751fae7b64df484711b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Wed, 15 Jan 2025 15:35:27 +0100 Subject: [PATCH 31/60] feat(zigbee): Add 2MB + 8MB partitions (#10864) --- boards.txt | 30 ++++++++++++++++++++++------ tools/partitions/zigbee_2MB.csv | 2 +- tools/partitions/zigbee_8MB.csv | 9 +++++++++ tools/partitions/zigbee_zczr_2MB.csv | 8 ++++++++ tools/partitions/zigbee_zczr_8MB.csv | 10 ++++++++++ 5 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 tools/partitions/zigbee_8MB.csv create mode 100644 tools/partitions/zigbee_zczr_2MB.csv create mode 100644 tools/partitions/zigbee_zczr_8MB.csv diff --git a/boards.txt b/boards.txt index 909874fc6d4..8e9655cda48 100644 --- a/boards.txt +++ b/boards.txt @@ -450,15 +450,24 @@ esp32h2.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 #esp32h2.menu.PartitionScheme.rainmaker_8MB=RainMaker 8MB #esp32h2.menu.PartitionScheme.rainmaker_8MB.build.partitions=rainmaker_8MB #esp32h2.menu.PartitionScheme.rainmaker_8MB.upload.maximum_size=4116480 -esp32h2.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs -esp32h2.menu.PartitionScheme.zigbee.build.partitions=zigbee -esp32h2.menu.PartitionScheme.zigbee.upload.maximum_size=1310720 esp32h2.menu.PartitionScheme.zigbee_2MB=Zigbee 2MB with spiffs esp32h2.menu.PartitionScheme.zigbee_2MB.build.partitions=zigbee_2MB esp32h2.menu.PartitionScheme.zigbee_2MB.upload.maximum_size=1310720 +esp32h2.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs +esp32h2.menu.PartitionScheme.zigbee.build.partitions=zigbee +esp32h2.menu.PartitionScheme.zigbee.upload.maximum_size=1310720 +esp32h2.menu.PartitionScheme.zigbee_8MB=Zigbee 8MB with spiffs +esp32h2.menu.PartitionScheme.zigbee_8MB.build.partitions=zigbee_8MB +esp32h2.menu.PartitionScheme.zigbee_8MB.upload.maximum_size=3407872 +esp32h2.menu.PartitionScheme.zigbee_zczr_2MB=Zigbee ZCZR 2MB with spiffs +esp32h2.menu.PartitionScheme.zigbee_zczr_2MB.build.partitions=zigbee_zczr_2MB +esp32h2.menu.PartitionScheme.zigbee_zczr_2MB.upload.maximum_size=1310720 esp32h2.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs esp32h2.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr esp32h2.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +esp32h2.menu.PartitionScheme.zigbee_zczr_8MB=Zigbee ZCZR 8MB with spiffs +esp32h2.menu.PartitionScheme.zigbee_zczr_8MB.build.partitions=zigbee_zczr_8MB +esp32h2.menu.PartitionScheme.zigbee_zczr_8MB.upload.maximum_size=3407872 esp32h2.menu.PartitionScheme.custom=Custom esp32h2.menu.PartitionScheme.custom.build.partitions= esp32h2.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -645,15 +654,24 @@ esp32c6.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 esp32c6.menu.PartitionScheme.rainmaker_8MB=RainMaker 8MB esp32c6.menu.PartitionScheme.rainmaker_8MB.build.partitions=rainmaker_8MB esp32c6.menu.PartitionScheme.rainmaker_8MB.upload.maximum_size=4116480 -esp32c6.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs -esp32c6.menu.PartitionScheme.zigbee.build.partitions=zigbee -esp32c6.menu.PartitionScheme.zigbee.upload.maximum_size=1310720 esp32c6.menu.PartitionScheme.zigbee_2MB=Zigbee 2MB with spiffs esp32c6.menu.PartitionScheme.zigbee_2MB.build.partitions=zigbee_2MB esp32c6.menu.PartitionScheme.zigbee_2MB.upload.maximum_size=1310720 +esp32c6.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs +esp32c6.menu.PartitionScheme.zigbee.build.partitions=zigbee +esp32c6.menu.PartitionScheme.zigbee.upload.maximum_size=1310720 +esp32c6.menu.PartitionScheme.zigbee_8MB=Zigbee 8MB with spiffs +esp32c6.menu.PartitionScheme.zigbee_8MB.build.partitions=zigbee_8MB +esp32c6.menu.PartitionScheme.zigbee_8MB.upload.maximum_size=3407872 +esp32c6.menu.PartitionScheme.zigbee_zczr_2MB=Zigbee ZCZR 2MB with spiffs +esp32c6.menu.PartitionScheme.zigbee_zczr_2MB.build.partitions=zigbee_zczr_2MB +esp32c6.menu.PartitionScheme.zigbee_zczr_2MB.upload.maximum_size=1310720 esp32c6.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs esp32c6.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr esp32c6.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +esp32c6.menu.PartitionScheme.zigbee_zczr_8MB=Zigbee ZCZR 8MB with spiffs +esp32c6.menu.PartitionScheme.zigbee_zczr_8MB.build.partitions=zigbee_zczr_8MB +esp32c6.menu.PartitionScheme.zigbee_zczr_8MB.upload.maximum_size=3407872 esp32c6.menu.PartitionScheme.custom=Custom esp32c6.menu.PartitionScheme.custom.build.partitions= esp32c6.menu.PartitionScheme.custom.upload.maximum_size=16777216 diff --git a/tools/partitions/zigbee_2MB.csv b/tools/partitions/zigbee_2MB.csv index 18adb6358bd..7034f9bd49b 100644 --- a/tools/partitions/zigbee_2MB.csv +++ b/tools/partitions/zigbee_2MB.csv @@ -1,6 +1,6 @@ # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x5000, -factory, app, factory, 0x10000, 0x140000, +factory, app, factory, 0x10000, 0x140000, spiffs, data, spiffs, 0x150000,0x9B000, zb_storage, data, fat, 0x1EB000,0x4000, zb_fct, data, fat, 0x1EF000,0x1000, diff --git a/tools/partitions/zigbee_8MB.csv b/tools/partitions/zigbee_8MB.csv new file mode 100644 index 00000000000..fdf46fb59d1 --- /dev/null +++ b/tools/partitions/zigbee_8MB.csv @@ -0,0 +1,9 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x340000, +app1, app, ota_1, 0x350000,0x340000, +spiffs, data, spiffs, 0x690000,0x15B000, +zb_storage, data, fat, 0x7EB000,0x4000, +zb_fct, data, fat, 0x7EF000,0x1000, +coredump, data, coredump,0x7F0000,0x10000, diff --git a/tools/partitions/zigbee_zczr_2MB.csv b/tools/partitions/zigbee_zczr_2MB.csv new file mode 100644 index 00000000000..10484eeed87 --- /dev/null +++ b/tools/partitions/zigbee_zczr_2MB.csv @@ -0,0 +1,8 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +factory, app, factory, 0x10000, 0x140000, +spiffs, data, spiffs, 0x150000,0x9A000, +zb_storage, data, fat, 0x1EA000,0x4000, +zb_fct, data, fat, 0x1EE000,0x1000, +rcp_fw, data, spiffs, 0x1EF000,0x1000, +coredump, data, coredump,0x1F0000,0x10000, diff --git a/tools/partitions/zigbee_zczr_8MB.csv b/tools/partitions/zigbee_zczr_8MB.csv new file mode 100644 index 00000000000..70dd680dc1a --- /dev/null +++ b/tools/partitions/zigbee_zczr_8MB.csv @@ -0,0 +1,10 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x340000, +app1, app, ota_1, 0x350000,0x340000, +spiffs, data, spiffs, 0x690000,0x15A000, +zb_storage, data, fat, 0x7EA000,0x4000, +zb_fct, data, fat, 0x7EE000,0x1000, +rcp_fw, data, spiffs, 0x7EF000,0x1000, +coredump, data, coredump,0x7F0000,0x10000, From 496b8411773243e1ad88a68652d6982ba2366d6b Mon Sep 17 00:00:00 2001 From: is-qian <2716275834@qq.com> Date: Tue, 21 Jan 2025 19:24:00 +0800 Subject: [PATCH 32/60] feat(esp32): Added a new device(xiao esp32s3 plus) (#10768) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(esp32): Added a new device(xiao esp32s3 plus) * Update boards.txt * Apply suggestions from code review * Fix filename error. * Fix filename error. * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- boards.txt | 178 ++++++++++++++++++ .../XIAO_ESP32S3_Plus/bootloader-tinyuf2.bin | Bin 0 -> 21408 bytes variants/XIAO_ESP32S3_Plus/partitions-8MB.csv | 10 + variants/XIAO_ESP32S3_Plus/pins_arduino.h | 90 +++++++++ variants/XIAO_ESP32S3_Plus/tinyuf2.bin | Bin 0 -> 141936 bytes 5 files changed, 278 insertions(+) create mode 100644 variants/XIAO_ESP32S3_Plus/bootloader-tinyuf2.bin create mode 100644 variants/XIAO_ESP32S3_Plus/partitions-8MB.csv create mode 100644 variants/XIAO_ESP32S3_Plus/pins_arduino.h create mode 100644 variants/XIAO_ESP32S3_Plus/tinyuf2.bin diff --git a/boards.txt b/boards.txt index 8e9655cda48..9ecd6359759 100644 --- a/boards.txt +++ b/boards.txt @@ -35211,6 +35211,184 @@ XIAO_ESP32S3.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## +XIAO_ESP32S3_Plus.name=XIAO_ESP32S3_PLUS +XIAO_ESP32S3_Plus.vid.0=0x2886 +XIAO_ESP32S3_Plus.pid.0=0x0063 +XIAO_ESP32S3_Plus.vid.1=0x2886 +XIAO_ESP32S3_Plus.pid.1=0x8063 + +XIAO_ESP32S3_Plus.bootloader.tool=esptool_py +XIAO_ESP32S3_Plus.bootloader.tool.default=esptool_py + +XIAO_ESP32S3_Plus.upload.tool=esptool_py +XIAO_ESP32S3_Plus.upload.tool.default=esptool_py +XIAO_ESP32S3_Plus.upload.tool.network=esp_ota + +XIAO_ESP32S3_Plus.upload.maximum_size=1310720 +XIAO_ESP32S3_Plus.upload.maximum_data_size=327680 +XIAO_ESP32S3_Plus.upload.flags= +XIAO_ESP32S3_Plus.upload.extra_flags= +XIAO_ESP32S3_Plus.upload.use_1200bps_touch=false +XIAO_ESP32S3_Plus.upload.wait_for_upload_port=false + +XIAO_ESP32S3_Plus.serial.disableDTR=false +XIAO_ESP32S3_Plus.serial.disableRTS=false + +XIAO_ESP32S3_Plus.build.tarch=xtensa +XIAO_ESP32S3_Plus.build.bootloader_addr=0x0 +XIAO_ESP32S3_Plus.build.target=esp32s3 +XIAO_ESP32S3_Plus.build.mcu=esp32s3 +XIAO_ESP32S3_Plus.build.core=esp32 +XIAO_ESP32S3_Plus.build.variant=XIAO_ESP32S3_Plus +XIAO_ESP32S3_Plus.build.board=XIAO_ESP32S3_PLUS + +XIAO_ESP32S3_Plus.build.usb_mode=0 +XIAO_ESP32S3_Plus.build.cdc_on_boot=1 +XIAO_ESP32S3_Plus.build.msc_on_boot=0 +XIAO_ESP32S3_Plus.build.dfu_on_boot=0 +XIAO_ESP32S3_Plus.build.f_cpu=240000000L +XIAO_ESP32S3_Plus.build.flash_size=8MB +XIAO_ESP32S3_Plus.build.flash_freq=80m +XIAO_ESP32S3_Plus.build.flash_mode=dio +XIAO_ESP32S3_Plus.build.boot=qio +XIAO_ESP32S3_Plus.build.boot_freq=80m +XIAO_ESP32S3_Plus.build.partitions=default_8MB +XIAO_ESP32S3_Plus.build.defines= +XIAO_ESP32S3_Plus.build.loop_core= +XIAO_ESP32S3_Plus.build.event_core= +XIAO_ESP32S3_Plus.build.psram_type=qspi +XIAO_ESP32S3_Plus.build.memory_type={build.boot}_{build.psram_type} + +XIAO_ESP32S3_Plus.menu.JTAGAdapter.default=Disabled +XIAO_ESP32S3_Plus.menu.JTAGAdapter.default.build.copy_jtag_files=0 +XIAO_ESP32S3_Plus.menu.JTAGAdapter.builtin=Integrated USB JTAG +XIAO_ESP32S3_Plus.menu.JTAGAdapter.builtin.build.openocdscript=esp32s3-builtin.cfg +XIAO_ESP32S3_Plus.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +XIAO_ESP32S3_Plus.menu.JTAGAdapter.external=FTDI Adapter +XIAO_ESP32S3_Plus.menu.JTAGAdapter.external.build.openocdscript=esp32s3-ftdi.cfg +XIAO_ESP32S3_Plus.menu.JTAGAdapter.external.build.copy_jtag_files=1 +XIAO_ESP32S3_Plus.menu.JTAGAdapter.bridge=ESP USB Bridge +XIAO_ESP32S3_Plus.menu.JTAGAdapter.bridge.build.openocdscript=esp32s3-bridge.cfg +XIAO_ESP32S3_Plus.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +XIAO_ESP32S3_Plus.menu.PSRAM.disabled=Disabled +XIAO_ESP32S3_Plus.menu.PSRAM.disabled.build.defines= +XIAO_ESP32S3_Plus.menu.PSRAM.disabled.build.psram_type=qspi +XIAO_ESP32S3_Plus.menu.PSRAM.opi=OPI PSRAM +XIAO_ESP32S3_Plus.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM +XIAO_ESP32S3_Plus.menu.PSRAM.opi.build.psram_type=opi + +XIAO_ESP32S3_Plus.menu.FlashMode.qio=QIO 80MHz +XIAO_ESP32S3_Plus.menu.FlashMode.qio.build.flash_mode=dio +XIAO_ESP32S3_Plus.menu.FlashMode.qio.build.boot=qio +XIAO_ESP32S3_Plus.menu.FlashMode.qio.build.boot_freq=80m +XIAO_ESP32S3_Plus.menu.FlashMode.qio.build.flash_freq=80m +XIAO_ESP32S3_Plus.menu.FlashMode.dio=DIO 80MHz +XIAO_ESP32S3_Plus.menu.FlashMode.dio.build.flash_mode=dio +XIAO_ESP32S3_Plus.menu.FlashMode.dio.build.boot=dio +XIAO_ESP32S3_Plus.menu.FlashMode.dio.build.boot_freq=80m +XIAO_ESP32S3_Plus.menu.FlashMode.dio.build.flash_freq=80m + +XIAO_ESP32S3_Plus.menu.FlashSize.8M=8MB (64Mb) +XIAO_ESP32S3_Plus.menu.FlashSize.8M.build.flash_size=8MB + +XIAO_ESP32S3_Plus.menu.LoopCore.1=Core 1 +XIAO_ESP32S3_Plus.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +XIAO_ESP32S3_Plus.menu.LoopCore.0=Core 0 +XIAO_ESP32S3_Plus.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +XIAO_ESP32S3_Plus.menu.EventsCore.1=Core 1 +XIAO_ESP32S3_Plus.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +XIAO_ESP32S3_Plus.menu.EventsCore.0=Core 0 +XIAO_ESP32S3_Plus.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +XIAO_ESP32S3_Plus.menu.USBMode.hwcdc=Hardware CDC and JTAG +XIAO_ESP32S3_Plus.menu.USBMode.hwcdc.build.usb_mode=1 +XIAO_ESP32S3_Plus.menu.USBMode.default=USB-OTG (TinyUSB) +XIAO_ESP32S3_Plus.menu.USBMode.default.build.usb_mode=0 + +XIAO_ESP32S3_Plus.menu.CDCOnBoot.default=Enabled +XIAO_ESP32S3_Plus.menu.CDCOnBoot.default.build.cdc_on_boot=1 +XIAO_ESP32S3_Plus.menu.CDCOnBoot.cdc=Disabled +XIAO_ESP32S3_Plus.menu.CDCOnBoot.cdc.build.cdc_on_boot=0 + +XIAO_ESP32S3_Plus.menu.MSCOnBoot.default=Disabled +XIAO_ESP32S3_Plus.menu.MSCOnBoot.default.build.msc_on_boot=0 +XIAO_ESP32S3_Plus.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +XIAO_ESP32S3_Plus.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +XIAO_ESP32S3_Plus.menu.DFUOnBoot.default=Disabled +XIAO_ESP32S3_Plus.menu.DFUOnBoot.default.build.dfu_on_boot=0 +XIAO_ESP32S3_Plus.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +XIAO_ESP32S3_Plus.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +XIAO_ESP32S3_Plus.menu.UploadMode.default=UART0 / Hardware CDC +XIAO_ESP32S3_Plus.menu.UploadMode.default.upload.use_1200bps_touch=false +XIAO_ESP32S3_Plus.menu.UploadMode.default.upload.wait_for_upload_port=false +XIAO_ESP32S3_Plus.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +XIAO_ESP32S3_Plus.menu.UploadMode.cdc.upload.use_1200bps_touch=true +XIAO_ESP32S3_Plus.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +XIAO_ESP32S3_Plus.menu.PartitionScheme.default_8MB=Default with spiffs (3MB APP/1.5MB SPIFFS) +XIAO_ESP32S3_Plus.menu.PartitionScheme.default_8MB.build.partitions=default_8MB +XIAO_ESP32S3_Plus.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 +XIAO_ESP32S3_Plus.menu.PartitionScheme.max_app_8MB=Maximum APP (7.9MB APP No OTA/No FS) +XIAO_ESP32S3_Plus.menu.PartitionScheme.max_app_8MB.build.partitions=max_app_8MB +XIAO_ESP32S3_Plus.menu.PartitionScheme.max_app_8MB.upload.maximum_size=8257536 +XIAO_ESP32S3_Plus.menu.PartitionScheme.tinyuf2=TinyUF2 8MB (2MB APP/3.7MB FFAT) +XIAO_ESP32S3_Plus.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +XIAO_ESP32S3_Plus.menu.PartitionScheme.tinyuf2.build.custom_partitions=partitions-8MB +XIAO_ESP32S3_Plus.menu.PartitionScheme.tinyuf2.upload.maximum_size=2097152 +XIAO_ESP32S3_Plus.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x410000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" + +XIAO_ESP32S3_Plus.menu.CPUFreq.240=240MHz (WiFi) +XIAO_ESP32S3_Plus.menu.CPUFreq.240.build.f_cpu=240000000L +XIAO_ESP32S3_Plus.menu.CPUFreq.160=160MHz (WiFi) +XIAO_ESP32S3_Plus.menu.CPUFreq.160.build.f_cpu=160000000L +XIAO_ESP32S3_Plus.menu.CPUFreq.80=80MHz (WiFi) +XIAO_ESP32S3_Plus.menu.CPUFreq.80.build.f_cpu=80000000L +XIAO_ESP32S3_Plus.menu.CPUFreq.40=40MHz +XIAO_ESP32S3_Plus.menu.CPUFreq.40.build.f_cpu=40000000L +XIAO_ESP32S3_Plus.menu.CPUFreq.20=20MHz +XIAO_ESP32S3_Plus.menu.CPUFreq.20.build.f_cpu=20000000L +XIAO_ESP32S3_Plus.menu.CPUFreq.10=10MHz +XIAO_ESP32S3_Plus.menu.CPUFreq.10.build.f_cpu=10000000L + +XIAO_ESP32S3_Plus.menu.UploadSpeed.921600=921600 +XIAO_ESP32S3_Plus.menu.UploadSpeed.921600.upload.speed=921600 +XIAO_ESP32S3_Plus.menu.UploadSpeed.115200=115200 +XIAO_ESP32S3_Plus.menu.UploadSpeed.115200.upload.speed=115200 +XIAO_ESP32S3_Plus.menu.UploadSpeed.256000.windows=256000 +XIAO_ESP32S3_Plus.menu.UploadSpeed.256000.upload.speed=256000 +XIAO_ESP32S3_Plus.menu.UploadSpeed.230400.windows.upload.speed=256000 +XIAO_ESP32S3_Plus.menu.UploadSpeed.230400=230400 +XIAO_ESP32S3_Plus.menu.UploadSpeed.230400.upload.speed=230400 +XIAO_ESP32S3_Plus.menu.UploadSpeed.460800.linux=460800 +XIAO_ESP32S3_Plus.menu.UploadSpeed.460800.macosx=460800 +XIAO_ESP32S3_Plus.menu.UploadSpeed.460800.upload.speed=460800 +XIAO_ESP32S3_Plus.menu.UploadSpeed.512000.windows=512000 +XIAO_ESP32S3_Plus.menu.UploadSpeed.512000.upload.speed=512000 + +XIAO_ESP32S3_Plus.menu.DebugLevel.none=None +XIAO_ESP32S3_Plus.menu.DebugLevel.none.build.code_debug=0 +XIAO_ESP32S3_Plus.menu.DebugLevel.error=Error +XIAO_ESP32S3_Plus.menu.DebugLevel.error.build.code_debug=1 +XIAO_ESP32S3_Plus.menu.DebugLevel.warn=Warn +XIAO_ESP32S3_Plus.menu.DebugLevel.warn.build.code_debug=2 +XIAO_ESP32S3_Plus.menu.DebugLevel.info=Info +XIAO_ESP32S3_Plus.menu.DebugLevel.info.build.code_debug=3 +XIAO_ESP32S3_Plus.menu.DebugLevel.debug=Debug +XIAO_ESP32S3_Plus.menu.DebugLevel.debug.build.code_debug=4 +XIAO_ESP32S3_Plus.menu.DebugLevel.verbose=Verbose +XIAO_ESP32S3_Plus.menu.DebugLevel.verbose.build.code_debug=5 + +XIAO_ESP32S3_Plus.menu.EraseFlash.none=Disabled +XIAO_ESP32S3_Plus.menu.EraseFlash.none.upload.erase_cmd= +XIAO_ESP32S3_Plus.menu.EraseFlash.all=Enabled +XIAO_ESP32S3_Plus.menu.EraseFlash.all.upload.erase_cmd=-e + +############################################################## + connaxio_espoir.name=Connaxio's Espoir connaxio_espoir.vid.0=0x10C4 connaxio_espoir.pid.0=0x8D9A diff --git a/variants/XIAO_ESP32S3_Plus/bootloader-tinyuf2.bin b/variants/XIAO_ESP32S3_Plus/bootloader-tinyuf2.bin new file mode 100644 index 0000000000000000000000000000000000000000..b11e5b236f0992d8351b579c8283c03a5e32a85c GIT binary patch literal 21408 zcmb_@3tUrI()h{EgMe2J4=F0zOE4I$*buCM+BHE$@v%izs&2ayAQ&t!A^7^}HWyon z*6sp!Td;PUP*c&$`s%i7mzAKU)=I1P<#x42+ib1X7L_+7_djzJURL+}f8YO~A9L?L zbLPyMGiT16nR72c3aL3$(`IRYCWLqcczyz@pm~lZkPr@j$|MotG6)f1Wvd_+Zz2FO zfCRt`KwDU-FEBEhx~yz{29uS`C<>OhD)5BvlGP z1IPdZ0D=I50T_Tu07`(V04jj#05Jfw0W<*d01E&X10(?~2UrOJ(>Lsw4Y+&&Bfutr zVgM7s^8jXm9RTG3F9TEo>;|v^>;b3+H~?@6;2nTt0DlK)05}aWsPQ$K*}6i*edgO( zkOj5@3LQe5lcURLj2pAk^+P6Vn3dd1tlyNbFDzsVv$p6N#ij?fO4p?u^lnMkT&BJ&bpJRw1}5-c|f|KBLzaWN${- zQ;?UxnJLUGDoEE)n>NkeI6nD_CHGkgi8;EpdL}(D*Qm?NEo5?vvW;2!;D!`n*ZQ?N zz^rf@^F%K2H5&8`2xbJAq-Qo}WoI*KdIr`^zCOd#MIv^=WEJvuiS~9EfKaWlmOZR!&h4h6Ct(3d0{Hdb~bUhie(42vB(c`iJol`ajz{#m^&H5jVvSb91w{aAbF5* za}E0RX9~g4+>y#OX09$fy(n8}1Q*3`rF=9p=#PNUJ${0h5q%d=ZUFsw%mVg|wOQ%z zty2jRi~&uI-|~RBL0_PsGt{Vu+1hZPp8W|nucBOZ?F`0a`V82_bg(u(p->+9nrvNu z;gCoVch>Z0$Uu!dI%g7-wM$4E#8`YXfI$`pcLloKwfaHYLGSk<|G3P(Yoa_ba_|rl z?py!xA?Wj%DfUCah2Cc(2fOhEj=Z`(nU7ni!+pr-&X{g+uXlIV5CaVnxY_=pY;kdJLL!19`rfr;1 z2PvLS5KU=g0j!t@N4tZDJ{bEAIHhb|!CJ`8j0RmU6FFl^oV(K@_6;4}FK2KsF0Aw{ z3MPVa8zi{s^t^(CqI{#9*St;nkQ$;jBPVzCjv^J_XQl^51%v5dArV72e0P3DD6>o74F$Al^F(1s7{<-WskFZA4^ zoHWq)f3m*8*1YpYy`i^<6dLwB2)@8UU{t$#Dj!+$n~PhJ>8d3QwvZV9s&-#zOp8Wb z{1YpdlLRg7^~v+J@nmV@6J#l(;^AT0;wPSTqa&BZ5wrm^9Uulk4S9CC~DftOzZqf=~)oFSh3V-g9wwZY{ez_-}=VckzN}H7Q z-?kd`T?7_s|KSL))ivpPIXU-HX-*b|H|SV+lTM7E3YP9!LJABcz`;Wx_Q2$JxdB%o zt_HXl=r?2);vo*Kr?etCjDgL4h#W`)*-mEG+M)tbd?tka{V~;$mbl;H#tHK~R2ReM zh3~Mx4X7Ff1y0j2bNW@4+gb-Tvj(=6j69DjhG{q%@;DS~-m;`9b;PnL-s01XU?t`n zA-pIo%Fl;0uKs1aJ}Ylc4%miUr^CdXm+Q$4!Ss2KL!8EgJ#B*fa1BC6%r}-M&STQD zjP7NpAIcCrbr0&-20PGmhC2rJg|Kp1zkB`Xu>lEh`p5+T{hjmyi;AoJr1pDM4I zFqapVp`X*Ym?AHlHw#tFn!wehz$LPh^QH=n25D$X?nFTGA%P|`u%2pSyDt|d;%Jx8rBW?B}jn=$Kclg zuZ4lNnGXpXW6a9YGm)y9k9hmatLc`mF@-Bz2ZXIg*eWEFou1gJ+pWIKSQuaL1RB190yE0CP+w zq=9>xYM!$M;PN~yGo%*o*L{iRyqIX{@jwMo49|0Yo983~JOQu*;Awy~fNX$#fFgj+ z09ygd0CoVB1H24y3}l%SIpI(5xMeaC(mV&x)DdWa3=N%H1#~umn^1QH?ot&=9pl$L zM+3ldFg6(goVtI{eKO}6z*zy3TIkf}04o8W0!RhO05AZw0DKQ{9pENFD?l4S7l4$| zsWiX>cwP+fP7u+w1QJcvSfWYtA({p#b4O2$P&SVAaU=;>;t#MCdpQk^VKKmN3DKl_ z6HPFz+qvKa76X(3Gy(jS1+L&>qS*ty_Q2f$cbo$=(0&L&puIAbkn2Jm6CnXm7q1|i zR;ZgA*gPiz;8}oopbydrZyHvoQNcjd2J`YfwB_bBa7Mgy0#|$s%o`^p+({>=i5pE; zJOuLsKu8d9mIAbLGqfb6m}_82iJX|2?@i^7$<@>=L4>9zw9a4!Ase{BWWpM`7Aa9Y zOaiA6TZA~IfJf#dve9BWVj(gs!*0eRJUEE>@p0g~)#?XI;h>-(jD^uIs5ffkPoy6kyFl&boxVXpyrkuu6WAi(JI% z7U4L}>GZr*vnled1?vR_r8EymT&>SB2u`%}6a@DFo@G zE2*Y>F5x>acs2VQWWRII;V?~kEIIWsXtKr%01XN-atHuT; z3OSS(NK@lch(sw|;zDJbI8jR5U3jiKb(rDkg@>wTmTDxZJ_Q%^vO{K>#Vz%+IuXkt zqm|-15!A(q(9j(vxVXbNufbmCBxkda*EC~opmA9`f-$oi3WBOT%55Fajb1UEVAC);Ym zhGRFLs}i3>#9Z7#7chvSG;R$dw-~DU^Zue0-Zj0hhfM2=XZmI#-A=~Ahtt2D;206!8H zy7W`mj-B+~rJuN>11PvUmWZ_1T=hQI?_F2K%_8;`a*n))(ybImo^_SAkBj`sHNQOo zt{dA&MV@l~;E=-gD+d+%fyvlsDhD-qE?>`r`q+6QKp zEyu@_58BfXIxRDqwNkI-54v-xj>mj$kE0w1Trl?Mk%kUTK6SVFSlJ6y(=?(!)!h+A zl};sr9g;{JKV`zmJ)ZlUo_mewZuQ(Pp8Fe~`(Hfw*FE>$p8IQ_`!3JD+Hp3~W6k!U?UoCL1k)mNxvInZ4TfgAf;{d#wU3uB_Sn75^4SYF)KL zHXV7^Q}jrB2R+X5y%12pN0fUB(hF_hAxq>r&{ls<7@~I7QtWb`kglicm*~}st3rBY zgb%W(7wTJ}RljU9kc}Rv_;ye>5Dc3*t_W#5!iRpTo}Rs0aYbm_@5@YKhREt37d=k@ zufewNaY27y4~$DkB1gGsaZM;;A4QRrLhCfwF=~m=q4P!bQ-=<e=? zR7&6x71Lk}QR-*H<$S9%Lm_ zU(*V~HUY~aw;Z2Zp=Jfcwqv1%B4z;{K!P+R-l{nvd)6mqg!s_Em_r{k^g6{Ep=K}B zA|^1UKYFP5J|C$Uf-&2Fa9XcB*_OF zMoSRwV+#@O9%t(jLVwLpYqOKxh;+q0Zes-=`c>$y^<6^y2ToXiA2_d2Y%bDRUj)wj zJ|P`MQx@e;hDilm0~`2dIu0M|E5*rUk1*@s?IPa#1$w6(p33=WFfx1Du@DrrnmwIJ44|E9Q3 zP*3Ui!A)N$=IvbZGmhgmr}a%II}iCd7J*9Ly3pSghP>>Ao%6!$PV4JVrjcQ1^CUN* zjdd4~h(ScZj$U!%7TO{Ny>CIhEkI(gbRMpBLilqP*A+_+mpg+O*9mBf{S#6=E?7c= z^=sZ?#>*XkWKmRR@-OuGx)BOLWIDr2DoUNP72rtB-fF=l>+?<~%GY9+g|jBNKlH9J zIn@FMPhkCnvo%TpwY3Aa7F&^Gq@U?$Z~gpA*?8LW5D%6 z%{HN0z_1HZx;mj=BC%$plJ#Da8oNw!TP!=wK-{1X@lyOGzB~oiGN@1QwCbH~dml7$ zye)#T(AFik+z?uh)L3dtRHqnvq?@1RhCe&aX;S+tS(99*k~5Jjo$M-SIIB|2%64Mp zVS8IVxnT6+6{yG)QS=DaiLLrW;#$^HBevP`{^l6ldoXjnt*YdLFn^x|bHhS?!=lCk zk`}72C8-#a0HHb^rAkX8@Z1g0p^|QnzUx7BVE8lqXPKobeiTp)#L#eop9zBe(QwcA zHxY>2`S>4V{~*YpWRS}Q z0bou8%a=%@Kbnz|%Rt)(Pz&^$agcum6U|qU*IWbm8K4Uw+P`^D9RQUI*$)8bPFc_& zA#=zZaOZO)LS}yf&;5DKA;@Dehr#>`a~fLIJZCk)9sp=Jn}K|!59I$D%5y;O7Xh-N z8~}LkB#sccnqX+(^4vAh=~DpG;v``z=TFFOkVgo44oRX@F|Ub)aYOxSXi0M6xzJty z2b2XD@EJIWG<517kmXA#{{Rr^)jVe*!1R2Q`Yhb-0GN07!F{$A@*#jF0ILAf01N;{ z0Dk}|1^6StE&xy`*#q})00O<6=llks9N=RB?qQIMA*36g1)#S@KF}WE9RLmh^C`^5 zR*h($Qv+}dfCjpoFb}c`L^B;g15gh0qk?g&-kYUKhWi2*zUP5xxRfb9V10GN>=uPItX$Zw$EGoZ6i=jZ|V9Kg!}Z@cRoxaYhG_gr*? z?BpkNYWZ9a~Vo=;O@E|N3;6hJXzdQo(W0ojFOE);G9^Ti*bpL!kSE zr-2{l?n^;tGOBrw7{D7~KEM`$j{#73A{cYfXrKWTTLAZ$0A2_9Cjjk7H2aeX`2n8q z0{rw7$1RsZeh+X0pbOwB(3b(g2;lxE4y+Y)VV!v?3FLSV;7!|?TU5@)6>c~5%McGdi&0<(L zsl47t0Wav{Vc_%~+O2>zJdblEC8yzjivNDk#RUT`q|Z=KL_j$Qptm)VB1c)kfVF5^ zNNOtZ#ay}pX#WL$kApd2fM64pIanYd;Bz3WiJ)QU!x&(62KW}xgx>?ELir8kkK-n$ zY`|e|5Bps`CATn@0@d0hL-HlTtLocqi_6-AA#9ftE=q&_8&|k5K5c@_KE%};=m`z` zG5^|Uz@pjyrR(Y?SHpgp{R_DA6=rwEMZ_NRplF(JTu-a&cQWq_TK3D13l5z?^5Z<8 zO^468Mnt7X2@dU-sb(TR7W+&L`mmZR{SAqGSBkd(9>vbdyWShEWG#29E1J;r*(l?qA!3V{VG`Ta|6kjxJh zr!{e@nwOERD(wJCjgo27v1L{jTnY?Pr zQ>n1|g4*N*Bq?5|nvQj`wQz}%LJFbr^hJ2YQAD{iFzlm|#dpbA z4cTce->E8>%W$88su+W|1eoyI2Ga9m^prQ1f*He{LX4f1WK7l()m<rq#i+Q- zd2;KJ^{!;g>osyXUco?DEr;x!s5C`SzC4kJC>$d3)wW*FqH0S%I5%mTj4q``&x)F4 zV2R0=GfxIkO%X)3z!>&9Rr&}~sf@wPqo-3%NLctMNOG{w(Z&I4sRt#g=l9;bh+B-f`42scq9)?ACwrjR*=}Qf{6HJg>~Z>5 zM9uM#oo{B&*E94&#V>$`2qzZt6Ivmq@uUsYQOE{e!bFk@JqnG#`1>k2acWSB2#=J zw6K_pOmc=D?=tO|S>%i-UWk;rxxrkFq=K;5wr&%Fqt#|Qh^x;O}qGtb^sFhn~F z&jLodFr6QTL%=8$GNBsFas5@!9g@~qHrrYTo;k<2T+QC|tf-$F0#VD`wr{xLh3?p2 zDb%-f`zWQbmeP+QDXY6|V+r)p;e^Dr10${CJEfPwjuz=XxsjEr$I8AL}53o-Jm-$LHwBMgW!SO@?j3 z;{3sbsXu`MpaYH7uXd}uj@62=H=;cd>*$6+%ZbI3_2lcr%9 zKJbQeoiNN#6eg!9w6e!hQi&71D0>Va6ME2L15LwGU(NfCu*BTaKOf68 z!&GH7nT(nL9944W7Cn~!y6gn=I@Kg0i^uJ)hSQWE0k=!i!#(A*x7w%V%un>#4&^uH zjL|a==7x~{3-)$sI|SPeEoR^S=JRH-t41Jzz9H#eTyv0PUqKqr`i7X8iDPVVnoLdJ zH7dqfC(=7P%bN|fza@05hi?frGLE7lC)Jo4`W0HdHb}1M=HjT4*C@K)2l7Yz3r_0` z&X(_))Grw}AGs$Vjm2)_ z`o~m7(LrI}-c@OpA35I*7tm6Zdx}s8^f+3|Y~Ko6ex1^2jD1Sg_N_NB$%W*IP;f4} z_|BmzJ14Y8NCRKw%-fXSCUyFb0F$~lH=34~ z`T4N(o$^LFK4G@)jF9%*h%Vk4Cn14Gfy03+uq&Kajgy^>gOXb?dtLe)XY}93D8A<) zADQU1&T?K6vJdlP?&REQn`H~b&O{88)OK~cGbzf{@}0>pbw;Y44UICVsHLTS@yL21 ztPeF(MfmLQQGAQ+e4N%Pn8S`hbe$Lb2;NJtq9xLPPtE$mX^ekkYHzV*EV!LFc&!#!vw(2u{%wF3H$n`&l>2wB4JxGnJT^mL>eX_ z&`)6d5{Iq%oU#AVXC3X7baE;lulNG-s?Qkv)jq3_Q*xUN{F_u0r)`P%T^FAySRz>| zcrxHv1=q)Z(znUmv|P4v%<^hqwiDT-LOSq)9(}m4kDj=AWV5(#GLlznL`3+&G37t;2`sJ!ym{$q-v885?7tA}_=flJ{8|U~q*<72 z7Ew~}*3^<~fyV^44^UTo`m7iFZb@KfY}mwe0+IUswZN4E+bPsb>c5#QBKxjB>zO{@ z(p0Gdfv*T0cHV?u?z5im1I@92#@403fUx=(xRmX^Zei=F9X9swSf%)qE4|LKC-7MU zsU_M!?1}^1oe4b|+HiiK{YW49y<-?!)MBVmWoVH5Hn<(ByKH~EZ+N*D%Dl7m)Z2#D zD;p0!=b=ZYRm#S{azo1o`%}Z{4fa>=;kRU1*=%2k^!CJltQonQ?Foo&v~2Xq!eckt z=kl~A{j{6hr<+0i!0dCd5+18Pf^Y68u@Luf}=gzgp z;6&f2MLD8G!Ly>rMKzRo?;a{)GEvIqI*JX(1`$5&c)VL5>pT3LzRDW1VVeX8?6YWp zm4KD?<+rqLPrUKH_WNFXwC0pd^PY_NN391T2*Gw$zN}YY5)j4Gqi=kt`I}7BG)OTO zDI&xBiXFgmfv}E(C>Wj+Q4i5 zcD5bZ&a2Pv>a|jRti8XTd7@$T%2C^V$Hy;_t)vZjvT$x5e8j@l z*hTDZ#Ey@O6~HN5XspihJQw+6@4oe*2{`i#FqJ51Vafv0N-F7q)^QA+jP^qBxv#GX zr_!+bi515Iv)jk^9CmcM7rGI#FCjzQ9{_VnV7=4}m~9>*y$oValnNqK6RVZn;Kye6 zs~4VAqUFH;>iCYNOyL|`xQlWK_w4WWTF>^Ns3=0hQ@u$aFl;(fm9~ST zY=wyZ2ao+dVr>P8c%MhCLd4^uP1=oNR?+c=-}$c-b?=WCB4=Sy{rs}B0u;#+M;742l+zE6oKORKy}^!lF%OCYt86w z0$qbf8ZAtnfX%}OQ|TiYWoNDIGgyF~?Kv(aqnDP#ktl5Zj&^^pO+K%y3sK>uD0SnV9HGl)Ax&|d#OHzNMu zq7!lQF!d?H{LoP77mlZGAHo`ryxbGGU!<6e0~>(@+l`%q%|I9ejBFC@GLG*u@bg~2 zPsM3&N6#xe+q9iMaUv2ZYO5C}T;uA5j(51ye{=fZAgk^kV6_cdHCiDLfNB4N=le14 zCnXkB^s#}&6>SKJyZ5>!)MS;8Q7vH@Nq~=Ek1x zuY#t82!;BlKbdk`(DO1h)bTG!K`I)d`7Z$n8|+Ut?2Voy); z*S~7}=0IEPu(m*DV zaV0wOHJwQBi|*vQ*1oF8y1ECFrO&%z!puucaX*D`LkgG(;iBk9OCLR=PRNeO5!F%Q zUi5_E9rZ%}bXa12y8^}1O0V&B(9=HUD|aT98+WhL@G0SrOSaFseWJi!5}x=AuF^|X zV)-#{GASE7F))FI%<7rYs`m!1*<*XGvw9#seFu97PYtIrbwb5Ru5V_EmpE8hc64G& zN1sgsq}RN}_GvxV$R4K<=xcfUTO0}As$Lsi`V%L&3cxi)_%vPT<&Tz+90<|jbCl1x zC-?kJ2SrbUES4Voq+CFImn&tSBM01v!?h8T`8z4e8Ztk0U0rW)p{6t!-Rdm8Q(07o- zo!!kN5M3|D_gFe%&w61`YPxA{be@jtsc7#`%`mNfkCG_JVXoWCb)!6rRxZV7M9Rh9 z^*?kU{((oqF_GhE=sR$G58K+EU)Z3#*1a)Cq-*P5mmb;HJ#%_=jO_63?i252g zT{q;X6`yp=EoAbasnT;?rB1BB;i?P~Lky+0|GnG#ukLDrQv^$820xFG`&HPx6Ts>D zN$mXmq5t^|>nifQ59fF9sw}YxUVWb`v5<Y=<%=qT|kdGWM&SJ>88g{ejn%5 z%)p7n&7jQHlPXSjTT6iK)f2Lce{}DXS6T#)pWil-cx5_^Z4qr--i4dkLwkBQ$pS9p{78|P#zb*F?$7m;q zhRP?C$0qy<8$qK2zlO8dl&rN=|kn)>!mOR6#DOYze1?&!y!T3%W3VoGGP82&}5O z{G;Onbls(cX;|HDWxJCc4EqHyr+^S;#g4R)<=qKz6iM`-x)x~beIA*P`%(0zx%MZz zt;TNlEKduuYfXsg8h)!HnE64y*zqys&0`W;(NU*r+Grno{PhbO5c;j z7bL2>o$`?gwsSbDbX;@2?sIv+;v>{UOt&?)8)AA1dlb1tT=REeWL|%aJygrEzw7SX zQt#a?O0qGPIzM&{8bxJ@U!Ax9N7uqkO;kfuEc8n1r1Xm=I$t2cXd{>dl9CYLT4~@y zS8Y&Wj}zAYlKIEZoME>guN|?Y_zZoWwVYFZG%Ao1*yFmtlEySnB_@ZdVZUnMsM_@h zW|}N7U%p{F-B7cKSk9%_u6OgksFUf<+{F-?oBbkl!VT_BFr2y&`Q&KB;Z%9U!nnA# z(KMCtA`HQBbw?)T6rW+B$E1qP6=^#9W|iZ(OEZa zYPVU#mnN)+J|VVcN*KB27rD;+j(rj=!FOW>PAZ%&uu_4dz@^^ZO8?1!n-Lb|r#}qJ zhj{l=msQ-&F30|ALZpGK$q@^&6>|{az=LKF!|vwiJQ1}_k9jwSO16+=LYkt-=1_;S zcEagQs$SM;XHpxtX4yv=Qp302qOQQVircPM-|oJkc#X@Up7k+bt-9THA?0kh{EVyF z`(3%9+2>-2wDlLJUV@a44>_bsIok;@{ySnQ+h1Ik8_cnaTK0;hS)_jnYEyhVj`>3g zINM73a(j$DFjNx527V^Cz2-{UB937pi}Qk@j8|TruwJ0Z=j@+!9kDPFaH}mXHa$0U zI-ltY*>gCmXMJ{`by&~{U+uDf(8c;8(tf&YDr|a=G;V!)7cGosQ7yYr8?;dI3>V9$ zJb9}_K4$4VT}ttei^?%OD%Gch>%HV6@#IH@5ndBoD?=f=99h1PslKg0%2nMycH!p! z@$bJFRSN5-CCztNrdi^Z=3gtem{r>({zc7Y`Mb@Ze*ym7#jUfzv ziz8!;v2?^PtFar{E%+QvAF&%6-ZHV-SlZEZV41^=?ACQzbGj0Kg0WAKe9kE)nbe5$ zUo`v^J~B;!gpHg_ms^ERXW_~50+3$|B=+BTS<_I;w@`%APN>53RC)(Y6GX|sAQnuM z{e4%Mzm$EtD=d_P>o&hw%IsJ1bXUmlx{{JaDR=QrpxiJOugWm($}V{8$790WnDg*0 z*KmwBsq0?M;$LAB@5RI&#DgyP%UlmfPazJ@^H2m&hr@aM)6Z)>2W9@eK(b;ZP$VVe zfnl(x~;2T%-E1HmdX|xlEVbq4HU_v~Tv3%!#kq-^%$Ip#v zS>dbl=*NSJ9Nk4H-isU2g`2=Vgp#hIm>Okp@ln-&SrY%oy2cR9ckpokIU&B1s#FH6 zs%1&5+=F)$_(gVf!iPjiZ0MKZ&OI1ZUAO=)N&&;PxfSCn^X>d;{!3=V*o0Gj>|yp( zQYkgwNlBB!{2vY6O2R@HhDEJYMB+rLm0=;DbizAmrvC+ZvLQ^P)k0OAVX|9aNYm7r zKb(dqP{OH?hsnuK%Y6vKOMeDK_K>{SdHy~mQ<@xHnEw+8wuXh~W=8GcY5hWub|xhS zln*0Eb;KDSl6>v%(l2B$(x~o36Ck|thes%xO@%q%17J@U!~y@z8KbD41*YrpHTyzH&-hC`YZhS zvq0ce1$^KG_scaMY>E>#`)=FXUVYd^tCy&QAgdipT@p z!MQB#00r$g1W1FSuA;Z2#-@R~!8e`kQ=PPTHNKUCdUkaujsFB-mO}m;76dOAUGQ4W z+q7>d{fOd^?i~FoDJ?AX3oWr&X#X<5@gOG{NG;&5KJEL=(kc~!$9@!u3WXhKxp5ED7jVq&83jcs`SltPqfAr_yJofOk zPUe+K_)$o;@9>K7PNo_wQioMkNtvmlnmP{B_I$aod+~-#ffXG3Pw4C~MRnm~Lo&9}Oj6u|(-Wf}qt&M+S%z8nHVB+cfiTQ$c1T;p6SS^LOoIHQ_ zP6u=Ws9GuPnH9p$KuUDv;7Je~aQjdyv$vJ&fK!MXtET^5_UtLFAI=8*T?Y_UNe|pr zAAAnn4ixx#Up&I|O^v7hFMN9&>2D9QpxRuyf)QEj!1sSyklyXLQ` zD~Pp5)L1^=+2DC?&i8EnR|mWYg<9)Z9Sp?Mp1K+HbiFc*p z-t1o$aC9 zjyQS(eDD4!J*v^-jr{X?+@thw>8A(i*73N9>0J8R=27*dkxY-Mkl%I0;=3XD*v6Yy zpbA!rV1Q4LfUzxRmhYr9FIuv2P}w`_=m7HN2TEm?UL~iv;(wMMf5_2_UpU*99oG3B_%bvZr(JGQdhA5s z$4#EeFuNMd4{I&&o66sBEU}RHDt$`Q$v*$;(C{ca2(#&@;Kh9qIJq*vhbk?QTfbU) zoermiYOF0yaGpeGL7Gj+O7I{LWQmlaEODVkvoIhm;4yfqA2w-?{yHy*q{AB4!A81m z4;36Egijh^tmC6p+Zz+9eSWd>ZR&(Bj&-&3V}^?vbOA4RL^sdCIzG1*T<%1S?<5S?*U!c7jOOL^0|L`tVWB}GZ`l?BSDz206T$G+8* zh-zlQxvpdKTD9$aF4G_K65a`A1}Ve*gSCb2CHqgh>D(Szok0{oa>x8^&0MWoVc|dq zlBqmLZ)Y#zh=QL3b6wK!-Y+7`_8RW0N)h;S6CWRi<4Q7%Le8|qZWZz#Tp2)Y6NP-x zuD#lxNWsYr?>kLx@Gl6Al41XedI-lY3GwXv?Ss$9+LZ(N&)Th@A!U`cTn;WsX1#zV z&(}S;{y%94Y`=$wD)886+xyGB4Y7Y|FW$nl(8ysokXcXN!=m9=Ea+{69C-PbP$_rD z=-=To$5Ev#;a~q;w4WtzYe*S#f_HK&wPBN}(q+U;Pf3z-3Jg?djPnrmW(PcKE8jzdhfWJ=<3CTD!+> zQ6znk_LZTuA%AjnPfg7Lk(wSO=eG)gkNxj$z$J^)mA12skU}Z3Zfy5VorGPFwMq&6 zZvuEy_-y_neESBoBVk#bY__-!uGY8P*p2P4OOO&hZ=l>KWE0PdVkT<=8zW(-x0_b@ zdUkb5q^2E|02KxyxSEo`av*%_3RKA|VP?Q+xGxYU*Kz$Xc_*}nsj1Ta94$)N2WxLq z(_SttfRY^Ibko{PYLJgo;_>qmAj=GpxrYW7S(IJVo^Xb1<;Utq%xagw@fa{WZ?=lJ@+5!H8X|(x8ss~0p_wU)P;5$65- zwr{1!pO4hOIuj1W$xQS1_u1K~6aM=t78A&UU*@UPpKsOfT)X?Duj6h_?)vh;>AJin SVR0lq@zytgJh)Ff;r{>(gA@M% literal 0 HcmV?d00001 diff --git a/variants/XIAO_ESP32S3_Plus/partitions-8MB.csv b/variants/XIAO_ESP32S3_Plus/partitions-8MB.csv new file mode 100644 index 00000000000..4026378b6fb --- /dev/null +++ b/variants/XIAO_ESP32S3_Plus/partitions-8MB.csv @@ -0,0 +1,10 @@ +# ESP-IDF Partition Table +# Name, Type, SubType, Offset, Size, Flags +# bootloader.bin,, 0x1000, 32K +# partition table,, 0x8000, 4K +nvs, data, nvs, 0x9000, 20K, +otadata, data, ota, 0xe000, 8K, +ota_0, 0, ota_0, 0x10000, 2048K, +ota_1, 0, ota_1, 0x210000, 2048K, +uf2, app, factory,0x410000, 256K, +ffat, data, fat, 0x450000, 3776K, diff --git a/variants/XIAO_ESP32S3_Plus/pins_arduino.h b/variants/XIAO_ESP32S3_Plus/pins_arduino.h new file mode 100644 index 00000000000..fb887287e35 --- /dev/null +++ b/variants/XIAO_ESP32S3_Plus/pins_arduino.h @@ -0,0 +1,90 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x2886 +#define USB_PID 0x0056 + +static const uint8_t LED_BUILTIN = 21; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t TX1 = 42; +static const uint8_t RX1 = 41; + +static const uint8_t SDA = 5; +static const uint8_t SCL = 6; + +static const uint8_t SS = 44; +static const uint8_t MOSI = 9; +static const uint8_t MISO = 8; +static const uint8_t SCK = 7; + +static const uint8_t MOSI1 = 11; +static const uint8_t MISO1 = 12; +static const uint8_t SCK1 = 13; + +static const uint8_t I2S_SCK = 39; +static const uint8_t I2S_SD = 38; +static const uint8_t I2S_WS = 40; + +static const uint8_t MTCK = 39; +static const uint8_t MTDO = 40; +static const uint8_t MTDI = 41; +static const uint8_t MTMS = 42; + +static const uint8_t DVP_Y8 = 11; +static const uint8_t DVP_YP = 12; +static const uint8_t DVP_PCLK = 13; +static const uint8_t DVP_VSYNC = 38; +static const uint8_t CAM_SCL = 39; +static const uint8_t CAM_SDA = 40; +static const uint8_t XMCLK = 10; + +static const uint8_t A0 = 1; +static const uint8_t A1 = 2; +static const uint8_t A2 = 3; +static const uint8_t A3 = 4; +static const uint8_t A4 = 5; +static const uint8_t A5 = 6; +static const uint8_t A8 = 7; +static const uint8_t A9 = 8; +static const uint8_t A10 = 9; +static const uint8_t ADC_BAT = 10; + +static const uint8_t D0 = 1; +static const uint8_t D1 = 2; +static const uint8_t D2 = 3; +static const uint8_t D3 = 4; +static const uint8_t D4 = 5; +static const uint8_t D5 = 6; +static const uint8_t D6 = 43; +static const uint8_t D7 = 44; +static const uint8_t D8 = 7; +static const uint8_t D9 = 8; +static const uint8_t D10 = 9; +static const uint8_t D11 = 38; +static const uint8_t D12 = 39; +static const uint8_t D13 = 40; +static const uint8_t D14 = 41; +static const uint8_t D15 = 42; +static const uint8_t D16 = 10; +static const uint8_t D17 = 13; +static const uint8_t D18 = 12; +static const uint8_t D19 = 11; + +static const uint8_t T1 = 1; +static const uint8_t T2 = 2; +static const uint8_t T3 = 3; +static const uint8_t T4 = 4; +static const uint8_t T5 = 5; +static const uint8_t T6 = 6; +static const uint8_t T7 = 7; +static const uint8_t T8 = 8; +static const uint8_t T9 = 9; + +#endif /* Pins_Arduino_h */ diff --git a/variants/XIAO_ESP32S3_Plus/tinyuf2.bin b/variants/XIAO_ESP32S3_Plus/tinyuf2.bin new file mode 100644 index 0000000000000000000000000000000000000000..86d981f8019368e49c7412d49731a0199b1722bd GIT binary patch literal 141936 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 literal 0 HcmV?d00001 From 15cbb1e857c6a0b4f6a88a611ff929e80ad5fe72 Mon Sep 17 00:00:00 2001 From: lsroka76 <95999074+lsroka76@users.noreply.github.com> Date: Mon, 27 Jan 2025 11:38:50 +0100 Subject: [PATCH 33/60] Add IAS Zone Notification Message service to ZigbeeHandlers and ZigbeeEP.h (#10821) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update ZigbeeHandlers.cpp * Update ZigbeeEP.h * Update ZigbeeEP.h make addBoundDevice virtual for override * fix(zigbee): Update and reorder handlers * fix(zigbee): Place default handler to the end * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- libraries/Zigbee/src/ZigbeeEP.h | 11 +++++---- libraries/Zigbee/src/ZigbeeHandlers.cpp | 30 +++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index d8ed900a7e6..ac22143c4d7 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -105,6 +105,13 @@ class ZigbeeEP { virtual void zbReadBasicCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented virtual void zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message); + virtual void zbIASZoneStatusChangeNotification(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message) {}; + + virtual void addBoundDevice(zb_device_params_t *device) { + _bound_devices.push_back(device); + _is_bound = true; + } + void onIdentify(void (*callback)(uint16_t)) { _on_identify = callback; } @@ -125,10 +132,6 @@ class ZigbeeEP { SemaphoreHandle_t lock; zb_power_source_t _power_source; - void addBoundDevice(zb_device_params_t *device) { - _bound_devices.push_back(device); - _is_bound = true; - } friend class ZigbeeCore; }; diff --git a/libraries/Zigbee/src/ZigbeeHandlers.cpp b/libraries/Zigbee/src/ZigbeeHandlers.cpp index e387b7ce432..bccb27da7f8 100644 --- a/libraries/Zigbee/src/ZigbeeHandlers.cpp +++ b/libraries/Zigbee/src/ZigbeeHandlers.cpp @@ -9,6 +9,7 @@ static esp_err_t zb_attribute_set_handler(const esp_zb_zcl_set_attr_value_messag static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_message_t *message); static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_resp_message_t *message); static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_report_resp_message_t *message); +static esp_err_t zb_cmd_ias_zone_status_change_handler(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message); static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message); // Zigbee action handlers @@ -20,8 +21,11 @@ static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, case ESP_ZB_CORE_REPORT_ATTR_CB_ID: ret = zb_attribute_reporting_handler((esp_zb_zcl_report_attr_message_t *)message); break; case ESP_ZB_CORE_CMD_READ_ATTR_RESP_CB_ID: ret = zb_cmd_read_attr_resp_handler((esp_zb_zcl_cmd_read_attr_resp_message_t *)message); break; case ESP_ZB_CORE_CMD_REPORT_CONFIG_RESP_CB_ID: ret = zb_configure_report_resp_handler((esp_zb_zcl_cmd_config_report_resp_message_t *)message); break; - case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; - default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; + case ESP_ZB_CORE_CMD_IAS_ZONE_ZONE_STATUS_CHANGE_NOT_ID: + ret = zb_cmd_ias_zone_status_change_handler((esp_zb_zcl_ias_zone_status_change_notification_message_t *)message); + break; + case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; + default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; } return ret; } @@ -132,6 +136,28 @@ static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_re return ESP_OK; } +static esp_err_t zb_cmd_ias_zone_status_change_handler(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message) { + if (!message) { + log_e("Empty message"); + return ESP_FAIL; + } + if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Received message: error status(%d)", message->info.status); + return ESP_ERR_INVALID_ARG; + } + log_v( + "IAS Zone Status Notification: from address(0x%x) src endpoint(%d) to dst endpoint(%d) cluster(0x%x)", message->info.src_address.u.short_addr, + message->info.src_endpoint, message->info.dst_endpoint, message->info.cluster + ); + + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + if (message->info.dst_endpoint == (*it)->getEndpoint()) { + (*it)->zbIASZoneStatusChangeNotification(message); + } + } + return ESP_OK; +} + static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message) { if (!message) { log_e("Empty message"); From 2fecc482b721b30085f2a9983aaf45f9d38cb064 Mon Sep 17 00:00:00 2001 From: lbernstone Date: Mon, 27 Jan 2025 00:39:15 -1000 Subject: [PATCH 34/60] fix(littlefs): Converted core disableWDT functions to bool (#10896) * fix(littlefs): Converted core disableWDT functions to bool * Missed the returns on core1 * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- cores/esp32/esp32-hal-misc.c | 12 ++++++++---- cores/esp32/esp32-hal.h | 4 ++-- libraries/LittleFS/src/LittleFS.cpp | 6 ++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c index 0bce548bdd2..02871872f83 100644 --- a/cores/esp32/esp32-hal-misc.c +++ b/cores/esp32/esp32-hal-misc.c @@ -156,11 +156,13 @@ void enableCore0WDT() { } } -void disableCore0WDT() { +bool disableCore0WDT() { TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCore(0); - if (idle_0 == NULL || esp_task_wdt_delete(idle_0) != ESP_OK) { + if (idle_0 == NULL || esp_task_wdt_status(idle_0) || esp_task_wdt_delete(idle_0) != ESP_OK) { log_e("Failed to remove Core 0 IDLE task from WDT"); + return false; } + return true; } #ifndef CONFIG_FREERTOS_UNICORE @@ -171,11 +173,13 @@ void enableCore1WDT() { } } -void disableCore1WDT() { +bool disableCore1WDT() { TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCore(1); - if (idle_1 == NULL || esp_task_wdt_delete(idle_1) != ESP_OK) { + if (idle_1 == NULL || esp_task_wdt_status(idle_1) || esp_task_wdt_delete(idle_1) != ESP_OK) { log_e("Failed to remove Core 1 IDLE task from WDT"); + return false; } + return true; } #endif diff --git a/cores/esp32/esp32-hal.h b/cores/esp32/esp32-hal.h index d0bd4b8bc93..5ed99aeb205 100644 --- a/cores/esp32/esp32-hal.h +++ b/cores/esp32/esp32-hal.h @@ -121,11 +121,11 @@ void feedLoopWDT(); //enable/disable WDT for the IDLE task on Core 0 (SYSTEM) void enableCore0WDT(); -void disableCore0WDT(); +bool disableCore0WDT(); #ifndef CONFIG_FREERTOS_UNICORE //enable/disable WDT for the IDLE task on Core 1 (Arduino) void enableCore1WDT(); -void disableCore1WDT(); +bool disableCore1WDT(); #endif //if xCoreID < 0 or CPU is unicore, it will use xTaskCreate, else xTaskCreatePinnedToCore diff --git a/libraries/LittleFS/src/LittleFS.cpp b/libraries/LittleFS/src/LittleFS.cpp index e86caeb74cc..761d1ba4c24 100644 --- a/libraries/LittleFS/src/LittleFS.cpp +++ b/libraries/LittleFS/src/LittleFS.cpp @@ -95,9 +95,11 @@ void LittleFSFS::end() { } bool LittleFSFS::format() { - disableCore0WDT(); + bool wdt_active = disableCore0WDT(); esp_err_t err = esp_littlefs_format(partitionLabel_); - enableCore0WDT(); + if (wdt_active) { + enableCore0WDT(); + } if (err) { log_e("Formatting LittleFS failed! Error: %d", err); return false; From 732a7cb4ee4ae4c5a3bcf07f15ac2526f3cefc06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Thu, 30 Jan 2025 18:11:40 +0100 Subject: [PATCH 35/60] feat(zigbee): Add Time cluster support + fix of duplicate indentify cluster (#10863) * feat(zigbee): Add Time cluster support * fix(zigbee): Remove duplicate of identify cluster * feat(zigbee): Remove unused variables in addTimeCluster * feat(zigbee): Update examples with optional Time cluster * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../Zigbee_Temperature_Sensor.ino | 21 +++ .../Zigbee_Thermostat/Zigbee_Thermostat.ino | 15 +++ libraries/Zigbee/src/ZigbeeEP.cpp | 125 ++++++++++++++++++ libraries/Zigbee/src/ZigbeeEP.h | 22 ++- libraries/Zigbee/src/ZigbeeHandlers.cpp | 2 + .../src/ep/ZigbeeCarbonDioxideSensor.cpp | 1 - libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp | 1 - .../Zigbee/src/ep/ZigbeeOccupancySensor.cpp | 1 - .../Zigbee/src/ep/ZigbeePressureSensor.cpp | 1 - 9 files changed, 181 insertions(+), 8 deletions(-) diff --git a/libraries/Zigbee/examples/Zigbee_Temperature_Sensor/Zigbee_Temperature_Sensor.ino b/libraries/Zigbee/examples/Zigbee_Temperature_Sensor/Zigbee_Temperature_Sensor.ino index 27aa2db97bd..ad007abbbaa 100644 --- a/libraries/Zigbee/examples/Zigbee_Temperature_Sensor/Zigbee_Temperature_Sensor.ino +++ b/libraries/Zigbee/examples/Zigbee_Temperature_Sensor/Zigbee_Temperature_Sensor.ino @@ -36,6 +36,11 @@ #define TEMP_SENSOR_ENDPOINT_NUMBER 10 uint8_t button = BOOT_PIN; +// Optional Time cluster variables +struct tm timeinfo; +struct tm *localTime; +int32_t timezone; + ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER); /************************ Temp sensor *****************************/ @@ -66,6 +71,9 @@ void setup() { // Optional: Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C) zbTempSensor.setTolerance(1); + // Optional: Time cluster configuration (default params, as this device will revieve time from coordinator) + zbTempSensor.addTimeCluster(); + // Add endpoint to Zigbee Core Zigbee.addEndpoint(&zbTempSensor); @@ -85,6 +93,19 @@ void setup() { } Serial.println(); + // Optional: If time cluster is added, time can be read from the coordinator + timeinfo = zbTempSensor.getTime(); + timezone = zbTempSensor.getTimezone(); + + Serial.println("UTC time:"); + Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); + + time_t local = mktime(&timeinfo) + timezone; + localTime = localtime(&local); + + Serial.println("Local time with timezone:"); + Serial.println(localTime, "%A, %B %d %Y %H:%M:%S"); + // Start Temperature sensor reading task xTaskCreate(temp_sensor_value_update, "temp_sensor_update", 2048, NULL, 10, NULL); diff --git a/libraries/Zigbee/examples/Zigbee_Thermostat/Zigbee_Thermostat.ino b/libraries/Zigbee/examples/Zigbee_Thermostat/Zigbee_Thermostat.ino index a4720feeba4..7cdf45ef711 100644 --- a/libraries/Zigbee/examples/Zigbee_Thermostat/Zigbee_Thermostat.ino +++ b/libraries/Zigbee/examples/Zigbee_Thermostat/Zigbee_Thermostat.ino @@ -45,6 +45,8 @@ float sensor_max_temp; float sensor_min_temp; float sensor_tolerance; +struct tm timeinfo = {}; // Time structure for Time cluster + /****************** Temperature sensor handling *******************/ void recieveSensorTemp(float temperature) { Serial.printf("Temperature sensor value: %.2f°C\n", temperature); @@ -71,6 +73,19 @@ void setup() { //Optional: set Zigbee device name and model zbThermostat.setManufacturerAndModel("Espressif", "ZigbeeThermostat"); + //Optional Time cluster configuration + //example time January 13, 2025 13:30:30 CET + timeinfo.tm_year = 2025 - 1900; // = 2025 + timeinfo.tm_mon = 0; // January + timeinfo.tm_mday = 13; // 13th + timeinfo.tm_hour = 12; // 12 hours - 1 hour (CET) + timeinfo.tm_min = 30; // 30 minutes + timeinfo.tm_sec = 30; // 30 seconds + timeinfo.tm_isdst = -1; + + // Set time and gmt offset (timezone in seconds -> CET = +3600 seconds) + zbThermostat.addTimeCluster(timeinfo, 3600); + //Add endpoint to Zigbee Core Zigbee.addEndpoint(&zbThermostat); diff --git a/libraries/Zigbee/src/ZigbeeEP.cpp b/libraries/Zigbee/src/ZigbeeEP.cpp index adc87540b83..6a2ace0b90c 100644 --- a/libraries/Zigbee/src/ZigbeeEP.cpp +++ b/libraries/Zigbee/src/ZigbeeEP.cpp @@ -238,4 +238,129 @@ void ZigbeeEP::zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message) { } } +void ZigbeeEP::addTimeCluster(tm time, int32_t gmt_offset) { + time_t utc_time = 0; + + // Check if time is set + if (time.tm_year > 0) { + // Convert time to UTC + utc_time = mktime(&time); + } + + // Create time cluster server attributes + esp_zb_attribute_list_t *time_cluster_server = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_TIME); + esp_zb_time_cluster_add_attr(time_cluster_server, ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID, (void *)&gmt_offset); + esp_zb_time_cluster_add_attr(time_cluster_server, ESP_ZB_ZCL_ATTR_TIME_TIME_ID, (void *)&utc_time); + // Create time cluster client attributes + esp_zb_attribute_list_t *time_cluster_client = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_TIME); + // Add time clusters to cluster list + esp_zb_cluster_list_add_time_cluster(_cluster_list, time_cluster_server, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_time_cluster(_cluster_list, time_cluster_client, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE); +} + +void ZigbeeEP::setTime(tm time) { + time_t utc_time = mktime(&time); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_TIME, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_TIME_TIME_ID, &utc_time, false); + esp_zb_lock_release(); +} + +void ZigbeeEP::setTimezone(int32_t gmt_offset) { + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_TIME, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID, &gmt_offset, false); + esp_zb_lock_release(); +} + +tm ZigbeeEP::getTime(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) { + /* Read peer time */ + esp_zb_zcl_read_attr_cmd_t read_req; + + if (short_addr >= 0) { + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = (uint16_t)short_addr; + } else { + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); + } + + uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TIME_TIME_ID}; + read_req.attr_number = ZB_ARRAY_LENTH(attributes); + read_req.attr_field = attributes; + + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TIME; + + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + + // clear read time + _read_time = 0; + + log_v("Reading time from endpoint %d", endpoint); + esp_zb_zcl_read_attr_cmd_req(&read_req); + + //Wait for response or timeout + if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) { + log_e("Error while reading time"); + return tm(); + } + + struct tm *timeinfo = localtime(&_read_time); + if (timeinfo) { + return *timeinfo; + } else { + log_e("Error while converting time"); + return tm(); + } +} + +int32_t ZigbeeEP::getTimezone(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) { + /* Read peer timezone */ + esp_zb_zcl_read_attr_cmd_t read_req; + + if (short_addr >= 0) { + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = (uint16_t)short_addr; + } else { + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); + } + + uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID}; + read_req.attr_number = ZB_ARRAY_LENTH(attributes); + read_req.attr_field = attributes; + + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TIME; + + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + + // clear read timezone + _read_timezone = 0; + + log_v("Reading timezone from endpoint %d", endpoint); + esp_zb_zcl_read_attr_cmd_req(&read_req); + + //Wait for response or timeout + if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) { + log_e("Error while reading timezone"); + } + + return _read_timezone; +} + +void ZigbeeEP::zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute) { + /* Time cluster attributes */ + if (attribute->id == ESP_ZB_ZCL_ATTR_TIME_TIME_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_UTC_TIME) { + log_v("Time attribute received"); + log_v("Time: %lld", *(uint32_t *)attribute->data.value); + _read_time = *(uint32_t *)attribute->data.value; + xSemaphoreGive(lock); + } else if (attribute->id == ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_S32) { + log_v("Timezone attribute received"); + log_v("Timezone: %d", *(int32_t *)attribute->data.value); + _read_timezone = *(int32_t *)attribute->data.value; + xSemaphoreGive(lock); + } +} + #endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index ac22143c4d7..eae81fafbc3 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -82,16 +82,27 @@ class ZigbeeEP { _allow_multiple_binding = bind; } - // Manufacturer name and model implemented + // Set Manufacturer name and model void setManufacturerAndModel(const char *name, const char *model); - void setPowerSource(zb_power_source_t power_source, uint8_t percentage = 255); - void setBatteryPercentage(uint8_t percentage); - void reportBatteryPercentage(); // Methods to read manufacturer and model name from selected endpoint and short address char *readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr); char *readModel(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr); + // Set Power source and battery percentage for battery powered devices + void setPowerSource(zb_power_source_t power_source, uint8_t percentage = 255); + void setBatteryPercentage(uint8_t percentage); + void reportBatteryPercentage(); + + // Set time + void addTimeCluster(tm time = {0}, int32_t gmt_offset = 0); // gmt offset in seconds + void setTime(tm time); + void setTimezone(int32_t gmt_offset); + + // Get time from Coordinator or specific endpoint (blocking until response) + struct tm getTime(uint8_t endpoint = 1, int32_t short_addr = 0x0000, esp_zb_ieee_addr_t ieee_addr = {0}); + int32_t getTimezone(uint8_t endpoint = 1, int32_t short_addr = 0x0000, esp_zb_ieee_addr_t ieee_addr = {0}); // gmt offset in seconds + bool epAllowMultipleBinding() { return _allow_multiple_binding; } @@ -104,6 +115,7 @@ class ZigbeeEP { virtual void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) {}; virtual void zbReadBasicCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented virtual void zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message); + virtual void zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented virtual void zbIASZoneStatusChangeNotification(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message) {}; @@ -120,6 +132,8 @@ class ZigbeeEP { char *_read_manufacturer; char *_read_model; void (*_on_identify)(uint16_t time); + time_t _read_time; + int32_t _read_timezone; protected: uint8_t _endpoint; diff --git a/libraries/Zigbee/src/ZigbeeHandlers.cpp b/libraries/Zigbee/src/ZigbeeHandlers.cpp index bccb27da7f8..3b4992d81db 100644 --- a/libraries/Zigbee/src/ZigbeeHandlers.cpp +++ b/libraries/Zigbee/src/ZigbeeHandlers.cpp @@ -105,6 +105,8 @@ static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_re if (variable->status == ESP_ZB_ZCL_STATUS_SUCCESS) { if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_BASIC) { (*it)->zbReadBasicCluster(&variable->attribute); //method zbReadBasicCluster implemented in the common EP class + } else if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_TIME) { + (*it)->zbReadTimeCluster(&variable->attribute); //method zbReadTimeCluster implemented in the common EP class } else { (*it)->zbAttributeRead(message->info.cluster, &variable->attribute); //method zbAttributeRead must be implemented in specific EP class } diff --git a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp index 5def43f4199..b4386b80386 100644 --- a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp @@ -11,7 +11,6 @@ esp_zb_cluster_list_t *zigbee_carbon_dioxide_sensor_clusters_create(zigbee_carbo esp_zb_cluster_list_add_carbon_dioxide_measurement_cluster( cluster_list, esp_zb_carbon_dioxide_measurement_cluster_create(carbon_dioxide_meas_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE ); - esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); return cluster_list; } diff --git a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp index 75196e78543..36d66840967 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp @@ -9,7 +9,6 @@ esp_zb_cluster_list_t *zigbee_flow_sensor_clusters_create(zigbee_flow_sensor_cfg esp_zb_cluster_list_add_basic_cluster(cluster_list, esp_zb_basic_cluster_create(basic_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(identify_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_flow_meas_cluster(cluster_list, esp_zb_flow_meas_cluster_create(flow_meas_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); - esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); return cluster_list; } diff --git a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp index 3a7acee040c..dec7910ac03 100644 --- a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp @@ -9,7 +9,6 @@ esp_zb_cluster_list_t *zigbee_occupancy_sensor_clusters_create(zigbee_occupancy_ esp_zb_cluster_list_add_basic_cluster(cluster_list, esp_zb_basic_cluster_create(basic_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(identify_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_occupancy_sensing_cluster(cluster_list, esp_zb_occupancy_sensing_cluster_create(occupancy_meas_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); - esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); return cluster_list; } diff --git a/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp b/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp index 24b4efb127e..b887680076d 100644 --- a/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp @@ -9,7 +9,6 @@ esp_zb_cluster_list_t *zigbee_pressure_sensor_clusters_create(zigbee_pressure_se esp_zb_cluster_list_add_basic_cluster(cluster_list, esp_zb_basic_cluster_create(basic_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(identify_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_pressure_meas_cluster(cluster_list, esp_zb_pressure_meas_cluster_create(pressure_meas_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); - esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); return cluster_list; } From 402ab56bf1d68f8fbc9788a168837b1534551390 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 30 Jan 2025 18:12:07 +0100 Subject: [PATCH 36/60] fix(SPIFFS): Use new disableWDT bool return value (#10909) Analogue to this PR, but for SPIFFS: https://github.com/espressif/arduino-esp32/pull/10896/files --- libraries/SPIFFS/src/SPIFFS.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/SPIFFS/src/SPIFFS.cpp b/libraries/SPIFFS/src/SPIFFS.cpp index a77b32990bf..0b2cc0d462d 100644 --- a/libraries/SPIFFS/src/SPIFFS.cpp +++ b/libraries/SPIFFS/src/SPIFFS.cpp @@ -91,9 +91,11 @@ void SPIFFSFS::end() { } bool SPIFFSFS::format() { - disableCore0WDT(); + bool wdt_active = disableCore0WDT(); esp_err_t err = esp_spiffs_format(partitionLabel_); - enableCore0WDT(); + if (wdt_active) { + enableCore0WDT(); + } if (err) { log_e("Formatting SPIFFS failed! Error: %d", err); return false; From f22866f888b713fa09c4b29b854a734478b407ad Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Thu, 30 Jan 2025 09:14:08 -0800 Subject: [PATCH 37/60] Update HTTPS certificate in BasicHttpsClient.ino (#10911) The certificate is outdated. The current certificate can be found using `openssl s_client -showcerts -connect jigsaw.w3.org:443` --- .../BasicHttpsClient/BasicHttpsClient.ino | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino b/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino index eb6caef0724..73e127d1261 100644 --- a/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino +++ b/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino @@ -14,30 +14,32 @@ #include -// This is a Baltimore CyberTrust cert, the root Certificate Authority that +// This is a Google Trust Services cert, the root Certificate Authority that // signed the server certificate for the demo server https://jigsaw.w3.org in this -// example. This certificate is valid until Mon, 12 May 2025 23:59:00 GMT -const char *rootCACertificate = "-----BEGIN CERTIFICATE-----\n" - "MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ\n" - "RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD\n" - "VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX\n" - "DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y\n" - "ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy\n" - "VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr\n" - "mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr\n" - "IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK\n" - "mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu\n" - "XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy\n" - "dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye\n" - "jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1\n" - "BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3\n" - "DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92\n" - "9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx\n" - "jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0\n" - "Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz\n" - "ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS\n" - "R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\n" - "-----END CERTIFICATE-----\n"; +// example. This certificate is valid until Jan 28 00:00:42 2028 GMT +const char *rootCACertificate = R"string_literal( +-----BEGIN CERTIFICATE----- +MIIDejCCAmKgAwIBAgIQf+UwvzMTQ77dghYQST2KGzANBgkqhkiG9w0BAQsFADBX +MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UE +CxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTIzMTEx +NTAzNDMyMVoXDTI4MDEyODAwMDA0MlowRzELMAkGA1UEBhMCVVMxIjAgBgNVBAoT +GUdvb2dsZSBUcnVzdCBTZXJ2aWNlcyBMTEMxFDASBgNVBAMTC0dUUyBSb290IFI0 +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE83Rzp2iLYK5DuDXFgTB7S0md+8Fhzube +Rr1r1WEYNa5A3XP3iZEwWus87oV8okB2O6nGuEfYKueSkWpz6bFyOZ8pn6KY019e +WIZlD6GEZQbR3IvJx3PIjGov5cSr0R2Ko4H/MIH8MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUgEzW63T/STaj1dj8tT7FavCUHYwwHwYDVR0jBBgwFoAUYHtmGkUN +l8qJUC99BM00qP/8/UswNgYIKwYBBQUHAQEEKjAoMCYGCCsGAQUFBzAChhpodHRw +Oi8vaS5wa2kuZ29vZy9nc3IxLmNydDAtBgNVHR8EJjAkMCKgIKAehhxodHRwOi8v +Yy5wa2kuZ29vZy9yL2dzcjEuY3JsMBMGA1UdIAQMMAowCAYGZ4EMAQIBMA0GCSqG +SIb3DQEBCwUAA4IBAQAYQrsPBtYDh5bjP2OBDwmkoWhIDDkic574y04tfzHpn+cJ +odI2D4SseesQ6bDrarZ7C30ddLibZatoKiws3UL9xnELz4ct92vID24FfVbiI1hY ++SW6FoVHkNeWIP0GCbaM4C6uVdF5dTUsMVs/ZbzNnIdCp5Gxmx5ejvEau8otR/Cs +kGN+hr/W5GvT1tMBjgWKZ1i4//emhA1JG1BbPzoLJQvyEotc03lXjTaCzv8mEbep +8RqZ7a2CPsgRbuvTPBwcOMBBmuFeU88+FSBX6+7iP0il8b4Z0QFqIwwMHfs/L6K1 +vepuoxtGzi4CZ68zJpiq1UvSqTbFJjtbD4seiMHl +-----END CERTIFICATE----- +)string_literal"; // Not sure if NetworkClientSecure checks the validity date of the certificate. // Setting clock just to be sure... From 0302b4db4741ae51ff45a3b1b533175ee0c0d92c Mon Sep 17 00:00:00 2001 From: "Sayed (Tashfi) Nowroz" Date: Fri, 31 Jan 2025 16:25:45 -0500 Subject: [PATCH 38/60] fix(logging): incorrect FPS logging (#10921) * fix(logging): Corrected FPS calculation Previously, last_frame was only updated once at the beginning of stream_handler, leading to incorrect FPS and avg_frame_time computation. This commit ensures last_frame is updated on each iteration after last FPS computation, resulting in accurate FPS logging. Fixes #10920 * Revert "fix(logging): Corrected FPS calculation" This reverts commit 0bb7b9555e7661c72dc3376cf8a001c6fd3758c8. * fix(loggin): Incorrect FPS computation fixed Corrected and tested change in FPS computation, suggested by @me-no-dev and found working with correct numbers. Previously, last_frame was only updated once at the beginning of stream_handler, leading to incorrect FPS and avg_frame_time computation. This commit ensures last_frame is updated on each iteration after last FPS computation, resulting in accurate FPS logging. Fixes #10920 --- libraries/ESP32/examples/Camera/CameraWebServer/app_httpd.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/ESP32/examples/Camera/CameraWebServer/app_httpd.cpp b/libraries/ESP32/examples/Camera/CameraWebServer/app_httpd.cpp index 6b62ee9b6cf..81d643e37ac 100644 --- a/libraries/ESP32/examples/Camera/CameraWebServer/app_httpd.cpp +++ b/libraries/ESP32/examples/Camera/CameraWebServer/app_httpd.cpp @@ -281,6 +281,8 @@ static esp_err_t stream_handler(httpd_req_t *req) { int64_t fr_end = esp_timer_get_time(); int64_t frame_time = fr_end - last_frame; + last_frame = fr_end; + frame_time /= 1000; #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO uint32_t avg_frame_time = ra_filter_run(&ra_filter, frame_time); From 1c01fcd196ad8278feafe35bc785999db72a5425 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Tue, 4 Feb 2025 06:04:58 -0300 Subject: [PATCH 39/60] fix(uart): fixed esp32s2 uart ci test case (#10926) Wrong pins were used for baud rate detection test case, therefore no data was reaching UART1 RX FIFO from UART0 TX loopback. --- tests/validation/uart/uart.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/validation/uart/uart.ino b/tests/validation/uart/uart.ino index 27bd95da7f8..358276c00b4 100644 --- a/tests/validation/uart/uart.ino +++ b/tests/validation/uart/uart.ino @@ -399,7 +399,8 @@ void auto_baudrate_test(void) { if (TEST_UART_NUM == 1) { selected_serial = &Serial1; - uart_internal_loopback(0, RX1); + // UART1 pins were swapped because of ESP32-P4 + uart_internal_loopback(0, /*RX1*/ TX1); } else { #ifdef RX2 selected_serial = &Serial2; From 2040cbad51b7e9917729244f8781b66786ddd50c Mon Sep 17 00:00:00 2001 From: Akashdeep Deb Date: Tue, 4 Feb 2025 09:24:49 +0000 Subject: [PATCH 40/60] Update README.md to add ESP-SR (#10925) Adding ESP-SR to the documentation Co-authored-by: Me No Dev --- libraries/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/README.md b/libraries/README.md index 38765f99b52..aee5bb07614 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -88,6 +88,9 @@ arduino-esp32 includes libraries for Arduino compatibility along with some objec ### SPIFFS SPI Flash Filesystem (see [spiffs-plugin](https://github.com/me-no-dev/arduino-esp32fs-plugin) to upload to device) +### SR + ESP-SR helps users build AI speech solutions based on ESP32-S3 or ESP32-P4 chips + ### Ticker A timer to call functions on an interval From 6eb99d3ae32966bb985853fdf73d6da2e34fd565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:25:11 +0100 Subject: [PATCH 41/60] feat(zigbee): Add IAS Zone endpoints (Contact Switch + Door/Window Handle) (#10918) * feat(zigbee): Add IAS Zone endpoints * ci(pre-commit): Apply automatic fixes * fix(ci): Typo fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- CMakeLists.txt | 2 + .../examples/Zigbee_Contact_Switch/README.md | 58 ++++++++++ .../Zigbee_Contact_Switch.ino | 100 ++++++++++++++++ .../examples/Zigbee_Contact_Switch/ci.json | 6 + libraries/Zigbee/src/Zigbee.h | 2 + libraries/Zigbee/src/ZigbeeEP.h | 1 + libraries/Zigbee/src/ZigbeeHandlers.cpp | 22 ++++ .../Zigbee/src/ep/ZigbeeContactSwitch.cpp | 96 ++++++++++++++++ libraries/Zigbee/src/ep/ZigbeeContactSwitch.h | 67 +++++++++++ .../Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp | 108 ++++++++++++++++++ .../Zigbee/src/ep/ZigbeeDoorWindowHandle.h | 71 ++++++++++++ 11 files changed, 533 insertions(+) create mode 100644 libraries/Zigbee/examples/Zigbee_Contact_Switch/README.md create mode 100644 libraries/Zigbee/examples/Zigbee_Contact_Switch/Zigbee_Contact_Switch.ino create mode 100644 libraries/Zigbee/examples/Zigbee_Contact_Switch/ci.json create mode 100644 libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp create mode 100644 libraries/Zigbee/src/ep/ZigbeeContactSwitch.h create mode 100644 libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp create mode 100644 libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fd23d14d77..b3cb6c06cdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,6 +290,8 @@ set(ARDUINO_LIBRARY_Zigbee_SRCS libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp + libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp + libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp ) set(ARDUINO_LIBRARY_BLE_SRCS diff --git a/libraries/Zigbee/examples/Zigbee_Contact_Switch/README.md b/libraries/Zigbee/examples/Zigbee_Contact_Switch/README.md new file mode 100644 index 00000000000..a5a32358a7c --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Contact_Switch/README.md @@ -0,0 +1,58 @@ +# Arduino-ESP32 Zigbee Contact Switch Example + +This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) contact switch (IAS Zone), +that can be used for example as window/door sensor having 2 states - closed/open. + +# Supported Targets + +Currently, this example supports the following targets. + +| Supported Targets | ESP32-C6 | ESP32-H2 | +| ----------------- | -------- | -------- | + +## Hardware Required + +* A USB cable for power supply and programming + +### Configure the Project + +Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2). +Set the Sensor GPIO by changing the `sensor_pin` variable. + +#### Using Arduino IDE + +To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits). + +* Before Compile/Verify, select the correct board: `Tools -> Board`. +* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)` +* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs` +* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port. +* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`. + +## Troubleshooting + +If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. + +***Important: Make sure you are using a good quality USB cable and that you have a reliable power source*** + +* **LED not blinking:** Check the wiring connection and the IO selection. +* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed. +* **COM port not detected:** Check the USB cable and the USB to Serial driver installation. + +If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute). + +## Contribute + +To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst) + +If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome! + +Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else. + +## Resources + +* Official ESP32 Forum: [Link](https://esp32.com) +* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32) +* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf) +* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf) +* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com) diff --git a/libraries/Zigbee/examples/Zigbee_Contact_Switch/Zigbee_Contact_Switch.ino b/libraries/Zigbee/examples/Zigbee_Contact_Switch/Zigbee_Contact_Switch.ino new file mode 100644 index 00000000000..ce9eedb683d --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Contact_Switch/Zigbee_Contact_Switch.ino @@ -0,0 +1,100 @@ +// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @brief This example demonstrates Zigbee contact switch (IAS Zone). + * + * The example demonstrates how to use Zigbee library to create a end device contact switch. + * The contact switch is a Zigbee end device, which is reporting data to the Zigbee network. + * + * Proper Zigbee mode must be selected in Tools->Zigbee mode + * and also the correct partition scheme must be selected in Tools->Partition Scheme. + * + * Please check the README.md for instructions and more detailed description. + * + * Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/) + */ + +#ifndef ZIGBEE_MODE_ED +#error "Zigbee end device mode is not selected in Tools->Zigbee mode" +#endif + +#include "Zigbee.h" + +/* Zigbee contact sensor configuration */ +#define CONTACT_SWITCH_ENDPOINT_NUMBER 10 +uint8_t button = BOOT_PIN; +uint8_t sensor_pin = 4; + +ZigbeeContactSwitch zbContactSwitch = ZigbeeContactSwitch(CONTACT_SWITCH_ENDPOINT_NUMBER); + +void setup() { + Serial.begin(115200); + + // Init button + switch + pinMode(button, INPUT_PULLUP); + pinMode(sensor_pin, INPUT_PULLUP); + + // Optional: set Zigbee device name and model + zbContactSwitch.setManufacturerAndModel("Espressif", "ZigbeeContactSwitch"); + + // Add endpoint to Zigbee Core + Zigbee.addEndpoint(&zbContactSwitch); + + Serial.println("Starting Zigbee..."); + // When all EPs are registered, start Zigbee in End Device mode + if (!Zigbee.begin()) { + Serial.println("Zigbee failed to start!"); + Serial.println("Rebooting..."); + ESP.restart(); + } else { + Serial.println("Zigbee started successfully!"); + } + Serial.println("Connecting to network"); + while (!Zigbee.connected()) { + Serial.print("."); + delay(100); + } + Serial.println(); +} + +void loop() { + // Checking pin for contact change + static bool contact = false; + if (digitalRead(sensor_pin) == HIGH && !contact) { + // Update contact sensor value + zbContactSwitch.setOpen(); + contact = true; + } else if (digitalRead(sensor_pin) == LOW && contact) { + zbContactSwitch.setClosed(); + contact = false; + } + + // Checking button for factory reset + if (digitalRead(button) == LOW) { // Push button pressed + // Key debounce handling + delay(100); + int startTime = millis(); + while (digitalRead(button) == LOW) { + delay(50); + if ((millis() - startTime) > 3000) { + // If key pressed for more than 3secs, factory reset Zigbee and reboot + Serial.println("Resetting Zigbee to factory and rebooting in 1s."); + delay(1000); + Zigbee.factoryReset(); + } + } + } + delay(100); +} diff --git a/libraries/Zigbee/examples/Zigbee_Contact_Switch/ci.json b/libraries/Zigbee/examples/Zigbee_Contact_Switch/ci.json new file mode 100644 index 00000000000..7b7ccef8ed7 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Contact_Switch/ci.json @@ -0,0 +1,6 @@ +{ + "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", + "requires": [ + "CONFIG_SOC_IEEE802154_SUPPORTED=y" + ] +} diff --git a/libraries/Zigbee/src/Zigbee.h b/libraries/Zigbee/src/Zigbee.h index cf58dc8d5d8..c97be9378a8 100644 --- a/libraries/Zigbee/src/Zigbee.h +++ b/libraries/Zigbee/src/Zigbee.h @@ -18,3 +18,5 @@ #include "ep/ZigbeeFlowSensor.h" #include "ep/ZigbeeOccupancySensor.h" #include "ep/ZigbeeCarbonDioxideSensor.h" +#include "ep/ZigbeeContactSwitch.h" +#include "ep/ZigbeeDoorWindowHandle.h" diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index eae81fafbc3..069db95aac0 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -118,6 +118,7 @@ class ZigbeeEP { virtual void zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented virtual void zbIASZoneStatusChangeNotification(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message) {}; + virtual void zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) {}; virtual void addBoundDevice(zb_device_params_t *device) { _bound_devices.push_back(device); diff --git a/libraries/Zigbee/src/ZigbeeHandlers.cpp b/libraries/Zigbee/src/ZigbeeHandlers.cpp index 3b4992d81db..52c38eb0633 100644 --- a/libraries/Zigbee/src/ZigbeeHandlers.cpp +++ b/libraries/Zigbee/src/ZigbeeHandlers.cpp @@ -10,6 +10,7 @@ static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_mes static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_resp_message_t *message); static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_report_resp_message_t *message); static esp_err_t zb_cmd_ias_zone_status_change_handler(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message); +static esp_err_t zb_cmd_ias_zone_enroll_response_handler(const esp_zb_zcl_ias_zone_enroll_response_message_t *message); static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message); // Zigbee action handlers @@ -24,6 +25,9 @@ static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, case ESP_ZB_CORE_CMD_IAS_ZONE_ZONE_STATUS_CHANGE_NOT_ID: ret = zb_cmd_ias_zone_status_change_handler((esp_zb_zcl_ias_zone_status_change_notification_message_t *)message); break; + case ESP_ZB_CORE_IAS_ZONE_ENROLL_RESPONSE_VALUE_CB_ID: + ret = zb_cmd_ias_zone_enroll_response_handler((esp_zb_zcl_ias_zone_enroll_response_message_t *)message); + break; case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; } @@ -160,6 +164,24 @@ static esp_err_t zb_cmd_ias_zone_status_change_handler(const esp_zb_zcl_ias_zone return ESP_OK; } +static esp_err_t zb_cmd_ias_zone_enroll_response_handler(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) { + if (!message) { + log_e("Empty message"); + return ESP_FAIL; + } + if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Received message: error status(%d)", message->info.status); + return ESP_ERR_INVALID_ARG; + } + log_v("IAS Zone Enroll Response received"); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + if (message->info.dst_endpoint == (*it)->getEndpoint()) { + (*it)->zbIASZoneEnrollResponse(message); + } + } + return ESP_OK; +} + static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message) { if (!message) { log_e("Empty message"); diff --git a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp new file mode 100644 index 00000000000..9a3551b0581 --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp @@ -0,0 +1,96 @@ +#include "ZigbeeContactSwitch.h" +#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED + +esp_zb_cluster_list_t *zigbee_contact_switch_clusters_create(zigbee_contact_switch_cfg_t *contact_switch) { + esp_zb_basic_cluster_cfg_t *basic_cfg = contact_switch ? &(contact_switch->basic_cfg) : NULL; + esp_zb_identify_cluster_cfg_t *identify_cfg = contact_switch ? &(contact_switch->identify_cfg) : NULL; + esp_zb_ias_zone_cluster_cfg_t *ias_zone_cfg = contact_switch ? &(contact_switch->ias_zone_cfg) : NULL; + esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create(); + esp_zb_cluster_list_add_basic_cluster(cluster_list, esp_zb_basic_cluster_create(basic_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(identify_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_ias_zone_cluster(cluster_list, esp_zb_ias_zone_cluster_create(ias_zone_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + return cluster_list; +} + +ZigbeeContactSwitch::ZigbeeContactSwitch(uint8_t endpoint) : ZigbeeEP(endpoint) { + _device_id = ESP_ZB_HA_IAS_ZONE_ID; + _zone_status = 0; + _zone_id = 0xff; + _ias_cie_endpoint = 1; + + //Create custom contact switch configuration + zigbee_contact_switch_cfg_t contact_switch_cfg = ZIGBEE_DEFAULT_CONTACT_SWITCH_CONFIG(); + _cluster_list = zigbee_contact_switch_clusters_create(&contact_switch_cfg); + + _ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_IAS_ZONE_ID, .app_device_version = 0}; +} + +void ZigbeeContactSwitch::setIASClientEndpoint(uint8_t ep_number) { + _ias_cie_endpoint = ep_number; +} + +void ZigbeeContactSwitch::setClosed() { + log_v("Setting Contact switch to closed"); + uint8_t closed = 0; // ALARM1 = 0, ALARM2 = 0 + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONESTATUS_ID, &closed, false + ); + esp_zb_lock_release(); + _zone_status = closed; + report(); +} + +void ZigbeeContactSwitch::setOpen() { + log_v("Setting Contact switch to open"); + uint8_t open = ESP_ZB_ZCL_IAS_ZONE_ZONE_STATUS_ALARM1 | ESP_ZB_ZCL_IAS_ZONE_ZONE_STATUS_ALARM2; // ALARM1 = 1, ALARM2 = 1 + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONESTATUS_ID, &open, false); + esp_zb_lock_release(); + _zone_status = open; + report(); +} + +void ZigbeeContactSwitch::report() { + /* Send IAS Zone status changed notification command */ + + esp_zb_zcl_ias_zone_status_change_notif_cmd_t status_change_notif_cmd; + status_change_notif_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + status_change_notif_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + status_change_notif_cmd.zcl_basic_cmd.dst_endpoint = _ias_cie_endpoint; //default is 1 + memcpy(status_change_notif_cmd.zcl_basic_cmd.dst_addr_u.addr_long, _ias_cie_addr, sizeof(esp_zb_ieee_addr_t)); + + status_change_notif_cmd.zone_status = _zone_status; + status_change_notif_cmd.extend_status = 0; + status_change_notif_cmd.zone_id = _zone_id; + status_change_notif_cmd.delay = 0; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); + esp_zb_lock_release(); + log_v("IAS Zone status changed notification sent"); +} + +void ZigbeeContactSwitch::zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) { + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE) { + log_v("IAS Zone Enroll Response: zone id(%d), status(%d)", message->zone_id, message->response_code); + if (message->response_code == ESP_ZB_ZCL_IAS_ZONE_ENROLL_RESPONSE_CODE_SUCCESS) { + log_v("IAS Zone Enroll Response: success"); + esp_zb_lock_acquire(portMAX_DELAY); + memcpy( + _ias_cie_addr, + (*(esp_zb_ieee_addr_t *) + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_IAS_CIE_ADDRESS_ID) + ->data_p), + sizeof(esp_zb_ieee_addr_t) + ); + esp_zb_lock_release(); + _zone_id = message->zone_id; + } + + } else { + log_w("Received message ignored. Cluster ID: %d not supported for On/Off Light", message->info.cluster); + } +} + +#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h new file mode 100644 index 00000000000..692caf092ba --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h @@ -0,0 +1,67 @@ +/* Class of Zigbee contact switch (IAS Zone) endpoint inherited from common EP class */ + +#pragma once + +#include "soc/soc_caps.h" +#include "sdkconfig.h" +#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED + +#include "ZigbeeEP.h" +#include "ha/esp_zigbee_ha_standard.h" + +// clang-format off +#define ZIGBEE_DEFAULT_CONTACT_SWITCH_CONFIG() \ + { \ + .basic_cfg = \ + { \ + .zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE, \ + .power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE, \ + }, \ + .identify_cfg = \ + { \ + .identify_time = ESP_ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE, \ + }, \ + .ias_zone_cfg = \ + { \ + .zone_state = ESP_ZB_ZCL_IAS_ZONE_ZONESTATE_NOT_ENROLLED, \ + .zone_type = ESP_ZB_ZCL_IAS_ZONE_ZONETYPE_CONTACT_SWITCH, \ + .zone_status = 0, \ + .ias_cie_addr = ESP_ZB_ZCL_ZONE_IAS_CIE_ADDR_DEFAULT, \ + .zone_id = 0xff, \ + .zone_ctx = {0, 0, 0, 0}, \ + }, \ + } +// clang-format on + +typedef struct zigbee_contact_switch_cfg_s { + esp_zb_basic_cluster_cfg_t basic_cfg; + esp_zb_identify_cluster_cfg_t identify_cfg; + esp_zb_ias_zone_cluster_cfg_t ias_zone_cfg; +} zigbee_contact_switch_cfg_t; + +class ZigbeeContactSwitch : public ZigbeeEP { +public: + ZigbeeContactSwitch(uint8_t endpoint); + ~ZigbeeContactSwitch() {} + + // Set the IAS Client endpoint number (default is 1) + void setIASClientEndpoint(uint8_t ep_number); + + // Set the contact switch value to closed + void setClosed(); + + // Set the contact switch value to open + void setOpen(); + + // Report the contact switch value, done automatically after setting the position + void report(); + +private: + void zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) override; + uint8_t _zone_status; + uint8_t _zone_id; + esp_zb_ieee_addr_t _ias_cie_addr; + uint8_t _ias_cie_endpoint; +}; + +#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp new file mode 100644 index 00000000000..364b3ef6d9d --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp @@ -0,0 +1,108 @@ +#include "ZigbeeDoorWindowHandle.h" +#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED + +esp_zb_cluster_list_t *zigbee_door_window_handle_clusters_create(zigbee_door_window_handle_cfg_t *door_window_handle) { + esp_zb_basic_cluster_cfg_t *basic_cfg = door_window_handle ? &(door_window_handle->basic_cfg) : NULL; + esp_zb_identify_cluster_cfg_t *identify_cfg = door_window_handle ? &(door_window_handle->identify_cfg) : NULL; + esp_zb_ias_zone_cluster_cfg_t *ias_zone_cfg = door_window_handle ? &(door_window_handle->ias_zone_cfg) : NULL; + esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create(); + esp_zb_cluster_list_add_basic_cluster(cluster_list, esp_zb_basic_cluster_create(basic_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(identify_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_ias_zone_cluster(cluster_list, esp_zb_ias_zone_cluster_create(ias_zone_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + return cluster_list; +} + +ZigbeeDoorWindowHandle::ZigbeeDoorWindowHandle(uint8_t endpoint) : ZigbeeEP(endpoint) { + _device_id = ESP_ZB_HA_IAS_ZONE_ID; + _zone_status = 0; + _zone_id = 0xff; + _ias_cie_endpoint = 1; + + //Create custom door window handle configuration + zigbee_door_window_handle_cfg_t door_window_handle_cfg = ZIGBEE_DEFAULT_DOOR_WINDOW_HANDLE_CONFIG(); + _cluster_list = zigbee_door_window_handle_clusters_create(&door_window_handle_cfg); + + _ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_IAS_ZONE_ID, .app_device_version = 0}; +} + +void ZigbeeDoorWindowHandle::setIASClientEndpoint(uint8_t ep_number) { + _ias_cie_endpoint = ep_number; +} + +void ZigbeeDoorWindowHandle::setClosed() { + log_v("Setting Door/Window handle to closed"); + uint8_t closed = 0; // ALARM1 = 0, ALARM2 = 0 + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONESTATUS_ID, &closed, false + ); + esp_zb_lock_release(); + _zone_status = closed; + report(); +} + +void ZigbeeDoorWindowHandle::setOpen() { + log_v("Setting Door/Window handle to open"); + uint8_t open = ESP_ZB_ZCL_IAS_ZONE_ZONE_STATUS_ALARM1 | ESP_ZB_ZCL_IAS_ZONE_ZONE_STATUS_ALARM2; // ALARM1 = 1, ALARM2 = 1 + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONESTATUS_ID, &open, false); + esp_zb_lock_release(); + _zone_status = open; + report(); +} + +void ZigbeeDoorWindowHandle::setTilted() { + log_v("Setting Door/Window handle to tilted"); + uint8_t tilted = ESP_ZB_ZCL_IAS_ZONE_ZONE_STATUS_ALARM1; // ALARM1 = 1, ALARM2 = 0 + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONESTATUS_ID, &tilted, false + ); + esp_zb_lock_release(); + _zone_status = tilted; + report(); +} + +void ZigbeeDoorWindowHandle::report() { + /* Send IAS Zone status changed notification command */ + + esp_zb_zcl_ias_zone_status_change_notif_cmd_t status_change_notif_cmd; + status_change_notif_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + status_change_notif_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + status_change_notif_cmd.zcl_basic_cmd.dst_endpoint = _ias_cie_endpoint; //default is 1 + memcpy(status_change_notif_cmd.zcl_basic_cmd.dst_addr_u.addr_long, _ias_cie_addr, sizeof(esp_zb_ieee_addr_t)); + + status_change_notif_cmd.zone_status = _zone_status; + status_change_notif_cmd.extend_status = 0; + status_change_notif_cmd.zone_id = _zone_id; + status_change_notif_cmd.delay = 0; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); + esp_zb_lock_release(); + log_v("IAS Zone status changed notification sent"); +} + +void ZigbeeDoorWindowHandle::zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) { + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE) { + log_v("IAS Zone Enroll Response: zone id(%d), status(%d)", message->zone_id, message->response_code); + if (message->response_code == ESP_ZB_ZCL_IAS_ZONE_ENROLL_RESPONSE_CODE_SUCCESS) { + log_v("IAS Zone Enroll Response: success"); + esp_zb_lock_acquire(portMAX_DELAY); + memcpy( + _ias_cie_addr, + (*(esp_zb_ieee_addr_t *) + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_IAS_CIE_ADDRESS_ID) + ->data_p), + sizeof(esp_zb_ieee_addr_t) + ); + esp_zb_lock_release(); + _zone_id = message->zone_id; + } + + } else { + log_w("Received message ignored. Cluster ID: %d not supported for On/Off Light", message->info.cluster); + } +} + +#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h new file mode 100644 index 00000000000..e60316f8764 --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h @@ -0,0 +1,71 @@ +/* Class of Zigbee door window handle (IAS Zone) endpoint inherited from common EP class */ + +#pragma once + +#include "soc/soc_caps.h" +#include "sdkconfig.h" +#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED + +#include "ZigbeeEP.h" +#include "ha/esp_zigbee_ha_standard.h" + +#define ESP_ZB_ZCL_IAS_ZONE_ZONETYPE_DOOR_WINDOW_HANDLE 0x0016 +// clang-format off +#define ZIGBEE_DEFAULT_DOOR_WINDOW_HANDLE_CONFIG() \ + { \ + .basic_cfg = \ + { \ + .zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE, \ + .power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE, \ + }, \ + .identify_cfg = \ + { \ + .identify_time = ESP_ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE, \ + }, \ + .ias_zone_cfg = \ + { \ + .zone_state = ESP_ZB_ZCL_IAS_ZONE_ZONESTATE_NOT_ENROLLED, \ + .zone_type = ESP_ZB_ZCL_IAS_ZONE_ZONETYPE_DOOR_WINDOW_HANDLE, \ + .zone_status = 0, \ + .ias_cie_addr = ESP_ZB_ZCL_ZONE_IAS_CIE_ADDR_DEFAULT, \ + .zone_id = 0xff, \ + .zone_ctx = {0, 0, 0, 0}, \ + }, \ + } +// clang-format on + +typedef struct zigbee_door_window_handle_cfg_s { + esp_zb_basic_cluster_cfg_t basic_cfg; + esp_zb_identify_cluster_cfg_t identify_cfg; + esp_zb_ias_zone_cluster_cfg_t ias_zone_cfg; +} zigbee_door_window_handle_cfg_t; + +class ZigbeeDoorWindowHandle : public ZigbeeEP { +public: + ZigbeeDoorWindowHandle(uint8_t endpoint); + ~ZigbeeDoorWindowHandle() {} + + // Set the IAS Client endpoint number (default is 1) + void setIASClientEndpoint(uint8_t ep_number); + + // Set the door/window handle value to closed + void setClosed(); + + // Set the door/window handle value to open + void setOpen(); + + // Set the door/window handle value to tilted + void setTilted(); + + // Report the door/window handle value, done automatically after setting the position + void report(); + +private: + void zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) override; + uint8_t _zone_status; + uint8_t _zone_id; + esp_zb_ieee_addr_t _ias_cie_addr; + uint8_t _ias_cie_endpoint; +}; + +#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED From b385562fa601d80633d5d0389e059a5b8ba0f2b7 Mon Sep 17 00:00:00 2001 From: thekurtovic <40248206+thekurtovic@users.noreply.github.com> Date: Tue, 4 Feb 2025 04:52:45 -0500 Subject: [PATCH 42/60] NetworkEvents allow stack size to be changed. (#10805) * feat(net): Allow for event task custom stack size * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: Me No Dev Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- libraries/Network/src/NetworkEvents.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/Network/src/NetworkEvents.cpp b/libraries/Network/src/NetworkEvents.cpp index 1a7177bae61..161e55c5d01 100644 --- a/libraries/Network/src/NetworkEvents.cpp +++ b/libraries/Network/src/NetworkEvents.cpp @@ -8,6 +8,10 @@ #include "esp_task.h" #include "esp32-hal.h" +#ifndef ARDUINO_NETWORK_EVENT_TASK_STACK_SIZE +#define ARDUINO_NETWORK_EVENT_TASK_STACK_SIZE 4096 +#endif + NetworkEvents::NetworkEvents() : _arduino_event_group(NULL), _arduino_event_queue(NULL), _arduino_event_task_handle(NULL) {} NetworkEvents::~NetworkEvents() { @@ -61,8 +65,8 @@ bool NetworkEvents::initNetworkEvents() { [](void *self) { static_cast(self)->_checkForEvent(); }, - "arduino_events", // label - 4096, // event task's stack size + "arduino_events", // label + ARDUINO_NETWORK_EVENT_TASK_STACK_SIZE, // event task's stack size this, ESP_TASKD_EVENT_PRIO - 1, &_arduino_event_task_handle, ARDUINO_EVENT_RUNNING_CORE ); if (!_arduino_event_task_handle) { From 8b31d1e1a86c0b3bacc59ab1dacc22ee3c6507c0 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 4 Feb 2025 12:53:29 +0200 Subject: [PATCH 43/60] fix(ci): Disable some RainMaker examples on ESP32 (#10931) --- libraries/RainMaker/examples/RMakerCustomAirCooler/ci.json | 3 +++ libraries/RainMaker/examples/RMakerSonoffDualR3/ci.json | 3 +++ 2 files changed, 6 insertions(+) diff --git a/libraries/RainMaker/examples/RMakerCustomAirCooler/ci.json b/libraries/RainMaker/examples/RMakerCustomAirCooler/ci.json index 1c80eda1d90..ce63fe9ccf0 100644 --- a/libraries/RainMaker/examples/RMakerCustomAirCooler/ci.json +++ b/libraries/RainMaker/examples/RMakerCustomAirCooler/ci.json @@ -1,4 +1,7 @@ { + "targets": { + "esp32": false + }, "fqbn_append": "PartitionScheme=rainmaker_4MB", "requires": [ "CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK=[1-9][0-9]*" diff --git a/libraries/RainMaker/examples/RMakerSonoffDualR3/ci.json b/libraries/RainMaker/examples/RMakerSonoffDualR3/ci.json index 1c80eda1d90..ce63fe9ccf0 100644 --- a/libraries/RainMaker/examples/RMakerSonoffDualR3/ci.json +++ b/libraries/RainMaker/examples/RMakerSonoffDualR3/ci.json @@ -1,4 +1,7 @@ { + "targets": { + "esp32": false + }, "fqbn_append": "PartitionScheme=rainmaker_4MB", "requires": [ "CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK=[1-9][0-9]*" From 6a1127625c1e12f222435dafbfa56aacc2098c97 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 4 Feb 2025 13:22:20 +0200 Subject: [PATCH 44/60] fix(ota): Make sure that ArduinoOTA.end() is called in the destructor (#10932) --- libraries/ArduinoOTA/src/ArduinoOTA.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ArduinoOTA/src/ArduinoOTA.cpp b/libraries/ArduinoOTA/src/ArduinoOTA.cpp index 0ab5466ab20..cb3ddc1e797 100644 --- a/libraries/ArduinoOTA/src/ArduinoOTA.cpp +++ b/libraries/ArduinoOTA/src/ArduinoOTA.cpp @@ -29,7 +29,7 @@ ArduinoOTAClass::ArduinoOTAClass() _start_callback(NULL), _end_callback(NULL), _error_callback(NULL), _progress_callback(NULL) {} ArduinoOTAClass::~ArduinoOTAClass() { - _udp_ota.stop(); + end(); } ArduinoOTAClass &ArduinoOTAClass::onStart(THandlerFunction fn) { From db0bbad9348f7d9bd0ce79152f004c61daf8793a Mon Sep 17 00:00:00 2001 From: Henning Kulander Date: Tue, 4 Feb 2025 16:52:02 +0100 Subject: [PATCH 45/60] Created Zigbee Endpoint for Window Covering. (#10914) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(zigbee): Added Endpoint for Window Covering (#10913) * Added example Sketch. * Added window covering to CMakeLists.txt. * feat(zigbee): Window covering tilt support and fixes * fix(zigbee): Fix typos * ci(pre-commit): Apply automatic fixes * fix(ci): Fixes of typos --------- Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- CMakeLists.txt | 1 + .../examples/Zigbee_Window_Covering/README.md | 69 ++++ .../Zigbee_Window_Covering.ino | 198 +++++++++++ .../examples/Zigbee_Window_Covering/ci.json | 6 + libraries/Zigbee/src/Zigbee.h | 1 + libraries/Zigbee/src/ZigbeeEP.h | 2 +- libraries/Zigbee/src/ZigbeeHandlers.cpp | 26 ++ .../Zigbee/src/ep/ZigbeeWindowCovering.cpp | 308 ++++++++++++++++++ .../Zigbee/src/ep/ZigbeeWindowCovering.h | 147 +++++++++ 9 files changed, 757 insertions(+), 1 deletion(-) create mode 100644 libraries/Zigbee/examples/Zigbee_Window_Covering/README.md create mode 100644 libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino create mode 100644 libraries/Zigbee/examples/Zigbee_Window_Covering/ci.json create mode 100644 libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp create mode 100644 libraries/Zigbee/src/ep/ZigbeeWindowCovering.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b3cb6c06cdc..40567d1b45c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,6 +292,7 @@ set(ARDUINO_LIBRARY_Zigbee_SRCS libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp + libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp ) set(ARDUINO_LIBRARY_BLE_SRCS diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/README.md b/libraries/Zigbee/examples/Zigbee_Window_Covering/README.md new file mode 100644 index 00000000000..469560a0e0c --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Window_Covering/README.md @@ -0,0 +1,69 @@ +# Arduino-ESP32 Window Covering Example + +This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) window covering device. + +To see if the communication with your Zigbee network works, use the Serial monitor and watch for output there. + +# Supported Targets + +Currently, this example supports the following targets. + +| Supported Targets | ESP32-C6 | ESP32-H2 | +| ----------------- | -------- | -------- | + +## Hardware Required + +* A USB cable for power supply and programming +* Board (ESP32-H2 or ESP32-C6) as Zigbee end device and upload the Zigbee_Window_Covering example +* Zigbee network / coordinator (Other board with switch examples or Zigbee2mqtt or ZigbeeHomeAssistant like application) + +### Configure the Project + +#### Using Arduino IDE + +To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits). + +* Before Compile/Verify, select the correct board: `Tools -> Board`. +* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)` +* Select Tools / USB CDC On Boot: "Enabled" +* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs` +* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port. +* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`. + +## Troubleshooting + +If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator. +You can do the following: + +* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`. +* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack. + +By default, the coordinator network is closed after rebooting or flashing new firmware. +To open the network you have 2 options: + +* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`. +* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join. + +***Important: Make sure you are using a good quality USB cable and that you have a reliable power source*** + +* **LED not blinking:** Check the wiring connection and the IO selection. +* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed. +* **COM port not detected:** Check the USB cable and the USB to Serial driver installation. + +If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute). + +## Contribute + +To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst) + +If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome! + +Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else. + +## Resources + +* Official ESP32 Forum: [Link](https://esp32.com) +* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32) +* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf) +* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf) +* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com) diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino b/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino new file mode 100644 index 00000000000..c7f9cf84f74 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino @@ -0,0 +1,198 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @brief This example demonstrates Zigbee Window Covering. + * + * The example demonstrates how to use Zigbee library to create a end device window covering device. + * The window covering is a Zigbee end device, which is moving the blinds (lift+tilt) and reporting + * its current position to the Zigbee network. + * + * Use setCoveringType() to set the type of covering (blind, shade, etc.). + * + * The example also demonstrates how to use the button to manually control the lift position. + * + * Proper Zigbee mode must be selected in Tools->Zigbee mode + * and also the correct partition scheme must be selected in Tools->Partition Scheme. + * + * Please check the README.md for instructions and more detailed description. + * + * Created by hennikul and Jan Procházka (https://github.com/P-R-O-C-H-Y/) + */ + +#ifndef ZIGBEE_MODE_ED +#error "Zigbee end device mode is not selected in Tools->Zigbee mode" +#endif + +#include "ZigbeeCore.h" +#include "ep/ZigbeeWindowCovering.h" + +#define ZIGBEE_COVERING_ENDPOINT 10 +#define BUTTON_PIN 9 // ESP32-C6/H2 Boot button + +#define MAX_LIFT 200 // centimeters from open position (0-900) +#define MIN_LIFT 0 + +#define MAX_TILT 40 // centimeters from open position (0-900) +#define MIN_TILT 0 + +uint16_t currentLift = MAX_LIFT; +uint8_t currentLiftPercentage = 100; + +uint16_t currentTilt = MAX_TILT; +uint8_t currentTiltPercentage = 100; + +ZigbeeWindowCovering zbCovering = ZigbeeWindowCovering(ZIGBEE_COVERING_ENDPOINT); + +void setup() { + Serial.begin(115200); + + // Init button for factory reset + pinMode(BUTTON_PIN, INPUT_PULLUP); + + // Optional: set Zigbee device name and model + zbCovering.setManufacturerAndModel("Espressif", "WindowBlinds"); + + // Set proper covering type, it defines which attributes are available + zbCovering.setCoveringType(BLIND_LIFT_AND_TILT); + + // Set configuration: operational, online, not commands_reversed, lift / tilt closed_loop, lift / tilt encoder_controlled + zbCovering.setConfigStatus(true, true, false, true, true, true, true); + + // Set mode: not motor_reversed, calibration_mode, not maintenance_mode, not leds_on + zbCovering.setMode(false, true, false, false); + + // Set limits of motion + zbCovering.setLimits(MIN_LIFT, MAX_LIFT, MIN_TILT, MAX_TILT); + + // Set callback function for open, close, filt and tilt change, stop + zbCovering.onOpen(fullOpen); + zbCovering.onClose(fullClose); + zbCovering.onGoToLiftPercentage(goToLiftPercentage); + zbCovering.onGoToTiltPercentage(goToTiltPercentage); + zbCovering.onStop(stopMotor); + + // Add endpoint to Zigbee Core + Serial.println("Adding ZigbeeWindowCovering endpoint to Zigbee Core"); + Zigbee.addEndpoint(&zbCovering); + + // When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE + Serial.println("Calling Zigbee.begin()"); + if (!Zigbee.begin()) { + Serial.println("Zigbee failed to start!"); + Serial.println("Rebooting..."); + ESP.restart(); + } + Serial.println("Connecting to network"); + while (!Zigbee.connected()) { + Serial.print("."); + delay(100); + } + Serial.println(); + + // Set initial position + zbCovering.setLiftPercentage(currentLiftPercentage); + zbCovering.setTiltPercentage(currentTiltPercentage); +} + +void loop() { + // Checking button for factory reset + if (digitalRead(BUTTON_PIN) == LOW) { // Push button pressed + // Key debounce handling + delay(100); + int startTime = millis(); + while (digitalRead(BUTTON_PIN) == LOW) { + delay(50); + if ((millis() - startTime) > 3000) { + // If key pressed for more than 3secs, factory reset Zigbee and reboot + Serial.printf("Resetting Zigbee to factory settings, reboot.\n"); + Zigbee.factoryReset(); + delay(30000); + } + } + // Manual lift control simulation by pressing button + manualControl(); + } + delay(500); +} + +void fullOpen() { + /* This is where you would trigger your motor to go to full open state, currentLift should + be updated until full open has been reached in order to provide feedback to controller of actual position + The stop can be always called, so the movement can be stopped at any time */ + + // Our cover updates instantly! + currentLift = MAX_LIFT; + currentLiftPercentage = 100; + Serial.println("Opening cover"); + // Update the current position + zbCovering.setLiftPercentage(currentLiftPercentage); +} + +void fullClose() { + /* This is where you would trigger your motor to go to full close state, currentLift should + be updated until full close has been reached in order to provide feedback to controller of actual position + The stop can be always called, so the movement can be stopped at any time */ + + // Our cover updates instantly! + currentLift = MIN_LIFT; + currentLiftPercentage = 0; + Serial.println("Closing cover"); + // Update the current position + zbCovering.setLiftPercentage(currentLiftPercentage); +} + +void goToLiftPercentage(uint8_t liftPercentage) { + /* This is where you would trigger your motor to go towards liftPercentage, currentLift should + be updated until liftPercentage has been reached in order to provide feedback to controller */ + + // Our simulated cover updates instantly! + currentLift = (liftPercentage * MAX_LIFT) / 100; + currentLiftPercentage = liftPercentage; + Serial.printf("New requested lift from Zigbee: %d (%d)\n", currentLift, liftPercentage); + + // Update the current position + zbCovering.setLiftPercentage(currentLiftPercentage); //or setLiftPosition() +} + +void goToTiltPercentage(uint8_t tiltPercentage) { + /* This is where you would trigger your motor to go towards tiltPercentage, currentTilt should + be updated until tiltPercentage has been reached in order to provide feedback to controller */ + + // Our simulated cover updates instantly! + currentTilt = (tiltPercentage * MAX_TILT) / 100; + currentTiltPercentage = tiltPercentage; + Serial.printf("New requested tilt from Zigbee: %d (%d)\n", currentTilt, tiltPercentage); + + // Update the current position + zbCovering.setTiltPercentage(currentTiltPercentage); //or setTiltPosition() +} + +void stopMotor() { + // Motor can be stopped while moving cover toward current target, when stopped the actual position should be updated + Serial.println("Stopping motor"); + // Update the current position of both lift and tilt + zbCovering.setLiftPercentage(currentLiftPercentage); + zbCovering.setTiltPercentage(currentTiltPercentage); +} + +void manualControl() { + // Simulate lift percentage move by increasing it by 20% each time + currentLiftPercentage += 20; + if (currentLiftPercentage > 100) { + currentLiftPercentage = 0; + } + zbCovering.setLiftPercentage(currentLiftPercentage); + // Also setLiftPosition() can be used to set the exact position instead of percentage +} diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/ci.json b/libraries/Zigbee/examples/Zigbee_Window_Covering/ci.json new file mode 100644 index 00000000000..7b7ccef8ed7 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Window_Covering/ci.json @@ -0,0 +1,6 @@ +{ + "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", + "requires": [ + "CONFIG_SOC_IEEE802154_SUPPORTED=y" + ] +} diff --git a/libraries/Zigbee/src/Zigbee.h b/libraries/Zigbee/src/Zigbee.h index c97be9378a8..49bfa44d5c1 100644 --- a/libraries/Zigbee/src/Zigbee.h +++ b/libraries/Zigbee/src/Zigbee.h @@ -20,3 +20,4 @@ #include "ep/ZigbeeCarbonDioxideSensor.h" #include "ep/ZigbeeContactSwitch.h" #include "ep/ZigbeeDoorWindowHandle.h" +#include "ep/ZigbeeWindowCovering.h" diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index 069db95aac0..fce0500ef0d 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -115,8 +115,8 @@ class ZigbeeEP { virtual void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) {}; virtual void zbReadBasicCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented virtual void zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message); + virtual void zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) {}; virtual void zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented - virtual void zbIASZoneStatusChangeNotification(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message) {}; virtual void zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) {}; diff --git a/libraries/Zigbee/src/ZigbeeHandlers.cpp b/libraries/Zigbee/src/ZigbeeHandlers.cpp index 52c38eb0633..575b25ef404 100644 --- a/libraries/Zigbee/src/ZigbeeHandlers.cpp +++ b/libraries/Zigbee/src/ZigbeeHandlers.cpp @@ -12,6 +12,7 @@ static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_re static esp_err_t zb_cmd_ias_zone_status_change_handler(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message); static esp_err_t zb_cmd_ias_zone_enroll_response_handler(const esp_zb_zcl_ias_zone_enroll_response_message_t *message); static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message); +static esp_err_t zb_window_covering_movement_resp_handler(const esp_zb_zcl_window_covering_movement_message_t *message); // Zigbee action handlers [[maybe_unused]] @@ -28,6 +29,9 @@ static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, case ESP_ZB_CORE_IAS_ZONE_ENROLL_RESPONSE_VALUE_CB_ID: ret = zb_cmd_ias_zone_enroll_response_handler((esp_zb_zcl_ias_zone_enroll_response_message_t *)message); break; + case ESP_ZB_CORE_WINDOW_COVERING_MOVEMENT_CB_ID: + ret = zb_window_covering_movement_resp_handler((esp_zb_zcl_window_covering_movement_message_t *)message); + break; case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; } @@ -198,4 +202,26 @@ static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_m return ESP_OK; } +static esp_err_t zb_window_covering_movement_resp_handler(const esp_zb_zcl_window_covering_movement_message_t *message) { + if (!message) { + log_e("Empty message"); + } + if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Received message: error status(%d)", message->info.status); + } + + log_v( + "Received message: endpoint(%d), cluster(0x%x), command(0x%x), payload(%d)", message->info.dst_endpoint, message->info.cluster, message->command, + message->payload + ); + + // List through all Zigbee EPs and call the callback function, with the message + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + if (message->info.dst_endpoint == (*it)->getEndpoint()) { + (*it)->zbWindowCoveringMovementCmd(message); //method zbWindowCoveringMovementCmd must be implemented in specific EP class + } + } + return ESP_OK; +} + #endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp new file mode 100644 index 00000000000..970017165e2 --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp @@ -0,0 +1,308 @@ + +#include "ZigbeeWindowCovering.h" +#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED + +esp_zb_cluster_list_t *ZigbeeWindowCovering::zigbee_window_covering_clusters_create(zigbee_window_covering_cfg_t *window_covering_cfg) { + esp_zb_attribute_list_t *esp_zb_basic_cluster = esp_zb_basic_cluster_create(&window_covering_cfg->basic_cfg); + esp_zb_attribute_list_t *esp_zb_identify_cluster = esp_zb_identify_cluster_create(&window_covering_cfg->identify_cfg); + esp_zb_attribute_list_t *esp_zb_groups_cluster = esp_zb_groups_cluster_create(&window_covering_cfg->groups_cfg); + esp_zb_attribute_list_t *esp_zb_scenes_cluster = esp_zb_scenes_cluster_create(&window_covering_cfg->scenes_cfg); + esp_zb_attribute_list_t *esp_zb_window_covering_cluster = esp_zb_window_covering_cluster_create(&window_covering_cfg->window_covering_cfg); + + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &_current_lift_percentage + ); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID, &_current_tilt_percentage + ); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, &_current_lift_position); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_ID, &_current_lift_position); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_ID, &_installed_open_limit_lift + ); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_ID, &_installed_open_limit_tilt + ); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_ID, &_installed_closed_limit_lift + ); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_ID, &_installed_closed_limit_tilt + ); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_ID, &_physical_closed_limit_lift + ); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHY_CLOSED_LIMIT_TILT_ID, &_physical_closed_limit_lift + ); + + // ------------------------------ Create cluster list ------------------------------ + esp_zb_cluster_list_t *esp_zb_cluster_list = esp_zb_zcl_cluster_list_create(); + esp_zb_cluster_list_add_basic_cluster(esp_zb_cluster_list, esp_zb_basic_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_identify_cluster(esp_zb_cluster_list, esp_zb_identify_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_groups_cluster(esp_zb_cluster_list, esp_zb_groups_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_scenes_cluster(esp_zb_cluster_list, esp_zb_scenes_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_window_covering_cluster(esp_zb_cluster_list, esp_zb_window_covering_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + + return esp_zb_cluster_list; +} + +ZigbeeWindowCovering::ZigbeeWindowCovering(uint8_t endpoint) : ZigbeeEP(endpoint) { + _device_id = ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID; + + // set default values for window covering attributes + _current_lift_percentage = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_DEFAULT_VALUE; + _current_tilt_percentage = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_DEFAULT_VALUE; + _installed_open_limit_lift = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_DEFAULT_VALUE; + _installed_closed_limit_lift = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_DEFAULT_VALUE; + _installed_open_limit_tilt = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_DEFAULT_VALUE; + _installed_closed_limit_tilt = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_DEFAULT_VALUE; + _current_lift_position = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_LIFT_DEFAULT_VALUE; + _current_tilt_position = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_TILT_DEFAULT_VALUE; + _physical_closed_limit_lift = ESP_ZB_ZCL_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_DEFAULT_VALUE; + _physical_closed_limit_tilt = ESP_ZB_ZCL_WINDOW_COVERING_PHY_CLOSED_LIMIT_TILT_DEFAULT_VALUE; + + // Create custom window covering configuration + zigbee_window_covering_cfg_t window_covering_cfg = ZIGBEE_DEFAULT_WINDOW_COVERING_CONFIG(); + _cluster_list = zigbee_window_covering_clusters_create(&window_covering_cfg); + + _ep_config = { + .endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID, .app_device_version = 0 + }; +} + +// Configuration methods for window covering +void ZigbeeWindowCovering::setCoveringType(ZigbeeWindowCoveringType covering_type) { + esp_zb_attribute_list_t *window_covering_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_WINDOW_COVERING_TYPE_ID, (void *)&covering_type); +} + +void ZigbeeWindowCovering::setConfigStatus( + bool operational, bool online, bool commands_reversed, bool lift_closed_loop, bool tilt_closed_loop, bool lift_encoder_controlled, + bool tilt_encoder_controlled +) { + uint8_t config_status = (operational ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_OPERATIONAL : 0) | (online ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_ONLINE : 0) + | (commands_reversed ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_REVERSE_COMMANDS : 0) + | (lift_closed_loop ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_LIFT_CONTROL_IS_CLOSED_LOOP : 0) + | (tilt_closed_loop ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_TILT_CONTROL_IS_CLOSED_LOOP : 0) + | (lift_encoder_controlled ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_LIFT_ENCODER_CONTROLLED : 0) + | (tilt_encoder_controlled ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_TILT_ENCODER_CONTROLLED : 0); + + log_v("Updating window covering config status to %d", config_status); + + esp_zb_attribute_list_t *window_covering_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_STATUS_ID, (void *)&config_status); +} + +void ZigbeeWindowCovering::setMode(bool motor_reversed, bool calibration_mode, bool maintenance_mode, bool leds_on) { + uint8_t mode = (motor_reversed ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_REVERSED_MOTOR_DIRECTION : 0) + | (calibration_mode ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_RUN_IN_CALIBRATION_MODE : 0) + | (maintenance_mode ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_MOTOR_IS_RUNNING_IN_MAINTENANCE_MODE : 0) + | (leds_on ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_LEDS_WILL_DISPLAY_FEEDBACK : 0); + + log_v("Updating window covering mode to %d", mode); + + esp_zb_attribute_list_t *window_covering_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_MODE_ID, (void *)&mode); +} + +void ZigbeeWindowCovering::setLimits( + uint16_t installed_open_limit_lift, uint16_t installed_closed_limit_lift, uint16_t installed_open_limit_tilt, uint16_t installed_closed_limit_tilt +) { + _installed_open_limit_lift = installed_open_limit_lift; + _installed_closed_limit_lift = installed_closed_limit_lift; + _physical_closed_limit_lift = installed_closed_limit_lift; + _installed_open_limit_tilt = installed_open_limit_tilt; + _installed_closed_limit_tilt = installed_closed_limit_tilt; + _physical_closed_limit_tilt = installed_closed_limit_tilt; + + esp_zb_attribute_list_t *window_covering_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_ID, (void *)&_installed_open_limit_lift); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_ID, (void *)&_installed_closed_limit_lift); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_ID, (void *)&_installed_open_limit_tilt); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_ID, (void *)&_installed_closed_limit_tilt); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_ID, (void *)&_physical_closed_limit_lift); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHY_CLOSED_LIMIT_TILT_ID, (void *)&_physical_closed_limit_tilt); +} + +// Callback for handling incoming messages and commands +void ZigbeeWindowCovering::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) { + //check the data and call right method + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING) { + log_v("Received attribute id: 0x%x / data.type: 0x%x", message->attribute.id, message->attribute.data.type); + if (message->attribute.id == ESP_ZB_ZCL_ATTR_WINDOW_COVERING_MODE_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_8BITMAP) { + uint8_t mode = *(uint8_t *)message->attribute.data.value; + bool motor_reversed = mode & ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_REVERSED_MOTOR_DIRECTION; + bool calibration_mode = mode & ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_RUN_IN_CALIBRATION_MODE; + bool maintenance_mode = mode & ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_MOTOR_IS_RUNNING_IN_MAINTENANCE_MODE; + bool leds_on = mode & ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_LEDS_WILL_DISPLAY_FEEDBACK; + log_v( + "Updating window covering mode to motor reversed: %d, calibration mode: %d, maintenance mode: %d, leds on: %d", motor_reversed, calibration_mode, + maintenance_mode, leds_on + ); + setMode(motor_reversed, calibration_mode, maintenance_mode, leds_on); + //Update Configuration status with motor reversed status + uint8_t config_status; + config_status = (*(uint8_t *)esp_zb_zcl_get_attribute( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_STATUS_ID + ) + ->data_p); + config_status = motor_reversed ? config_status | ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_REVERSE_COMMANDS + : config_status & ~ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_REVERSE_COMMANDS; + log_v("Updating window covering config status to %d", config_status); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_STATUS_ID, &config_status, + false + ); + esp_zb_lock_release(); + return; + } + } else { + log_w("Received message ignored. Cluster ID: %d not supported for Window Covering", message->info.cluster); + } +} + +void ZigbeeWindowCovering::zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) { + // check the data and call right method + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING) { + if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_UP_OPEN) { + open(); + return; + } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_DOWN_CLOSE) { + close(); + return; + } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_STOP) { + stop(); + return; + } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_GO_TO_LIFT_PERCENTAGE) { + if (_current_lift_percentage != message->payload.percentage_lift_value) { + _current_lift_percentage = message->payload.percentage_lift_value; + goToLiftPercentage(_current_lift_percentage); + } + return; + } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_GO_TO_TILT_PERCENTAGE) { + if (_current_tilt_percentage != message->payload.percentage_tilt_value) { + _current_tilt_percentage = message->payload.percentage_tilt_value; + goToTiltPercentage(_current_tilt_percentage); + } + } else { + log_w("Received message ignored. Command: %d not supported for Window Covering", message->command); + } + } else { + log_w("Received message ignored. Cluster ID: %d not supported for Window Covering", message->info.cluster); + } +} + +void ZigbeeWindowCovering::open() { + if (_on_open) { + _on_open(); + } +} + +void ZigbeeWindowCovering::close() { + if (_on_close) { + _on_close(); + } +} + +void ZigbeeWindowCovering::goToLiftPercentage(uint8_t lift_percentage) { + if (_on_go_to_lift_percentage) { + _on_go_to_lift_percentage(lift_percentage); + } +} + +void ZigbeeWindowCovering::goToTiltPercentage(uint8_t tilt_percentage) { + if (_on_go_to_tilt_percentage) { + _on_go_to_tilt_percentage(tilt_percentage); + } +} + +void ZigbeeWindowCovering::stop() { + if (_on_stop) { + _on_stop(); + } +} + +// Methods to control window covering from user application +void ZigbeeWindowCovering::setLiftPosition(uint16_t lift_position) { + // Update both lift attributes + _current_lift_position = lift_position; + _current_lift_percentage = ((lift_position - _installed_open_limit_lift) * 100) / (_installed_closed_limit_lift - _installed_open_limit_lift); + + log_v("Updating window covering lift position to %d (%d%)", _current_lift_position, _current_lift_percentage); + // set lift state + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, + &_current_lift_position, false + ); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, + &_current_lift_percentage, false + ); + esp_zb_lock_release(); +} + +void ZigbeeWindowCovering::setLiftPercentage(uint8_t lift_percentage) { + // Update both lift attributes + _current_lift_percentage = lift_percentage; + _current_lift_position = _installed_open_limit_lift + ((_installed_closed_limit_lift - _installed_open_limit_lift) * lift_percentage) / 100; + + log_v("Updating window covering lift position to %d (%d%)", _current_lift_position, _current_lift_percentage); + // set lift state + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, + &_current_lift_position, false + ); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, + &_current_lift_percentage, false + ); + esp_zb_lock_release(); +} + +void ZigbeeWindowCovering::setTiltPosition(uint16_t tilt_position) { + // Update both tilt attributes + _current_tilt_position = tilt_position; + _current_tilt_percentage = ((tilt_position - _installed_open_limit_tilt) * 100) / (_installed_closed_limit_tilt - _installed_open_limit_tilt); + + log_v("Updating window covering tilt position to %d (%d%)", _current_tilt_position, _current_tilt_percentage); + // set lift state + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_ID, + &_current_tilt_position, false + ); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID, + &_current_tilt_percentage, false + ); + esp_zb_lock_release(); +} + +void ZigbeeWindowCovering::setTiltPercentage(uint8_t tilt_percentage) { + // Update both tilt attributes + _current_tilt_percentage = tilt_percentage; + _current_tilt_position = _installed_open_limit_lift + ((_installed_closed_limit_tilt - _installed_open_limit_tilt) * tilt_percentage) / 100; + + log_v("Updating window covering tilt position to %d (%d%)", _current_tilt_position, _current_tilt_percentage); + // set lift state + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_ID, + &_current_tilt_position, false + ); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID, + &_current_tilt_percentage, false + ); + esp_zb_lock_release(); +} + +#endif // SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h new file mode 100644 index 00000000000..5cc2b13701a --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h @@ -0,0 +1,147 @@ +/* Class of Zigbee Window Covering endpoint inherited from common EP class */ + +#pragma once + +#include "soc/soc_caps.h" +#include "sdkconfig.h" +#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED + +#include "ZigbeeEP.h" +#include "ha/esp_zigbee_ha_standard.h" + +// Window covering types supported by Zigbee Window Covering cluster +enum ZigbeeWindowCoveringType { + ROLLERSHADE = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE, // LIFT support + ROLLERSHADE_2_MOTOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_2_MOTOR, // LIFT support + ROLLERSHADE_EXTERIOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_EXTERIOR, // LIFT support + ROLLERSHADE_EXTERIOR_2_MOTOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_EXTERIOR_2_MOTOR, // LIFT support + DRAPERY = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_DRAPERY, // LIFT support + AWNING = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_AWNING, // LIFT support + SHUTTER = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_SHUTTER, // TILT support + BLIND_TILT_ONLY = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_TILT_BLIND_TILT_ONLY, // TILT support + BLIND_LIFT_AND_TILT = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_TILT_BLIND_LIFT_AND_TILT, // LIFT and TILT support + PROJECTOR_SCREEN = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_PROJECTOR_SCREEN, // LIFT support +}; + +// clang-format off +#define ZIGBEE_DEFAULT_WINDOW_COVERING_CONFIG() \ + { \ + .basic_cfg = \ + { \ + .zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE, \ + .power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE, \ + }, \ + .identify_cfg = \ + { \ + .identify_time = ESP_ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE, \ + }, \ + .groups_cfg = \ + { \ + .groups_name_support_id = ESP_ZB_ZCL_GROUPS_NAME_SUPPORT_DEFAULT_VALUE, \ + }, \ + .scenes_cfg = \ + { \ + .scenes_count = ESP_ZB_ZCL_SCENES_SCENE_COUNT_DEFAULT_VALUE, \ + .current_scene = ESP_ZB_ZCL_SCENES_CURRENT_SCENE_DEFAULT_VALUE, \ + .current_group = ESP_ZB_ZCL_SCENES_CURRENT_GROUP_DEFAULT_VALUE, \ + .scene_valid = ESP_ZB_ZCL_SCENES_SCENE_VALID_DEFAULT_VALUE, \ + .name_support = ESP_ZB_ZCL_SCENES_NAME_SUPPORT_DEFAULT_VALUE, \ + }, \ + .window_covering_cfg = \ + { \ + .covering_type = ESP_ZB_ZCL_WINDOW_COVERING_WINDOW_COVERING_TYPE_DEFAULT_VALUE, \ + .covering_status = ESP_ZB_ZCL_WINDOW_COVERING_CONFIG_STATUS_DEFAULT_VALUE, \ + .covering_mode = ESP_ZB_ZCL_WINDOW_COVERING_MODE_DEFAULT_VALUE, \ + }, \ + } +// clang-format on + +typedef struct zigbee_window_covering_cfg_s { + esp_zb_basic_cluster_cfg_t basic_cfg; + esp_zb_identify_cluster_cfg_t identify_cfg; + esp_zb_groups_cluster_cfg_t groups_cfg; + esp_zb_scenes_cluster_cfg_t scenes_cfg; + esp_zb_window_covering_cluster_cfg_t window_covering_cfg; +} zigbee_window_covering_cfg_t; + +class ZigbeeWindowCovering : public ZigbeeEP { +public: + ZigbeeWindowCovering(uint8_t endpoint); + ~ZigbeeWindowCovering(); + + // Set the callback functions for the window covering commands + void onOpen(void (*callback)()) { + _on_open = callback; + } + void onClose(void (*callback)()) { + _on_close = callback; + } + void onGoToLiftPercentage(void (*callback)(uint8_t)) { + _on_go_to_lift_percentage = callback; + } + void onGoToTiltPercentage(void (*callback)(uint8_t)) { + _on_go_to_tilt_percentage = callback; + } + void onStop(void (*callback)()) { + _on_stop = callback; + } + + // Set the window covering position in centimeters or percentage (0-100) + void setLiftPosition(uint16_t lift_position); + void setLiftPercentage(uint8_t lift_percentage); + void setTiltPosition(uint16_t tilt_position); + void setTiltPercentage(uint8_t tilt_percentage); + + // Set the window covering type (see ZigbeeWindowCoveringType) + void setCoveringType(ZigbeeWindowCoveringType covering_type); + + // Set window covering config/status, for more info see esp_zb_zcl_window_covering_config_status_t + void setConfigStatus( + bool operational, bool online, bool commands_reversed, bool lift_closed_loop, bool tilt_closed_loop, bool lift_encoder_controlled, + bool tilt_encoder_controlled + ); + + // Set configuration mode of window covering, for more info see esp_zb_zcl_window_covering_mode_t + void setMode(bool motor_reversed, bool calibration_mode, bool maintenance_mode, bool leds_on); + + // Set limits of motion, for more info see esp_zb_zcl_window_covering_info_attr_t + void setLimits( + uint16_t installed_open_limit_lift, uint16_t installed_closed_limit_lift, uint16_t installed_open_limit_tilt, uint16_t installed_closed_limit_tilt + ); + +private: + void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; + void zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) override; + + // Create window covering cluster list + esp_zb_cluster_list_t *zigbee_window_covering_clusters_create(zigbee_window_covering_cfg_t *window_covering_cfg); + + void open(); + void close(); + void goToLiftPercentage(uint8_t); + void goToTiltPercentage(uint8_t); + void stop(); + + // callback function to be called on lift percentage change (lift percentage) + void (*_on_open)(); + void (*_on_close)(); + void (*_on_go_to_lift_percentage)(uint8_t); + void (*_on_go_to_tilt_percentage)(uint8_t); + void (*_on_stop)(); + + // Widows covering lift attributes + uint8_t _current_lift_percentage; + uint16_t _current_lift_position; + uint16_t _installed_open_limit_lift; + uint16_t _installed_closed_limit_lift; + uint16_t _physical_closed_limit_lift; + + // Windows covering tilt attributes + uint8_t _current_tilt_percentage; + uint16_t _current_tilt_position; + uint16_t _installed_open_limit_tilt; + uint16_t _installed_closed_limit_tilt; + uint16_t _physical_closed_limit_tilt; +}; + +#endif // SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED From 6fcaf690970401e63e75609628663b862a928b68 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Thu, 6 Feb 2025 11:19:27 +0200 Subject: [PATCH 46/60] fix(wifi): Make sure that esp-hosted events are propagated (#10939) --- libraries/Network/src/NetworkInterface.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/Network/src/NetworkInterface.cpp b/libraries/Network/src/NetworkInterface.cpp index 4f310821204..01790ec2493 100644 --- a/libraries/Network/src/NetworkInterface.cpp +++ b/libraries/Network/src/NetworkInterface.cpp @@ -81,7 +81,7 @@ void NetworkInterface::_onIpEvent(int32_t event_id, void *event_data) { ); #endif memcpy(&arduino_event.event_info.got_ip, event_data, sizeof(ip_event_got_ip_t)); -#if SOC_WIFI_SUPPORTED +#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED if (_interface_id == ESP_NETIF_ID_STA) { arduino_event.event_id = ARDUINO_EVENT_WIFI_STA_GOT_IP; } else @@ -96,7 +96,7 @@ void NetworkInterface::_onIpEvent(int32_t event_id, void *event_data) { #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE log_v("%s Lost IP", desc()); #endif -#if SOC_WIFI_SUPPORTED +#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED if (_interface_id == ESP_NETIF_ID_STA) { arduino_event.event_id = ARDUINO_EVENT_WIFI_STA_LOST_IP; } else @@ -123,7 +123,7 @@ void NetworkInterface::_onIpEvent(int32_t event_id, void *event_data) { ); #endif memcpy(&arduino_event.event_info.got_ip6, event_data, sizeof(ip_event_got_ip6_t)); -#if SOC_WIFI_SUPPORTED +#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED if (_interface_id == ESP_NETIF_ID_STA) { arduino_event.event_id = ARDUINO_EVENT_WIFI_STA_GOT_IP6; } else if (_interface_id == ESP_NETIF_ID_AP) { @@ -136,7 +136,7 @@ void NetworkInterface::_onIpEvent(int32_t event_id, void *event_data) { arduino_event.event_id = ARDUINO_EVENT_ETH_GOT_IP6; } #endif /* CONFIG_LWIP_IPV6 */ -#if SOC_WIFI_SUPPORTED +#if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED } else if (event_id == IP_EVENT_AP_STAIPASSIGNED && _interface_id == ESP_NETIF_ID_AP) { setStatusBits(ESP_NETIF_HAS_IP_BIT); #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE From 5ba4c21a990f46b61ae8c7913b81e15064b2a8ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:06:07 +0100 Subject: [PATCH 47/60] fix(zigbee): Add default destructor and fix initialization of tm struct (#10943) --- libraries/Zigbee/src/ZigbeeEP.h | 2 +- libraries/Zigbee/src/ep/ZigbeeWindowCovering.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index fce0500ef0d..584873f50dd 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -95,7 +95,7 @@ class ZigbeeEP { void reportBatteryPercentage(); // Set time - void addTimeCluster(tm time = {0}, int32_t gmt_offset = 0); // gmt offset in seconds + void addTimeCluster(tm time = {}, int32_t gmt_offset = 0); // gmt offset in seconds void setTime(tm time); void setTimezone(int32_t gmt_offset); diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h index 5cc2b13701a..08e0a35f737 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h @@ -67,7 +67,7 @@ typedef struct zigbee_window_covering_cfg_s { class ZigbeeWindowCovering : public ZigbeeEP { public: ZigbeeWindowCovering(uint8_t endpoint); - ~ZigbeeWindowCovering(); + ~ZigbeeWindowCovering() {} // Set the callback functions for the window covering commands void onOpen(void (*callback)()) { From 250c1abf781eba8cec7a524090939c30e05b690c Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Wed, 12 Feb 2025 10:40:15 +0200 Subject: [PATCH 48/60] fix(i2s): Add missing initializer for I2S CLK config (#10963) * fix(i2s): Add missing initializer for I2S CLK config * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- libraries/ESP_I2S/src/ESP_I2S.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/ESP_I2S/src/ESP_I2S.cpp b/libraries/ESP_I2S/src/ESP_I2S.cpp index d0ceb0c4b4c..6bf8089e48a 100644 --- a/libraries/ESP_I2S/src/ESP_I2S.cpp +++ b/libraries/ESP_I2S/src/ESP_I2S.cpp @@ -11,6 +11,12 @@ #include "mp3dec.h" #endif +#if SOC_I2S_HW_VERSION_2 +#undef I2S_STD_CLK_DEFAULT_CONFIG +#define I2S_STD_CLK_DEFAULT_CONFIG(rate) \ + { .sample_rate_hz = rate, .clk_src = I2S_CLK_SRC_DEFAULT, .ext_clk_freq_hz = 0, .mclk_multiple = I2S_MCLK_MULTIPLE_256, } +#endif + #define I2S_READ_CHUNK_SIZE 1920 #define I2S_DEFAULT_CFG() \ From 7b651b64d61ccd513480305f60fae877b26f764f Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Thu, 13 Feb 2025 12:51:28 +0200 Subject: [PATCH 49/60] feat(cdc): Add support for two CDC ports at once (#10962) * feat(cdc): Add support for two CDC ports at once * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- cores/esp32/USBCDC.cpp | 68 ++++++++++++++++++++++++--------- cores/esp32/esp32-hal-tinyusb.c | 24 ++++++------ cores/esp32/esp32-hal-tinyusb.h | 8 ++++ 3 files changed, 72 insertions(+), 28 deletions(-) diff --git a/cores/esp32/USBCDC.cpp b/cores/esp32/USBCDC.cpp index 795a17dc0b8..945021a79e2 100644 --- a/cores/esp32/USBCDC.cpp +++ b/cores/esp32/USBCDC.cpp @@ -25,8 +25,7 @@ ESP_EVENT_DEFINE_BASE(ARDUINO_USB_CDC_EVENTS); esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait); esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg); -#define MAX_USB_CDC_DEVICES 2 -USBCDC *devices[MAX_USB_CDC_DEVICES] = {NULL, NULL}; +USBCDC *devices[CFG_TUD_CDC]; static uint16_t load_cdc_descriptor(uint8_t *dst, uint8_t *itf) { uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB CDC"); @@ -38,23 +37,43 @@ static uint16_t load_cdc_descriptor(uint8_t *dst, uint8_t *itf) { return TUD_CDC_DESC_LEN; } +static uint16_t load_cdc_descriptor2(uint8_t *dst, uint8_t *itf) { + uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB CDC2"); + uint8_t ep_ntfy = tinyusb_get_free_in_endpoint(); + TU_VERIFY(ep_ntfy != 0); + uint8_t ep_in = tinyusb_get_free_in_endpoint(); + TU_VERIFY(ep_in != 0); + uint8_t ep_out = tinyusb_get_free_out_endpoint(); + TU_VERIFY(ep_out != 0); + uint8_t descriptor[TUD_CDC_DESC_LEN] = { + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_CDC_DESCRIPTOR(*itf, str_index, (uint8_t)(0x80 | ep_ntfy), CFG_TUD_ENDOINT_SIZE, ep_out, (uint8_t)(0x80 | ep_in), CFG_TUD_ENDOINT_SIZE) + }; + *itf += 2; + memcpy(dst, descriptor, TUD_CDC_DESC_LEN); + return TUD_CDC_DESC_LEN; +} + // Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { - if (itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL) { + //log_v("ITF: %u, DTR: %u, RTS: %u", itf, dtr, rts); + if (itf < CFG_TUD_CDC && devices[itf] != NULL) { devices[itf]->_onLineState(dtr, rts); } } // Invoked when line coding is change via SET_LINE_CODING void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const *p_line_coding) { - if (itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL) { + //log_v("ITF: %u, BITRATE: %lu, STOP_BITS: %u, PARITY: %u, DATA_BITS: %u", itf, p_line_coding->bit_rate, p_line_coding->stop_bits, p_line_coding->parity, p_line_coding->data_bits); + if (itf < CFG_TUD_CDC && devices[itf] != NULL) { devices[itf]->_onLineCoding(p_line_coding->bit_rate, p_line_coding->stop_bits, p_line_coding->parity, p_line_coding->data_bits); } } // Invoked when received new data void tud_cdc_rx_cb(uint8_t itf) { - if (itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL) { + //log_v("ITF: %u", itf); + if (itf < CFG_TUD_CDC && devices[itf] != NULL) { devices[itf]->_onRX(); } } @@ -66,13 +85,13 @@ void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) { // Invoked when space becomes available in TX buffer void tud_cdc_tx_complete_cb(uint8_t itf) { - if (itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL) { + if (itf < CFG_TUD_CDC && devices[itf] != NULL) { devices[itf]->_onTX(); } } static void ARDUINO_ISR_ATTR cdc0_write_char(char c) { - if (devices[0] != NULL) { + if (CFG_TUD_CDC && devices[0] != NULL) { tud_cdc_n_write_char(0, c); } } @@ -84,9 +103,15 @@ static void usb_unplugged_cb(void *arg, esp_event_base_t event_base, int32_t eve USBCDC::USBCDC(uint8_t itfn) : itf(itfn), bit_rate(0), stop_bits(0), parity(0), data_bits(0), dtr(false), rts(false), connected(false), reboot_enable(true), rx_queue(NULL), tx_lock(NULL), tx_timeout_ms(250) { - tinyusb_enable_interface(USB_INTERFACE_CDC, TUD_CDC_DESC_LEN, load_cdc_descriptor); - if (itf < MAX_USB_CDC_DEVICES) { + if (itf < CFG_TUD_CDC) { + if (itf == 0) { + tinyusb_enable_interface(USB_INTERFACE_CDC, TUD_CDC_DESC_LEN, load_cdc_descriptor); + } else { + tinyusb_enable_interface(USB_INTERFACE_CDC2, TUD_CDC_DESC_LEN, load_cdc_descriptor2); + } arduino_usb_event_handler_register_with(ARDUINO_USB_EVENTS, ARDUINO_USB_STOPPED_EVENT, usb_unplugged_cb, this); + } else { + log_e("Maximum of %u CDC devices are supported", CFG_TUD_CDC); } } @@ -142,6 +167,9 @@ size_t USBCDC::setRxBufferSize(size_t rx_queue_len) { } void USBCDC::begin(unsigned long baud) { + if (itf >= CFG_TUD_CDC) { + return; + } if (tx_lock == NULL) { tx_lock = xSemaphoreCreateMutex(); } @@ -153,6 +181,9 @@ void USBCDC::begin(unsigned long baud) { } void USBCDC::end() { + if (itf >= CFG_TUD_CDC) { + return; + } connected = false; devices[itf] = NULL; setRxBufferSize(0); @@ -298,14 +329,14 @@ bool USBCDC::rebootEnabled(void) { } int USBCDC::available(void) { - if (itf >= MAX_USB_CDC_DEVICES || rx_queue == NULL) { + if (itf >= CFG_TUD_CDC || rx_queue == NULL) { return -1; } return uxQueueMessagesWaiting(rx_queue); } int USBCDC::peek(void) { - if (itf >= MAX_USB_CDC_DEVICES || rx_queue == NULL) { + if (itf >= CFG_TUD_CDC || rx_queue == NULL) { return -1; } uint8_t c; @@ -316,7 +347,7 @@ int USBCDC::peek(void) { } int USBCDC::read(void) { - if (itf >= MAX_USB_CDC_DEVICES || rx_queue == NULL) { + if (itf >= CFG_TUD_CDC || rx_queue == NULL) { return -1; } uint8_t c = 0; @@ -327,7 +358,7 @@ int USBCDC::read(void) { } size_t USBCDC::read(uint8_t *buffer, size_t size) { - if (itf >= MAX_USB_CDC_DEVICES || rx_queue == NULL) { + if (itf >= CFG_TUD_CDC || rx_queue == NULL) { return -1; } uint8_t c = 0; @@ -339,7 +370,7 @@ size_t USBCDC::read(uint8_t *buffer, size_t size) { } void USBCDC::flush(void) { - if (itf >= MAX_USB_CDC_DEVICES || tx_lock == NULL || !tud_cdc_n_connected(itf)) { + if (itf >= CFG_TUD_CDC || tx_lock == NULL || !tud_cdc_n_connected(itf)) { return; } if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) { @@ -350,7 +381,7 @@ void USBCDC::flush(void) { } int USBCDC::availableForWrite(void) { - if (itf >= MAX_USB_CDC_DEVICES || tx_lock == NULL || !tud_cdc_n_connected(itf)) { + if (itf >= CFG_TUD_CDC || tx_lock == NULL || !tud_cdc_n_connected(itf)) { return 0; } if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) { @@ -362,7 +393,7 @@ int USBCDC::availableForWrite(void) { } size_t USBCDC::write(const uint8_t *buffer, size_t size) { - if (itf >= MAX_USB_CDC_DEVICES || tx_lock == NULL || buffer == NULL || size == 0 || !tud_cdc_n_connected(itf)) { + if (itf >= CFG_TUD_CDC || tx_lock == NULL || buffer == NULL || size == 0 || !tud_cdc_n_connected(itf)) { return 0; } if (xPortInIsrContext()) { @@ -415,6 +446,9 @@ uint32_t USBCDC::baudRate() { } void USBCDC::setDebugOutput(bool en) { + if (itf) { + return; + } if (en) { uartSetDebug(NULL); ets_install_putc2((void (*)(char)) & cdc0_write_char); @@ -424,7 +458,7 @@ void USBCDC::setDebugOutput(bool en) { } USBCDC::operator bool() const { - if (itf >= MAX_USB_CDC_DEVICES) { + if (itf >= CFG_TUD_CDC) { return false; } return connected; diff --git a/cores/esp32/esp32-hal-tinyusb.c b/cores/esp32/esp32-hal-tinyusb.c index f83e8b61bd2..0991e08d27f 100644 --- a/cores/esp32/esp32-hal-tinyusb.c +++ b/cores/esp32/esp32-hal-tinyusb.c @@ -616,7 +616,7 @@ void usb_persist_restart(restart_type_t mode) { } static bool tinyusb_reserve_in_endpoint(uint8_t endpoint) { - if (endpoint > 6 || (tinyusb_endpoints.in & BIT(endpoint)) != 0) { + if (endpoint > CFG_TUD_NUM_EPS || (tinyusb_endpoints.in & BIT(endpoint)) != 0) { return false; } tinyusb_endpoints.in |= BIT(endpoint); @@ -624,7 +624,7 @@ static bool tinyusb_reserve_in_endpoint(uint8_t endpoint) { } static bool tinyusb_reserve_out_endpoint(uint8_t endpoint) { - if (endpoint > 6 || (tinyusb_endpoints.out & BIT(endpoint)) != 0) { + if (endpoint > CFG_TUD_NUM_EPS || (tinyusb_endpoints.out & BIT(endpoint)) != 0) { return false; } tinyusb_endpoints.out |= BIT(endpoint); @@ -632,11 +632,13 @@ static bool tinyusb_reserve_out_endpoint(uint8_t endpoint) { } static bool tinyusb_has_available_fifos(void) { - uint8_t max_endpoints = 4, active_endpoints = 0; + uint8_t max_endpoints = CFG_TUD_NUM_IN_EPS - 1, active_endpoints = 0; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 if (tinyusb_loaded_interfaces_mask & BIT(USB_INTERFACE_CDC)) { - max_endpoints = 5; //CDC endpoint 0x85 is actually not linked to FIFO and not used + max_endpoints = CFG_TUD_NUM_IN_EPS; //CDC endpoint 0x85 is actually not linked to FIFO and not used } - for (uint8_t i = 1; i < 7; i++) { +#endif + for (uint8_t i = 1; i <= CFG_TUD_NUM_EPS; i++) { if ((tinyusb_endpoints.in & BIT(i)) != 0) { active_endpoints++; } @@ -771,7 +773,7 @@ static void usb_device_task(void *param) { * PUBLIC API * */ #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR -const char *tinyusb_interface_names[USB_INTERFACE_MAX] = {"MSC", "DFU", "HID", "VENDOR", "CDC", "MIDI", "CUSTOM"}; +const char *tinyusb_interface_names[USB_INTERFACE_MAX] = {"MSC", "DFU", "HID", "VENDOR", "CDC", "CDC2", "MIDI", "CUSTOM"}; #endif static bool tinyusb_is_initialized = false; @@ -862,7 +864,7 @@ uint8_t tinyusb_get_free_duplex_endpoint(void) { log_e("No available IN endpoints"); return 0; } - for (uint8_t i = 1; i < 7; i++) { + for (uint8_t i = 1; i <= CFG_TUD_NUM_IN_EPS; i++) { if ((tinyusb_endpoints.in & BIT(i)) == 0 && (tinyusb_endpoints.out & BIT(i)) == 0) { tinyusb_endpoints.in |= BIT(i); tinyusb_endpoints.out |= BIT(i); @@ -878,13 +880,13 @@ uint8_t tinyusb_get_free_in_endpoint(void) { log_e("No available IN endpoints"); return 0; } - for (uint8_t i = 1; i < 7; i++) { + for (uint8_t i = 1; i <= CFG_TUD_NUM_IN_EPS; i++) { if ((tinyusb_endpoints.in & BIT(i)) == 0 && (tinyusb_endpoints.out & BIT(i)) != 0) { tinyusb_endpoints.in |= BIT(i); return i; } } - for (uint8_t i = 1; i < 7; i++) { + for (uint8_t i = 1; i <= CFG_TUD_NUM_IN_EPS; i++) { if ((tinyusb_endpoints.in & BIT(i)) == 0) { tinyusb_endpoints.in |= BIT(i); return i; @@ -894,13 +896,13 @@ uint8_t tinyusb_get_free_in_endpoint(void) { } uint8_t tinyusb_get_free_out_endpoint(void) { - for (uint8_t i = 1; i < 7; i++) { + for (uint8_t i = 1; i <= CFG_TUD_NUM_EPS; i++) { if ((tinyusb_endpoints.out & BIT(i)) == 0 && (tinyusb_endpoints.in & BIT(i)) != 0) { tinyusb_endpoints.out |= BIT(i); return i; } } - for (uint8_t i = 1; i < 7; i++) { + for (uint8_t i = 1; i <= CFG_TUD_NUM_EPS; i++) { if ((tinyusb_endpoints.out & BIT(i)) == 0) { tinyusb_endpoints.out |= BIT(i); return i; diff --git a/cores/esp32/esp32-hal-tinyusb.h b/cores/esp32/esp32-hal-tinyusb.h index 0b42760e69f..73210c4872b 100644 --- a/cores/esp32/esp32-hal-tinyusb.h +++ b/cores/esp32/esp32-hal-tinyusb.h @@ -38,6 +38,13 @@ extern "C" { #define CFG_TUD_ENDOINT_SIZE 64 #endif #endif +#if CONFIG_IDF_TARGET_ESP32P4 +#define CFG_TUD_NUM_EPS 15 +#define CFG_TUD_NUM_IN_EPS 8 +#else +#define CFG_TUD_NUM_EPS 6 +#define CFG_TUD_NUM_IN_EPS 5 +#endif typedef struct { uint16_t vid; @@ -88,6 +95,7 @@ typedef enum { USB_INTERFACE_HID, USB_INTERFACE_VENDOR, USB_INTERFACE_CDC, + USB_INTERFACE_CDC2, USB_INTERFACE_MIDI, USB_INTERFACE_CUSTOM, USB_INTERFACE_MAX From 6c3a49cac7815a160fe70da07bac66e56b6ef547 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Feb 2025 13:01:13 +0200 Subject: [PATCH 50/60] build(deps): bump cryptography from 43.0.1 to 44.0.1 in /tests (#10961) Bumps [cryptography](https://github.com/pyca/cryptography) from 43.0.1 to 44.0.1. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/43.0.1...44.0.1) --- updated-dependencies: - dependency-name: cryptography dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/requirements.txt b/tests/requirements.txt index cef0bf17881..c98c032a3ae 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,4 +1,4 @@ -cryptography==43.0.1 +cryptography==44.0.1 --only-binary cryptography pytest-cov==5.0.0 pytest-embedded-serial-esp==1.12.0 From 83abca1604d6118269f04228f57e93f493241e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Thu, 13 Feb 2025 12:02:35 +0100 Subject: [PATCH 51/60] feat(zigbee): Add OTA client cluster support (#10946) * feat(zigbee): Add OTA client cluster support * feat(zigbee): Add conditions to reject OTA upgrade * feat(zigbee): Add newest version of OTA handler * fix(zigbee): Fix errors and warnings, swap parameters order * feat(zigbee): Add simple OTA Client example * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../examples/Zigbee_OTA_Client/README.md | 68 ++++++ .../Zigbee_OTA_Client/Zigbee_OTA_Client.ino | 112 ++++++++++ .../Zigbee/examples/Zigbee_OTA_Client/ci.json | 6 + libraries/Zigbee/src/ZigbeeEP.cpp | 68 ++++++ libraries/Zigbee/src/ZigbeeEP.h | 23 +- libraries/Zigbee/src/ZigbeeHandlers.cpp | 205 ++++++++++++++++-- 6 files changed, 465 insertions(+), 17 deletions(-) create mode 100644 libraries/Zigbee/examples/Zigbee_OTA_Client/README.md create mode 100644 libraries/Zigbee/examples/Zigbee_OTA_Client/Zigbee_OTA_Client.ino create mode 100644 libraries/Zigbee/examples/Zigbee_OTA_Client/ci.json diff --git a/libraries/Zigbee/examples/Zigbee_OTA_Client/README.md b/libraries/Zigbee/examples/Zigbee_OTA_Client/README.md new file mode 100644 index 00000000000..143ff946f28 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_OTA_Client/README.md @@ -0,0 +1,68 @@ +# Arduino-ESP32 Zigbee OTA Client + on/off light Example + +This example shows how to configure the Zigbee end device with OTA Client and use it as a Home Automation (HA) on/off light. + +# Supported Targets + +Currently, this example supports the following targets. + +| Supported Targets | ESP32-C6 | ESP32-H2 | +| ----------------- | -------- | -------- | + +## Hardware Required + +* A USB cable for power supply and programming + +### Configure the Project + +Set the LED GPIO by changing the `LED_PIN` definition. By default, the LED_PIN is `RGB_BUILTIN`. +By default, the `rgbLedWrite` function is used to control the LED. You can change it to digitalWrite to control a simple LED. + +#### Using Arduino IDE + +To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits). + +* Before Compile/Verify, select the correct board: `Tools -> Board`. +* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)` +* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs` +* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port. +* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`. + +## Troubleshooting + +If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator. +You can do the following: + +* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`. +* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack. + +By default, the coordinator network is closed after rebooting or flashing new firmware. +To open the network you have 2 options: + +* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`. +* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join. + + +***Important: Make sure you are using a good quality USB cable and that you have a reliable power source*** + +* **LED not blinking:** Check the wiring connection and the IO selection. +* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed. +* **COM port not detected:** Check the USB cable and the USB to Serial driver installation. + +If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute). + +## Contribute + +To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst) + +If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome! + +Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else. + +## Resources + +* Official ESP32 Forum: [Link](https://esp32.com) +* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32) +* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf) +* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf) +* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com) diff --git a/libraries/Zigbee/examples/Zigbee_OTA_Client/Zigbee_OTA_Client.ino b/libraries/Zigbee/examples/Zigbee_OTA_Client/Zigbee_OTA_Client.ino new file mode 100644 index 00000000000..29d114014b4 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_OTA_Client/Zigbee_OTA_Client.ino @@ -0,0 +1,112 @@ +// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @brief This example demonstrates OTA support on light bulb. + * + * The example demonstrates how to use Zigbee library to create a end device light bulb with OTA support. + * The light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator. + * + * Proper Zigbee mode must be selected in Tools->Zigbee mode + * and also the correct partition scheme must be selected in Tools->Partition Scheme. + * + * Please check the README.md for instructions and more detailed description. + * + * Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/) + */ + +#ifndef ZIGBEE_MODE_ED +#error "Zigbee end device mode is not selected in Tools->Zigbee mode" +#endif + +#include "Zigbee.h" + +/* Zigbee light bulb configuration */ +#define ZIGBEE_LIGHT_ENDPOINT 1 +uint8_t led = RGB_BUILTIN; +uint8_t button = BOOT_PIN; + +/* Zigbee OTA configuration */ +#define OTA_UPGRADE_RUNNING_FILE_VERSION 0x01010100 // Increment this value when the running image is updated +#define OTA_UPGRADE_DOWNLOADED_FILE_VERSION 0x01010101 // Increment this value when the downloaded image is updated +#define OTA_UPGRADE_HW_VERSION 0x0101 // The hardware version, this can be used to differentiate between different hardware versions + +ZigbeeLight zbLight = ZigbeeLight(ZIGBEE_LIGHT_ENDPOINT); + +/********************* RGB LED functions **************************/ +void setLED(bool value) { + digitalWrite(led, value); +} + +/********************* Arduino functions **************************/ +void setup() { + Serial.begin(115200); + + // Init LED and turn it OFF (if LED_PIN == RGB_BUILTIN, the rgbLedWrite() will be used under the hood) + pinMode(led, OUTPUT); + digitalWrite(led, LOW); + + // Init button for factory reset + pinMode(button, INPUT_PULLUP); + + // Optional: set Zigbee device name and model + zbLight.setManufacturerAndModel("Espressif", "ZBLightBulb"); + + // Set callback function for light change + zbLight.onLightChange(setLED); + + // Add OTA client to the light bulb + zbLight.addOTAClient(OTA_UPGRADE_RUNNING_FILE_VERSION, OTA_UPGRADE_DOWNLOADED_FILE_VERSION, OTA_UPGRADE_HW_VERSION); + + // Add endpoint to Zigbee Core + Serial.println("Adding ZigbeeLight endpoint to Zigbee Core"); + Zigbee.addEndpoint(&zbLight); + + // When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE + if (!Zigbee.begin()) { + Serial.println("Zigbee failed to start!"); + Serial.println("Rebooting..."); + ESP.restart(); + } + Serial.println("Connecting to network"); + while (!Zigbee.connected()) { + Serial.print("."); + delay(100); + } + Serial.println(); + + // Start Zigbee OTA client query, first request is within a minute and the next requests are sent every hour automatically + zbLight.requestOTAUpdate(); +} + +void loop() { + // Checking button for factory reset + if (digitalRead(button) == LOW) { // Push button pressed + // Key debounce handling + delay(100); + int startTime = millis(); + while (digitalRead(button) == LOW) { + delay(50); + if ((millis() - startTime) > 3000) { + // If key pressed for more than 3secs, factory reset Zigbee and reboot + Serial.println("Resetting Zigbee to factory and rebooting in 1s."); + delay(1000); + Zigbee.factoryReset(); + } + } + // Toggle light by pressing the button + zbLight.setLight(!zbLight.getLightState()); + } + delay(100); +} diff --git a/libraries/Zigbee/examples/Zigbee_OTA_Client/ci.json b/libraries/Zigbee/examples/Zigbee_OTA_Client/ci.json new file mode 100644 index 00000000000..7b7ccef8ed7 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_OTA_Client/ci.json @@ -0,0 +1,6 @@ +{ + "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", + "requires": [ + "CONFIG_SOC_IEEE802154_SUPPORTED=y" + ] +} diff --git a/libraries/Zigbee/src/ZigbeeEP.cpp b/libraries/Zigbee/src/ZigbeeEP.cpp index 6a2ace0b90c..f3f890393eb 100644 --- a/libraries/Zigbee/src/ZigbeeEP.cpp +++ b/libraries/Zigbee/src/ZigbeeEP.cpp @@ -363,4 +363,72 @@ void ZigbeeEP::zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute) { } } +// typedef struct esp_zb_ota_cluster_cfg_s { +// uint32_t ota_upgrade_file_version; /*!< The attribute indicates the file version of the running firmware image on the device */ +// uint16_t ota_upgrade_manufacturer; /*!< The attribute indicates the value for the manufacturer of the device */ +// uint16_t ota_upgrade_image_type; /*!< The attribute indicates the the image type of the file that the client is currently downloading */ +// uint32_t ota_upgrade_downloaded_file_ver; /*!< The attribute indicates the file version of the downloaded image on the device*/ +// esp_zb_ota_cluster_cfg_t; + +// typedef struct esp_zb_zcl_ota_upgrade_client_variable_s { +// uint16_t timer_query; /*!< The field indicates the time of querying OTA image for OTA upgrade client */ +// uint16_t hw_version; /*!< The hardware version */ +// uint8_t max_data_size; /*!< The maximum size of OTA data */ +// } esp_zb_zcl_ota_upgrade_client_variable_t; + +void ZigbeeEP::addOTAClient( + uint32_t file_version, uint32_t downloaded_file_ver, uint16_t hw_version, uint16_t manufacturer, uint16_t image_type, uint8_t max_data_size +) { + + esp_zb_ota_cluster_cfg_t ota_cluster_cfg = {}; + ota_cluster_cfg.ota_upgrade_file_version = file_version; //OTA_UPGRADE_RUNNING_FILE_VERSION; + ota_cluster_cfg.ota_upgrade_downloaded_file_ver = downloaded_file_ver; //OTA_UPGRADE_DOWNLOADED_FILE_VERSION; + ota_cluster_cfg.ota_upgrade_manufacturer = manufacturer; //OTA_UPGRADE_MANUFACTURER; + ota_cluster_cfg.ota_upgrade_image_type = image_type; //OTA_UPGRADE_IMAGE_TYPE; + + esp_zb_attribute_list_t *ota_cluster = esp_zb_ota_cluster_create(&ota_cluster_cfg); + + esp_zb_zcl_ota_upgrade_client_variable_t variable_config = {}; + variable_config.timer_query = ESP_ZB_ZCL_OTA_UPGRADE_QUERY_TIMER_COUNT_DEF; + variable_config.hw_version = hw_version; //OTA_UPGRADE_HW_VERSION; + variable_config.max_data_size = max_data_size; //OTA_UPGRADE_MAX_DATA_SIZE; + + uint16_t ota_upgrade_server_addr = 0xffff; + uint8_t ota_upgrade_server_ep = 0xff; + + ESP_ERROR_CHECK(esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_CLIENT_DATA_ID, (void *)&variable_config)); + ESP_ERROR_CHECK(esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_SERVER_ADDR_ID, (void *)&ota_upgrade_server_addr)); + ESP_ERROR_CHECK(esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_SERVER_ENDPOINT_ID, (void *)&ota_upgrade_server_ep)); + + ESP_ERROR_CHECK(esp_zb_cluster_list_add_ota_cluster(_cluster_list, ota_cluster, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE)); +} + +static void findOTAServer(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) { + if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) { + esp_zb_ota_upgrade_client_query_interval_set(*((uint8_t *)user_ctx), OTA_UPGRADE_QUERY_INTERVAL); + esp_zb_ota_upgrade_client_query_image_req(addr, endpoint); + log_i("Query OTA upgrade from server endpoint: %d after %d seconds", endpoint, OTA_UPGRADE_QUERY_INTERVAL); + } else { + log_w("No OTA Server found"); + } +} + +void ZigbeeEP::requestOTAUpdate() { + esp_zb_zdo_match_desc_req_param_t req; + uint16_t cluster_list[] = {ESP_ZB_ZCL_CLUSTER_ID_OTA_UPGRADE}; + + /* Match the OTA server of coordinator */ + req.addr_of_interest = 0x0000; + req.dst_nwk_addr = 0x0000; + req.num_in_clusters = 1; + req.num_out_clusters = 0; + req.profile_id = ESP_ZB_AF_HA_PROFILE_ID; + req.cluster_list = cluster_list; + esp_zb_lock_acquire(portMAX_DELAY); + if (esp_zb_bdb_dev_joined()) { + esp_zb_zdo_match_cluster(&req, findOTAServer, &_endpoint); + } + esp_zb_lock_release(); +} + #endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index 584873f50dd..72cde275293 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -8,7 +8,8 @@ #include /* Useful defines */ -#define ZB_CMD_TIMEOUT 10000 // 10 seconds +#define ZB_CMD_TIMEOUT 10000 // 10 seconds +#define OTA_UPGRADE_QUERY_INTERVAL (1 * 60) // 1 hour = 60 minutes #define ZB_ARRAY_LENTH(arr) (sizeof(arr) / sizeof(arr[0])) #define XYZ_TO_RGB(X, Y, Z, r, g, b) \ @@ -107,6 +108,26 @@ class ZigbeeEP { return _allow_multiple_binding; } + // OTA methods + /** + * @brief Add OTA client to the Zigbee endpoint. + * + * @param file_version The current file version of the OTA client. + * @param downloaded_file_ver The version of the downloaded file. + * @param hw_version The hardware version of the device. + * @param manufacturer The manufacturer code (default: 0x1001). + * @param image_type The image type code (default: 0x1011). + * @param max_data_size The maximum data size for OTA transfer (default and recommended: 223). + */ + void addOTAClient( + uint32_t file_version, uint32_t downloaded_file_ver, uint16_t hw_version, uint16_t manufacturer = 0x1001, uint16_t image_type = 0x1011, + uint8_t max_data_size = 223 + ); + /** + * @brief Request OTA update from the server, first request is within a minute and the next requests are sent every hour automatically. + */ + void requestOTAUpdate(); + // findEndpoind may be implemented by EPs to find and bind devices virtual void findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req) {}; diff --git a/libraries/Zigbee/src/ZigbeeHandlers.cpp b/libraries/Zigbee/src/ZigbeeHandlers.cpp index 575b25ef404..3af1b6c52aa 100644 --- a/libraries/Zigbee/src/ZigbeeHandlers.cpp +++ b/libraries/Zigbee/src/ZigbeeHandlers.cpp @@ -4,6 +4,26 @@ #if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED +#include "esp_ota_ops.h" +#if CONFIG_ZB_DELTA_OTA // Delta OTA, code is prepared for this feature but not enabled by default +#include "esp_delta_ota_ops.h" +#endif + +//OTA Upgrade defines and variables +#define OTA_ELEMENT_HEADER_LEN 6 /* OTA element format header size include tag identifier and length field */ + +/** + * @name Enumeration for the tag identifier denotes the type and format of the data within the element + * @anchor esp_ota_element_tag_id_t + */ +typedef enum esp_ota_element_tag_id_e { + UPGRADE_IMAGE = 0x0000, /*!< Upgrade image */ +} esp_ota_element_tag_id_t; + +static const esp_partition_t *s_ota_partition = NULL; +static esp_ota_handle_t s_ota_handle = 0; +static bool s_tagid_received = false; + // forward declaration of all implemented handlers static esp_err_t zb_attribute_set_handler(const esp_zb_zcl_set_attr_value_message_t *message); static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_message_t *message); @@ -13,6 +33,8 @@ static esp_err_t zb_cmd_ias_zone_status_change_handler(const esp_zb_zcl_ias_zone static esp_err_t zb_cmd_ias_zone_enroll_response_handler(const esp_zb_zcl_ias_zone_enroll_response_message_t *message); static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message); static esp_err_t zb_window_covering_movement_resp_handler(const esp_zb_zcl_window_covering_movement_message_t *message); +static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_value_message_t *message); +static esp_err_t zb_ota_upgrade_query_image_resp_handler(const esp_zb_zcl_ota_upgrade_query_image_resp_message_t *message); // Zigbee action handlers [[maybe_unused]] @@ -32,6 +54,10 @@ static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, case ESP_ZB_CORE_WINDOW_COVERING_MOVEMENT_CB_ID: ret = zb_window_covering_movement_resp_handler((esp_zb_zcl_window_covering_movement_message_t *)message); break; + case ESP_ZB_CORE_OTA_UPGRADE_VALUE_CB_ID: ret = zb_ota_upgrade_status_handler((esp_zb_zcl_ota_upgrade_value_message_t *)message); break; + case ESP_ZB_CORE_OTA_UPGRADE_QUERY_IMAGE_RESP_CB_ID: + ret = zb_ota_upgrade_query_image_resp_handler((esp_zb_zcl_ota_upgrade_query_image_resp_message_t *)message); + break; case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; } @@ -186,22 +212,6 @@ static esp_err_t zb_cmd_ias_zone_enroll_response_handler(const esp_zb_zcl_ias_zo return ESP_OK; } -static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message) { - if (!message) { - log_e("Empty message"); - return ESP_FAIL; - } - if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { - log_e("Received message: error status(%d)", message->info.status); - return ESP_ERR_INVALID_ARG; - } - log_v( - "Received default response: from address(0x%x), src_endpoint(%d) to dst_endpoint(%d), cluster(0x%x) with status 0x%x", - message->info.src_address.u.short_addr, message->info.src_endpoint, message->info.dst_endpoint, message->info.cluster, message->status_code - ); - return ESP_OK; -} - static esp_err_t zb_window_covering_movement_resp_handler(const esp_zb_zcl_window_covering_movement_message_t *message) { if (!message) { log_e("Empty message"); @@ -224,4 +234,167 @@ static esp_err_t zb_window_covering_movement_resp_handler(const esp_zb_zcl_windo return ESP_OK; } +static esp_err_t esp_element_ota_data(uint32_t total_size, const void *payload, uint16_t payload_size, void **outbuf, uint16_t *outlen) { + static uint16_t tagid = 0; + void *data_buf = NULL; + uint16_t data_len; + + if (!s_tagid_received) { + uint32_t length = 0; + if (!payload || payload_size <= OTA_ELEMENT_HEADER_LEN) { + log_e("Invalid element format"); + return ESP_ERR_INVALID_ARG; + } + + const uint8_t *payload_ptr = (const uint8_t *)payload; + tagid = *(const uint16_t *)payload_ptr; + length = *(const uint32_t *)(payload_ptr + sizeof(tagid)); + if ((length + OTA_ELEMENT_HEADER_LEN) != total_size) { + log_e("Invalid element length [%ld/%ld]", length, total_size); + return ESP_ERR_INVALID_ARG; + } + + s_tagid_received = true; + + data_buf = (void *)(payload_ptr + OTA_ELEMENT_HEADER_LEN); + data_len = payload_size - OTA_ELEMENT_HEADER_LEN; + } else { + data_buf = (void *)payload; + data_len = payload_size; + } + + switch (tagid) { + case UPGRADE_IMAGE: + *outbuf = data_buf; + *outlen = data_len; + break; + default: + log_e("Unsupported element tag identifier %d", tagid); + return ESP_ERR_INVALID_ARG; + break; + } + + return ESP_OK; +} + +static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_value_message_t *message) { + static uint32_t total_size = 0; + static uint32_t offset = 0; + [[maybe_unused]] + static int64_t start_time = 0; + esp_err_t ret = ESP_OK; + + if (message->info.status == ESP_ZB_ZCL_STATUS_SUCCESS) { + switch (message->upgrade_status) { + case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_START: + log_i("Zigbee - OTA upgrade start"); + start_time = esp_timer_get_time(); + s_ota_partition = esp_ota_get_next_update_partition(NULL); + assert(s_ota_partition); +#if CONFIG_ZB_DELTA_OTA + ret = esp_delta_ota_begin(s_ota_partition, 0, &s_ota_handle); +#else + ret = esp_ota_begin(s_ota_partition, 0, &s_ota_handle); +#endif + if (ret != ESP_OK) { + log_e("Zigbee - Failed to begin OTA partition, status: %s", esp_err_to_name(ret)); + return ret; + } + break; + case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_RECEIVE: + total_size = message->ota_header.image_size; + offset += message->payload_size; + log_i("Zigbee - OTA Client receives data: progress [%ld/%ld]", offset, total_size); + if (message->payload_size && message->payload) { + uint16_t payload_size = 0; + void *payload = NULL; + ret = esp_element_ota_data(total_size, message->payload, message->payload_size, &payload, &payload_size); + if (ret != ESP_OK) { + log_e("Zigbee - Failed to element OTA data, status: %s", esp_err_to_name(ret)); + return ret; + } +#if CONFIG_ZB_DELTA_OTA + ret = esp_delta_ota_write(s_ota_handle, payload, payload_size); +#else + ret = esp_ota_write(s_ota_handle, (const void *)payload, payload_size); +#endif + if (ret != ESP_OK) { + log_e("Zigbee - Failed to write OTA data to partition, status: %s", esp_err_to_name(ret)); + return ret; + } + } + break; + case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_APPLY: log_i("Zigbee - OTA upgrade apply"); break; + case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_CHECK: + ret = offset == total_size ? ESP_OK : ESP_FAIL; + offset = 0; + total_size = 0; + s_tagid_received = false; + log_i("Zigbee - OTA upgrade check status: %s", esp_err_to_name(ret)); + break; + case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_FINISH: + log_i("Zigbee - OTA Finish"); + log_i( + "Zigbee - OTA Information: version: 0x%lx, manufacturer code: 0x%x, image type: 0x%x, total size: %ld bytes, cost time: %lld ms,", + message->ota_header.file_version, message->ota_header.manufacturer_code, message->ota_header.image_type, message->ota_header.image_size, + (esp_timer_get_time() - start_time) / 1000 + ); +#if CONFIG_ZB_DELTA_OTA + ret = esp_delta_ota_end(s_ota_handle); +#else + ret = esp_ota_end(s_ota_handle); +#endif + if (ret != ESP_OK) { + log_e("Zigbee - Failed to end OTA partition, status: %s", esp_err_to_name(ret)); + return ret; + } + ret = esp_ota_set_boot_partition(s_ota_partition); + if (ret != ESP_OK) { + log_e("Zigbee - Failed to set OTA boot partition, status: %s", esp_err_to_name(ret)); + return ret; + } + log_w("Zigbee - Prepare to restart system"); + esp_restart(); + break; + default: log_i("Zigbee - OTA status: %d", message->upgrade_status); break; + } + } + return ret; +} + +static esp_err_t zb_ota_upgrade_query_image_resp_handler(const esp_zb_zcl_ota_upgrade_query_image_resp_message_t *message) { + if (message->info.status == ESP_ZB_ZCL_STATUS_SUCCESS) { + log_i("Zigbee - Queried OTA image from address: 0x%04hx, endpoint: %d", message->server_addr.u.short_addr, message->server_endpoint); + log_i("Zigbee - Image version: 0x%lx, manufacturer code: 0x%x, image size: %ld", message->file_version, message->manufacturer_code, message->image_size); + if (message->image_size == 0) { + log_i("Zigbee - Rejecting OTA image upgrade, image size is 0"); + return ESP_FAIL; + } + if (message->file_version == 0) { + log_i("Zigbee - Rejecting OTA image upgrade, file version is 0"); + return ESP_FAIL; + } + log_i("Zigbee - Approving OTA image upgrade"); + } else { + log_i("Zigbee - OTA image upgrade response status: 0x%x", message->info.status); + } + return ESP_OK; +} + +static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message) { + if (!message) { + log_e("Empty message"); + return ESP_FAIL; + } + if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Received message: error status(%d)", message->info.status); + return ESP_ERR_INVALID_ARG; + } + log_v( + "Received default response: from address(0x%x), src_endpoint(%d) to dst_endpoint(%d), cluster(0x%x) with status 0x%x", + message->info.src_address.u.short_addr, message->info.src_endpoint, message->info.dst_endpoint, message->info.cluster, message->status_code + ); + return ESP_OK; +} + #endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED From 7f8c77f9ee7cfac433ef21304c7abf01cf2b86a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Thu, 13 Feb 2025 12:03:02 +0100 Subject: [PATCH 52/60] feat(zigbee): Add vibration sensor endpoint (#10944) --- CMakeLists.txt | 1 + .../Zigbee_Vibration_Sensor/README.md | 58 ++++++++++ .../Zigbee_Vibration_Sensor.ino | 104 ++++++++++++++++++ .../examples/Zigbee_Vibration_Sensor/ci.json | 6 + libraries/Zigbee/src/Zigbee.h | 1 + .../Zigbee/src/ep/ZigbeeVibrationSensor.cpp | 86 +++++++++++++++ .../Zigbee/src/ep/ZigbeeVibrationSensor.h | 64 +++++++++++ 7 files changed, 320 insertions(+) create mode 100644 libraries/Zigbee/examples/Zigbee_Vibration_Sensor/README.md create mode 100644 libraries/Zigbee/examples/Zigbee_Vibration_Sensor/Zigbee_Vibration_Sensor.ino create mode 100644 libraries/Zigbee/examples/Zigbee_Vibration_Sensor/ci.json create mode 100644 libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp create mode 100644 libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 40567d1b45c..5ee4ce664ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -293,6 +293,7 @@ set(ARDUINO_LIBRARY_Zigbee_SRCS libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp + libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp ) set(ARDUINO_LIBRARY_BLE_SRCS diff --git a/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/README.md b/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/README.md new file mode 100644 index 00000000000..b0e5b5f09e8 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/README.md @@ -0,0 +1,58 @@ +# Arduino-ESP32 Zigbee Vibration Sensor Example + +This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) vibration sensor (IAS Zone), +that can be used for example as a security device which is sensing a vibrations. + +# Supported Targets + +Currently, this example supports the following targets. + +| Supported Targets | ESP32-C6 | ESP32-H2 | +| ----------------- | -------- | -------- | + +## Hardware Required + +* A USB cable for power supply and programming + +### Configure the Project + +Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2). +Set the Sensor GPIO by changing the `sensor_pin` variable. + +#### Using Arduino IDE + +To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits). + +* Before Compile/Verify, select the correct board: `Tools -> Board`. +* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)` +* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs` +* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port. +* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`. + +## Troubleshooting + +If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. + +***Important: Make sure you are using a good quality USB cable and that you have a reliable power source*** + +* **LED not blinking:** Check the wiring connection and the IO selection. +* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed. +* **COM port not detected:** Check the USB cable and the USB to Serial driver installation. + +If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute). + +## Contribute + +To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst) + +If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome! + +Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else. + +## Resources + +* Official ESP32 Forum: [Link](https://esp32.com) +* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32) +* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf) +* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf) +* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com) diff --git a/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/Zigbee_Vibration_Sensor.ino b/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/Zigbee_Vibration_Sensor.ino new file mode 100644 index 00000000000..d9ac7b6e241 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/Zigbee_Vibration_Sensor.ino @@ -0,0 +1,104 @@ +// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @brief This example demonstrates Zigbee vibration sensor (IAS Zone). + * + * The example demonstrates how to use Zigbee library to create a end device vibration sensor. + * The vibration sensor is a Zigbee end device, which is reporting data to the Zigbee network. + * + * Proper Zigbee mode must be selected in Tools->Zigbee mode + * and also the correct partition scheme must be selected in Tools->Partition Scheme. + * + * Please check the README.md for instructions and more detailed description. + * + * Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/) + */ + +#ifndef ZIGBEE_MODE_ED +#error "Zigbee end device mode is not selected in Tools->Zigbee mode" +#endif + +#include "Zigbee.h" + +/* Zigbee vibration sensor configuration */ +#define VIBRATION_SENSOR_ENDPOINT_NUMBER 10 +uint8_t button = BOOT_PIN; +uint8_t sensor_pin = 4; + +ZigbeeVibrationSensor zbVibrationSensor = ZigbeeVibrationSensor(VIBRATION_SENSOR_ENDPOINT_NUMBER); + +void setup() { + Serial.begin(115200); + + // Init button + sensor + pinMode(button, INPUT_PULLUP); + pinMode(sensor_pin, INPUT); + + // Optional: set Zigbee device name and model + zbVibrationSensor.setManufacturerAndModel("Espressif", "ZigbeeVibrationSensor"); + + // Add endpoint to Zigbee Core + Zigbee.addEndpoint(&zbVibrationSensor); + + Serial.println("Starting Zigbee..."); + // When all EPs are registered, start Zigbee in End Device mode + if (!Zigbee.begin()) { + Serial.println("Zigbee failed to start!"); + Serial.println("Rebooting..."); + ESP.restart(); + } else { + Serial.println("Zigbee started successfully!"); + } + Serial.println("Connecting to network"); + while (!Zigbee.connected()) { + Serial.print("."); + delay(100); + } + Serial.println(); +} + +void loop() { + // Checking pin for contact change + static bool sensed = false; + if (digitalRead(sensor_pin) == HIGH && !sensed) { + // Update contact sensor value + zbVibrationSensor.setVibration(true); + sensed = true; + //if sensed, wait 2 seconds before next sensing + delay(2000); + } else if (digitalRead(sensor_pin) == LOW && sensed) { + zbVibrationSensor.setVibration(false); + sensed = false; + //if not sensed, wait 0,5 seconds before next sensing + delay(500); + } + + // Checking button for factory reset + if (digitalRead(button) == LOW) { // Push button pressed + // Key debounce handling + delay(100); + int startTime = millis(); + while (digitalRead(button) == LOW) { + delay(50); + if ((millis() - startTime) > 3000) { + // If key pressed for more than 3secs, factory reset Zigbee and reboot + Serial.println("Resetting Zigbee to factory and rebooting in 1s."); + delay(1000); + Zigbee.factoryReset(); + } + } + } + delay(100); +} diff --git a/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/ci.json b/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/ci.json new file mode 100644 index 00000000000..7b7ccef8ed7 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/ci.json @@ -0,0 +1,6 @@ +{ + "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", + "requires": [ + "CONFIG_SOC_IEEE802154_SUPPORTED=y" + ] +} diff --git a/libraries/Zigbee/src/Zigbee.h b/libraries/Zigbee/src/Zigbee.h index 49bfa44d5c1..b785c28d0df 100644 --- a/libraries/Zigbee/src/Zigbee.h +++ b/libraries/Zigbee/src/Zigbee.h @@ -21,3 +21,4 @@ #include "ep/ZigbeeContactSwitch.h" #include "ep/ZigbeeDoorWindowHandle.h" #include "ep/ZigbeeWindowCovering.h" +#include "ep/ZigbeeVibrationSensor.h" diff --git a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp new file mode 100644 index 00000000000..2f2172c89df --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp @@ -0,0 +1,86 @@ +#include "ZigbeeVibrationSensor.h" +#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED + +esp_zb_cluster_list_t *zigbee_vibration_sensor_clusters_create(zigbee_vibration_sensor_cfg_t *vibration_sensor) { + esp_zb_basic_cluster_cfg_t *basic_cfg = vibration_sensor ? &(vibration_sensor->basic_cfg) : NULL; + esp_zb_identify_cluster_cfg_t *identify_cfg = vibration_sensor ? &(vibration_sensor->identify_cfg) : NULL; + esp_zb_ias_zone_cluster_cfg_t *ias_zone_cfg = vibration_sensor ? &(vibration_sensor->ias_zone_cfg) : NULL; + esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create(); + esp_zb_cluster_list_add_basic_cluster(cluster_list, esp_zb_basic_cluster_create(basic_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(identify_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_ias_zone_cluster(cluster_list, esp_zb_ias_zone_cluster_create(ias_zone_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + return cluster_list; +} + +ZigbeeVibrationSensor::ZigbeeVibrationSensor(uint8_t endpoint) : ZigbeeEP(endpoint) { + _device_id = ESP_ZB_HA_IAS_ZONE_ID; + _zone_status = 0; + _zone_id = 0xff; + _ias_cie_endpoint = 1; + + //Create custom vibration sensor configuration + zigbee_vibration_sensor_cfg_t vibration_sensor_cfg = ZIGBEE_DEFAULT_VIBRATION_SENSOR_CONFIG(); + _cluster_list = zigbee_vibration_sensor_clusters_create(&vibration_sensor_cfg); + + _ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_IAS_ZONE_ID, .app_device_version = 0}; +} + +void ZigbeeVibrationSensor::setIASClientEndpoint(uint8_t ep_number) { + _ias_cie_endpoint = ep_number; +} + +void ZigbeeVibrationSensor::setVibration(bool sensed) { + log_v("Setting Vibration sensor to %s", sensed ? "sensed" : "not sensed"); + uint8_t vibration = (uint8_t)sensed; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONESTATUS_ID, &vibration, false + ); + esp_zb_lock_release(); + _zone_status = vibration; + report(); +} + +void ZigbeeVibrationSensor::report() { + /* Send IAS Zone status changed notification command */ + + esp_zb_zcl_ias_zone_status_change_notif_cmd_t status_change_notif_cmd; + status_change_notif_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + status_change_notif_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + status_change_notif_cmd.zcl_basic_cmd.dst_endpoint = _ias_cie_endpoint; //default is 1 + memcpy(status_change_notif_cmd.zcl_basic_cmd.dst_addr_u.addr_long, _ias_cie_addr, sizeof(esp_zb_ieee_addr_t)); + + status_change_notif_cmd.zone_status = _zone_status; + status_change_notif_cmd.extend_status = 0; + status_change_notif_cmd.zone_id = _zone_id; + status_change_notif_cmd.delay = 0; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); + esp_zb_lock_release(); + log_v("IAS Zone status changed notification sent"); +} + +void ZigbeeVibrationSensor::zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) { + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE) { + log_v("IAS Zone Enroll Response: zone id(%d), status(%d)", message->zone_id, message->response_code); + if (message->response_code == ESP_ZB_ZCL_IAS_ZONE_ENROLL_RESPONSE_CODE_SUCCESS) { + log_v("IAS Zone Enroll Response: success"); + esp_zb_lock_acquire(portMAX_DELAY); + memcpy( + _ias_cie_addr, + (*(esp_zb_ieee_addr_t *) + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_IAS_CIE_ADDRESS_ID) + ->data_p), + sizeof(esp_zb_ieee_addr_t) + ); + esp_zb_lock_release(); + _zone_id = message->zone_id; + } + + } else { + log_w("Received message ignored. Cluster ID: %d not supported for On/Off Light", message->info.cluster); + } +} + +#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h new file mode 100644 index 00000000000..799cc943cdb --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h @@ -0,0 +1,64 @@ +/* Class of Zigbee contact switch (IAS Zone) endpoint inherited from common EP class */ + +#pragma once + +#include "soc/soc_caps.h" +#include "sdkconfig.h" +#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED + +#include "ZigbeeEP.h" +#include "ha/esp_zigbee_ha_standard.h" + +// clang-format off +#define ZIGBEE_DEFAULT_VIBRATION_SENSOR_CONFIG() \ + { \ + .basic_cfg = \ + { \ + .zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE, \ + .power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE, \ + }, \ + .identify_cfg = \ + { \ + .identify_time = ESP_ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE, \ + }, \ + .ias_zone_cfg = \ + { \ + .zone_state = ESP_ZB_ZCL_IAS_ZONE_ZONESTATE_NOT_ENROLLED, \ + .zone_type = ESP_ZB_ZCL_IAS_ZONE_ZONETYPE_VIBRATION_MOVEMENT, \ + .zone_status = 0, \ + .ias_cie_addr = ESP_ZB_ZCL_ZONE_IAS_CIE_ADDR_DEFAULT, \ + .zone_id = 0xff, \ + .zone_ctx = {0, 0, 0, 0}, \ + }, \ + } +// clang-format on + +typedef struct zigbee_vibration_sensor_cfg_s { + esp_zb_basic_cluster_cfg_t basic_cfg; + esp_zb_identify_cluster_cfg_t identify_cfg; + esp_zb_ias_zone_cluster_cfg_t ias_zone_cfg; +} zigbee_vibration_sensor_cfg_t; + +class ZigbeeVibrationSensor : public ZigbeeEP { +public: + ZigbeeVibrationSensor(uint8_t endpoint); + ~ZigbeeVibrationSensor() {} + + // Set the IAS Client endpoint number (default is 1) + void setIASClientEndpoint(uint8_t ep_number); + + // Set the vibration sensor value (true = sensed, false = not sensed) + void setVibration(bool sensed); + + // Report the vibration sensor value, done automatically after setting the sensed value + void report(); + +private: + void zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) override; + uint8_t _zone_status; + uint8_t _zone_id; + esp_zb_ieee_addr_t _ias_cie_addr; + uint8_t _ias_cie_endpoint; +}; + +#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED From 606a4049a22f33ed32d8e3242b8d7bac536e0c8c Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Thu, 13 Feb 2025 13:16:30 +0200 Subject: [PATCH 53/60] IDF release/v5.3 (#10873) * IDF release/v5.3 adf53196 * IDF release/v5.3 fb25eb02 * IDF release/v5.3 fb25eb02 * IDF release/v5.3 fb25eb02 * IDF release/v5.3 fb25eb02 * IDF release/v5.3 489d7a2b --- package/package_esp32_index.template.json | 68 +++++++++++------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/package/package_esp32_index.template.json b/package/package_esp32_index.template.json index ad031a4d155..5d73debe76f 100644 --- a/package/package_esp32_index.template.json +++ b/package/package_esp32_index.template.json @@ -42,7 +42,7 @@ { "packager": "esp32", "name": "esp32-arduino-libs", - "version": "idf-release_v5.3-cfea4f7c-v1" + "version": "idf-release_v5.3-489d7a2b-v1" }, { "packager": "esp32", @@ -95,63 +95,63 @@ "tools": [ { "name": "esp32-arduino-libs", - "version": "idf-release_v5.3-cfea4f7c-v1", + "version": "idf-release_v5.3-489d7a2b-v1", "systems": [ { "host": "i686-mingw32", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", - "size": "341118284" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "checksum": "SHA-256:489012502218a7d30f6c312764bc8d10830a51e1db29558f15181c68373d0095", + "size": "341414090" }, { "host": "x86_64-mingw32", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", - "size": "341118284" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "checksum": "SHA-256:489012502218a7d30f6c312764bc8d10830a51e1db29558f15181c68373d0095", + "size": "341414090" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", - "size": "341118284" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "checksum": "SHA-256:489012502218a7d30f6c312764bc8d10830a51e1db29558f15181c68373d0095", + "size": "341414090" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", - "size": "341118284" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "checksum": "SHA-256:489012502218a7d30f6c312764bc8d10830a51e1db29558f15181c68373d0095", + "size": "341414090" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", - "size": "341118284" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "checksum": "SHA-256:489012502218a7d30f6c312764bc8d10830a51e1db29558f15181c68373d0095", + "size": "341414090" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", - "size": "341118284" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "checksum": "SHA-256:489012502218a7d30f6c312764bc8d10830a51e1db29558f15181c68373d0095", + "size": "341414090" }, { "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", - "size": "341118284" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "checksum": "SHA-256:489012502218a7d30f6c312764bc8d10830a51e1db29558f15181c68373d0095", + "size": "341414090" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-cfea4f7c-v1.zip", - "checksum": "SHA-256:1099291229a9be453c771c5867b8487a0266db9246b672417337b8f511d7f820", - "size": "341118284" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.3/esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.3-489d7a2b-v1.zip", + "checksum": "SHA-256:489012502218a7d30f6c312764bc8d10830a51e1db29558f15181c68373d0095", + "size": "341414090" } ] }, From fb6b788b30d69e57995d36db5374645e4c2b4a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Thu, 13 Feb 2025 12:34:30 +0100 Subject: [PATCH 54/60] feat(zigbee): Support HSV color commands for RGB light endpoint (#10959) * feat(zigbee): Support HSV color commands * ci(pre-commit): Apply automatic fixes * feat(zigbee): Add hue and sat attributes and update color capabilities * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: Me No Dev Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- libraries/Zigbee/src/ZigbeeEP.h | 16 +-- .../src/ep/ZigbeeColorDimmableLight.cpp | 116 +++++++++--------- .../Zigbee/src/ep/ZigbeeColorDimmableLight.h | 60 +++++++-- .../Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp | 50 ++------ 4 files changed, 126 insertions(+), 116 deletions(-) diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index 72cde275293..3bdd7f22b23 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -6,27 +6,13 @@ #if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED #include +#include /* Useful defines */ #define ZB_CMD_TIMEOUT 10000 // 10 seconds #define OTA_UPGRADE_QUERY_INTERVAL (1 * 60) // 1 hour = 60 minutes #define ZB_ARRAY_LENTH(arr) (sizeof(arr) / sizeof(arr[0])) -#define XYZ_TO_RGB(X, Y, Z, r, g, b) \ - { \ - r = (float)(3.240479 * (X) - 1.537150 * (Y) - 0.498535 * (Z)); \ - g = (float)(-0.969256 * (X) + 1.875992 * (Y) + 0.041556 * (Z)); \ - b = (float)(0.055648 * (X) - 0.204043 * (Y) + 1.057311 * (Z)); \ - if (r > 1) { \ - r = 1; \ - } \ - if (g > 1) { \ - g = 1; \ - } \ - if (b > 1) { \ - b = 1; \ - } \ - } #define RGB_TO_XYZ(r, g, b, X, Y, Z) \ { \ diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp index f034daba54a..7ffd6976e1f 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp @@ -4,8 +4,17 @@ ZigbeeColorDimmableLight::ZigbeeColorDimmableLight(uint8_t endpoint) : ZigbeeEP(endpoint) { _device_id = ESP_ZB_HA_COLOR_DIMMABLE_LIGHT_DEVICE_ID; - esp_zb_color_dimmable_light_cfg_t light_cfg = ESP_ZB_DEFAULT_COLOR_DIMMABLE_LIGHT_CONFIG(); + esp_zb_color_dimmable_light_cfg_t light_cfg = ZIGBEE_DEFAULT_COLOR_DIMMABLE_LIGHT_CONFIG(); _cluster_list = esp_zb_color_dimmable_light_clusters_create(&light_cfg); + + //Add support for hue and saturation + uint8_t hue = 0; + uint8_t saturation = 0; + + esp_zb_attribute_list_t *color_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_color_control_cluster_add_attr(color_cluster, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID, &hue); + esp_zb_color_control_cluster_add_attr(color_cluster, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID, &saturation); + _ep_config = { .endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_COLOR_DIMMABLE_LIGHT_DEVICE_ID, .app_device_version = 0 }; @@ -13,9 +22,7 @@ ZigbeeColorDimmableLight::ZigbeeColorDimmableLight(uint8_t endpoint) : ZigbeeEP( //set default values _current_state = false; _current_level = 255; - _current_red = 255; - _current_green = 255; - _current_blue = 255; + _current_color = {255, 255, 255}; } uint16_t ZigbeeColorDimmableLight::getCurrentColorX() { @@ -32,37 +39,18 @@ uint16_t ZigbeeColorDimmableLight::getCurrentColorY() { ->data_p); } -void ZigbeeColorDimmableLight::calculateRGB(uint16_t x, uint16_t y, uint8_t &red, uint8_t &green, uint8_t &blue) { - float r, g, b, color_x, color_y; - color_x = (float)x / 65535; - color_y = (float)y / 65535; - - float color_X = color_x / color_y; - float color_Z = (1 - color_x - color_y) / color_y; - - XYZ_TO_RGB(color_X, 1, color_Z, r, g, b); - - red = (uint8_t)(r * (float)255); - green = (uint8_t)(g * (float)255); - blue = (uint8_t)(b * (float)255); +uint8_t ZigbeeColorDimmableLight::getCurrentColorHue() { + return (*(uint8_t *)esp_zb_zcl_get_attribute( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID + ) + ->data_p); } -void ZigbeeColorDimmableLight::calculateXY(uint8_t red, uint8_t green, uint8_t blue, uint16_t &x, uint16_t &y) { - // Convert RGB to XYZ - float r = (float)red / 255.0f; - float g = (float)green / 255.0f; - float b = (float)blue / 255.0f; - - float X, Y, Z; - RGB_TO_XYZ(r, g, b, X, Y, Z); - - // Convert XYZ to xy chromaticity coordinates - float color_x = X / (X + Y + Z); - float color_y = Y / (X + Y + Z); - - // Convert normalized xy to 16-bit values - x = (uint16_t)(color_x * 65535.0f); - y = (uint16_t)(color_y * 65535.0f); +uint8_t ZigbeeColorDimmableLight::getCurrentColorSaturation() { + return (*(uint16_t *)esp_zb_zcl_get_attribute( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID + ) + ->data_p); } //set attribute method -> method overridden in child class @@ -94,11 +82,7 @@ void ZigbeeColorDimmableLight::zbAttributeSet(const esp_zb_zcl_set_attr_value_me uint16_t light_color_x = (*(uint16_t *)message->attribute.data.value); uint16_t light_color_y = getCurrentColorY(); //calculate RGB from XY and call setColor() - uint8_t red, green, blue; - calculateRGB(light_color_x, light_color_y, red, green, blue); - _current_blue = blue; - _current_green = green; - _current_red = red; + _current_color = espXYToRgbColor(255, light_color_x, light_color_y); //TODO: Check if level is correct lightChanged(); return; @@ -106,11 +90,17 @@ void ZigbeeColorDimmableLight::zbAttributeSet(const esp_zb_zcl_set_attr_value_me uint16_t light_color_x = getCurrentColorX(); uint16_t light_color_y = (*(uint16_t *)message->attribute.data.value); //calculate RGB from XY and call setColor() - uint8_t red, green, blue; - calculateRGB(light_color_x, light_color_y, red, green, blue); - _current_blue = blue; - _current_green = green; - _current_red = red; + _current_color = espXYToRgbColor(255, light_color_x, light_color_y); //TODO: Check if level is correct + lightChanged(); + return; + } else if (message->attribute.id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U8) { + uint8_t light_color_hue = (*(uint8_t *)message->attribute.data.value); + _current_color = espHsvToRgbColor(light_color_hue, getCurrentColorSaturation(), 255); + lightChanged(); + return; + } else if (message->attribute.id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U8) { + uint8_t light_color_saturation = (*(uint8_t *)message->attribute.data.value); + _current_color = espHsvToRgbColor(getCurrentColorHue(), light_color_saturation, 255); lightChanged(); return; } else { @@ -123,7 +113,7 @@ void ZigbeeColorDimmableLight::zbAttributeSet(const esp_zb_zcl_set_attr_value_me void ZigbeeColorDimmableLight::lightChanged() { if (_on_light_change) { - _on_light_change(_current_state, _current_red, _current_green, _current_blue, _current_level); + _on_light_change(_current_state, _current_color.r, _current_color.g, _current_color.b, _current_level); } } @@ -131,12 +121,13 @@ void ZigbeeColorDimmableLight::setLight(bool state, uint8_t level, uint8_t red, //Update all attributes _current_state = state; _current_level = level; - _current_red = red; - _current_green = green; - _current_blue = blue; + _current_color = {red, green, blue}; lightChanged(); - log_v("Updating on/off light state to %d", state); + espXyColor_t xy_color = espRgbColorToXYColor(_current_color); + espHsvColor_t hsv_color = espRgbColorToHsvColor(_current_color); + + log_v("Updating light state: %d, level: %d, color: %d, %d, %d", state, level, red, green, blue); /* Update light clusters */ esp_zb_lock_acquire(portMAX_DELAY); //set on/off state @@ -147,28 +138,43 @@ void ZigbeeColorDimmableLight::setLight(bool state, uint8_t level, uint8_t red, esp_zb_zcl_set_attribute_val( _endpoint, ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID, &_current_level, false ); - //set color - uint16_t color_x, color_y; - calculateXY(red, green, blue, color_x, color_y); + //set xy color + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_X_ID, &xy_color.x, false + ); esp_zb_zcl_set_attribute_val( - _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_X_ID, &color_x, false + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_Y_ID, &xy_color.y, false ); + //set hsv color + uint8_t hue = (uint8_t)hsv_color.h; esp_zb_zcl_set_attribute_val( - _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_Y_ID, &color_y, false + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID, &hue, false + ); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID, &hsv_color.s, false ); esp_zb_lock_release(); } void ZigbeeColorDimmableLight::setLightState(bool state) { - setLight(state, _current_level, _current_red, _current_green, _current_blue); + setLight(state, _current_level, _current_color.r, _current_color.g, _current_color.b); } void ZigbeeColorDimmableLight::setLightLevel(uint8_t level) { - setLight(_current_state, level, _current_red, _current_green, _current_blue); + setLight(_current_state, level, _current_color.r, _current_color.g, _current_color.b); } void ZigbeeColorDimmableLight::setLightColor(uint8_t red, uint8_t green, uint8_t blue) { setLight(_current_state, _current_level, red, green, blue); } +void ZigbeeColorDimmableLight::setLightColor(espRgbColor_t rgb_color) { + setLight(_current_state, _current_level, rgb_color.r, rgb_color.g, rgb_color.b); +} + +void ZigbeeColorDimmableLight::setLightColor(espHsvColor_t hsv_color) { + espRgbColor_t rgb_color = espHsvColorToRgbColor(hsv_color); + setLight(_current_state, _current_level, rgb_color.r, rgb_color.g, rgb_color.b); +} + #endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h index dad267c7b39..265fec1b37c 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h @@ -9,6 +9,47 @@ #include "ZigbeeEP.h" #include "ha/esp_zigbee_ha_standard.h" +#define ZIGBEE_DEFAULT_COLOR_DIMMABLE_LIGHT_CONFIG() \ + { \ + .basic_cfg = \ + { \ + .zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE, \ + .power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE, \ + }, \ + .identify_cfg = \ + { \ + .identify_time = ESP_ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE, \ + }, \ + .groups_cfg = \ + { \ + .groups_name_support_id = ESP_ZB_ZCL_GROUPS_NAME_SUPPORT_DEFAULT_VALUE, \ + }, \ + .scenes_cfg = \ + { \ + .scenes_count = ESP_ZB_ZCL_SCENES_SCENE_COUNT_DEFAULT_VALUE, \ + .current_scene = ESP_ZB_ZCL_SCENES_CURRENT_SCENE_DEFAULT_VALUE, \ + .current_group = ESP_ZB_ZCL_SCENES_CURRENT_GROUP_DEFAULT_VALUE, \ + .scene_valid = ESP_ZB_ZCL_SCENES_SCENE_VALID_DEFAULT_VALUE, \ + .name_support = ESP_ZB_ZCL_SCENES_NAME_SUPPORT_DEFAULT_VALUE, \ + }, \ + .on_off_cfg = \ + { \ + .on_off = ESP_ZB_ZCL_ON_OFF_ON_OFF_DEFAULT_VALUE, \ + }, \ + .level_cfg = \ + { \ + .current_level = ESP_ZB_ZCL_LEVEL_CONTROL_CURRENT_LEVEL_DEFAULT_VALUE, \ + }, \ + .color_cfg = { \ + .current_x = ESP_ZB_ZCL_COLOR_CONTROL_CURRENT_X_DEF_VALUE, \ + .current_y = ESP_ZB_ZCL_COLOR_CONTROL_CURRENT_Y_DEF_VALUE, \ + .color_mode = ESP_ZB_ZCL_COLOR_CONTROL_COLOR_MODE_DEFAULT_VALUE, \ + .options = ESP_ZB_ZCL_COLOR_CONTROL_OPTIONS_DEFAULT_VALUE, \ + .enhanced_color_mode = ESP_ZB_ZCL_COLOR_CONTROL_ENHANCED_COLOR_MODE_DEFAULT_VALUE, \ + .color_capabilities = 0x0009, \ + }, \ + } + class ZigbeeColorDimmableLight : public ZigbeeEP { public: ZigbeeColorDimmableLight(uint8_t endpoint); @@ -24,6 +65,8 @@ class ZigbeeColorDimmableLight : public ZigbeeEP { void setLightState(bool state); void setLightLevel(uint8_t level); void setLightColor(uint8_t red, uint8_t green, uint8_t blue); + void setLightColor(espRgbColor_t rgb_color); + void setLightColor(espHsvColor_t hsv_color); void setLight(bool state, uint8_t level, uint8_t red, uint8_t green, uint8_t blue); bool getLightState() { @@ -32,23 +75,26 @@ class ZigbeeColorDimmableLight : public ZigbeeEP { uint8_t getLightLevel() { return _current_level; } + espRgbColor_t getLightColor() { + return _current_color; + } uint8_t getLightRed() { - return _current_red; + return _current_color.r; } uint8_t getLightGreen() { - return _current_green; + return _current_color.g; } uint8_t getLightBlue() { - return _current_blue; + return _current_color.b; } private: void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; - void calculateRGB(uint16_t x, uint16_t y, uint8_t &red, uint8_t &green, uint8_t &blue); - void calculateXY(uint8_t red, uint8_t green, uint8_t blue, uint16_t &x, uint16_t &y); uint16_t getCurrentColorX(); uint16_t getCurrentColorY(); + uint8_t getCurrentColorHue(); + uint8_t getCurrentColorSaturation(); void lightChanged(); //callback function to be called on light change (State, R, G, B, Level) @@ -56,9 +102,7 @@ class ZigbeeColorDimmableLight : public ZigbeeEP { bool _current_state; uint8_t _current_level; - uint16_t _current_red; - uint16_t _current_green; - uint16_t _current_blue; + espRgbColor_t _current_color; }; #endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp index 4fd492a5477..7bdd8b8ad6a 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp @@ -16,24 +16,6 @@ ZigbeeColorDimmerSwitch::ZigbeeColorDimmerSwitch(uint8_t endpoint) : ZigbeeEP(en }; } -void ZigbeeColorDimmerSwitch::calculateXY(uint8_t red, uint8_t green, uint8_t blue, uint16_t &x, uint16_t &y) { - // Convert RGB to XYZ - float r = (float)red / 255.0f; - float g = (float)green / 255.0f; - float b = (float)blue / 255.0f; - - float X, Y, Z; - RGB_TO_XYZ(r, g, b, X, Y, Z); - - // Convert XYZ to xy chromaticity coordinates - float color_x = X / (X + Y + Z); - float color_y = Y / (X + Y + Z); - - // Convert normalized xy to 16-bit values - x = (uint16_t)(color_x * 65535.0f); - y = (uint16_t)(color_y * 65535.0f); -} - void ZigbeeColorDimmerSwitch::bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx) { if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) { log_i("Bound successfully!"); @@ -417,15 +399,13 @@ void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level, uint8_t endpoint, esp void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t blue) { if (_is_bound) { - //Convert RGB to XY - uint16_t color_x, color_y; - calculateXY(red, green, blue, color_x, color_y); + espXyColor_t xy_color = espRgbToXYColor(red, green, blue); esp_zb_zcl_color_move_to_color_cmd_t cmd_req; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; - cmd_req.color_x = color_x; - cmd_req.color_y = color_y; + cmd_req.color_x = xy_color.x; + cmd_req.color_y = xy_color.y; cmd_req.transition_time = 0; log_v("Sending 'set light color' command"); esp_zb_lock_acquire(portMAX_DELAY); @@ -438,16 +418,14 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t blue, uint16_t group_addr) { if (_is_bound) { - //Convert RGB to XY - uint16_t color_x, color_y; - calculateXY(red, green, blue, color_x, color_y); + espXyColor_t xy_color = espRgbToXYColor(red, green, blue); esp_zb_zcl_color_move_to_color_cmd_t cmd_req; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; - cmd_req.color_x = color_x; - cmd_req.color_y = color_y; + cmd_req.color_x = xy_color.x; + cmd_req.color_y = xy_color.y; cmd_req.transition_time = 0; log_v("Sending 'set light color' command to group address 0x%x", group_addr); esp_zb_lock_acquire(portMAX_DELAY); @@ -460,17 +438,15 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - //Convert RGB to XY - uint16_t color_x, color_y; - calculateXY(red, green, blue, color_x, color_y); + espXyColor_t xy_color = espRgbToXYColor(red, green, blue); esp_zb_zcl_color_move_to_color_cmd_t cmd_req; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; - cmd_req.color_x = color_x; - cmd_req.color_y = color_y; + cmd_req.color_x = xy_color.x; + cmd_req.color_y = xy_color.y; cmd_req.transition_time = 0; log_v("Sending 'set light color' command to endpoint %d, address 0x%x", endpoint, short_addr); esp_zb_lock_acquire(portMAX_DELAY); @@ -483,17 +459,15 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - //Convert RGB to XY - uint16_t color_x, color_y; - calculateXY(red, green, blue, color_x, color_y); + espXyColor_t xy_color = espRgbToXYColor(red, green, blue); esp_zb_zcl_color_move_to_color_cmd_t cmd_req; cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; memcpy(cmd_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); - cmd_req.color_x = color_x; - cmd_req.color_y = color_y; + cmd_req.color_x = xy_color.x; + cmd_req.color_y = xy_color.y; cmd_req.transition_time = 0; log_v( "Sending 'set light color' command to endpoint %d, ieee address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", endpoint, ieee_addr[7], ieee_addr[6], From 6fb55a7f688742c50ccd715843b03d8e599f31bf Mon Sep 17 00:00:00 2001 From: vortigont Date: Thu, 13 Feb 2025 21:03:21 +0900 Subject: [PATCH 55/60] feat(board): Update 3rd party board Huidu HD-WF2/HD-WF4 (#10957) * feat(board): Update 3rd party board Huidu HD-WF2/HD-WF4 Followup #10779 - fixed flash mode to 'dio' - removed psram flags - corrected gpio mapping defines * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- boards.txt | 28 ---------- variants/huidu_hd_wf2/pins_arduino.h | 15 ------ variants/huidu_hd_wf4/pins_arduino.h | 77 +++++++++++++++------------- 3 files changed, 41 insertions(+), 79 deletions(-) diff --git a/boards.txt b/boards.txt index 9ecd6359759..86f821476cf 100644 --- a/boards.txt +++ b/boards.txt @@ -48923,20 +48923,6 @@ huidu_hd_wf2.menu.CPUFreq.80.build.f_cpu=80000000L huidu_hd_wf2.menu.CPUFreq.40=40MHz huidu_hd_wf2.menu.CPUFreq.40.build.f_cpu=40000000L -huidu_hd_wf2.menu.PSRAM.enabled=QSPI Flash fix -huidu_hd_wf2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM -huidu_hd_wf2.menu.PSRAM.enabled.build.psram_type=qspi - -huidu_hd_wf2.menu.FlashMode.qio=QIO 80MHz -huidu_hd_wf2.menu.FlashMode.qio.build.flash_mode=dio -huidu_hd_wf2.menu.FlashMode.qio.build.boot=qio -huidu_hd_wf2.menu.FlashMode.qio.build.boot_freq=80m -huidu_hd_wf2.menu.FlashMode.qio.build.flash_freq=80m -huidu_hd_wf2.menu.FlashMode.qio120=QIO 120MHz -huidu_hd_wf2.menu.FlashMode.qio120.build.flash_mode=dio -huidu_hd_wf2.menu.FlashMode.qio120.build.boot=qio -huidu_hd_wf2.menu.FlashMode.qio120.build.boot_freq=120m -huidu_hd_wf2.menu.FlashMode.qio120.build.flash_freq=80m huidu_hd_wf2.menu.FlashMode.dio=DIO 80MHz huidu_hd_wf2.menu.FlashMode.dio.build.flash_mode=dio huidu_hd_wf2.menu.FlashMode.dio.build.boot=dio @@ -49076,20 +49062,6 @@ huidu_hd_wf4.menu.CPUFreq.80.build.f_cpu=80000000L huidu_hd_wf4.menu.CPUFreq.40=40MHz huidu_hd_wf4.menu.CPUFreq.40.build.f_cpu=40000000L -huidu_hd_wf4.menu.PSRAM.enabled=QSPI Flash fix -huidu_hd_wf4.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM -huidu_hd_wf4.menu.PSRAM.enabled.build.psram_type=qspi - -huidu_hd_wf4.menu.FlashMode.qio=QIO 80MHz -huidu_hd_wf4.menu.FlashMode.qio.build.flash_mode=dio -huidu_hd_wf4.menu.FlashMode.qio.build.boot=qio -huidu_hd_wf4.menu.FlashMode.qio.build.boot_freq=80m -huidu_hd_wf4.menu.FlashMode.qio.build.flash_freq=80m -huidu_hd_wf4.menu.FlashMode.qio120=QIO 120MHz -huidu_hd_wf4.menu.FlashMode.qio120.build.flash_mode=dio -huidu_hd_wf4.menu.FlashMode.qio120.build.boot=qio -huidu_hd_wf4.menu.FlashMode.qio120.build.boot_freq=120m -huidu_hd_wf4.menu.FlashMode.qio120.build.flash_freq=80m huidu_hd_wf4.menu.FlashMode.dio=DIO 80MHz huidu_hd_wf4.menu.FlashMode.dio.build.flash_mode=dio huidu_hd_wf4.menu.FlashMode.dio.build.boot=dio diff --git a/variants/huidu_hd_wf2/pins_arduino.h b/variants/huidu_hd_wf2/pins_arduino.h index 97d72dcc0f3..6068e4d6371 100644 --- a/variants/huidu_hd_wf2/pins_arduino.h +++ b/variants/huidu_hd_wf2/pins_arduino.h @@ -58,19 +58,4 @@ static const uint8_t MOSI = 11; static const uint8_t MISO = 13; static const uint8_t SCK = 12; -static const uint8_t T1 = WF2_X1_R1_PIN; -static const uint8_t T2 = WF2_X1_R2_PIN; -static const uint8_t T3 = WF2_X1_G1_PIN; -static const uint8_t T4 = WF2_X1_G2_PIN; -static const uint8_t T5 = WF2_X1_B1_PIN; -static const uint8_t T6 = WF2_X1_B2_PIN; -static const uint8_t T7 = WF2_A_PIN; -static const uint8_t T8 = WF2_B_PIN; -static const uint8_t T9 = WF2_C_PIN; -static const uint8_t T10 = WF2_D_PIN; -static const uint8_t T11 = WF2_X1_E_PIN; -static const uint8_t T12 = WF2_OE_PIN; -static const uint8_t T13 = WF2_CLK_PIN; -static const uint8_t T14 = WF2_LAT_PIN; - #endif /* Pins_Arduino_h */ diff --git a/variants/huidu_hd_wf4/pins_arduino.h b/variants/huidu_hd_wf4/pins_arduino.h index cd96db6c445..5b8667477bf 100644 --- a/variants/huidu_hd_wf4/pins_arduino.h +++ b/variants/huidu_hd_wf4/pins_arduino.h @@ -19,7 +19,7 @@ #define WF4_CLK_PIN 34 #define WF4_LAT_PIN 33 -// X1 +// X1 HUB75 #define WF4_X1_R1_PIN 2 #define WF4_X1_R2_PIN 3 #define WF4_X1_G1_PIN 6 @@ -28,7 +28,7 @@ #define WF4_X1_B2_PIN 11 #define WF4_X1_CS_PIN 45 // CS gpio must be set HIGH to enable X1 output -// X2 +// X2 HUB75 #define WF4_X2_R1_PIN 4 #define WF4_X2_R2_PIN 5 #define WF4_X2_G1_PIN 8 @@ -37,7 +37,7 @@ #define WF4_X2_B2_PIN 13 #define WF4_X2_CS_PIN WF4_X1_CS_PIN // CS gpio must be set HIGH to enable X2 output -// X3 +// X3 HUB75 #define WF4_X3_R1_PIN 2 #define WF4_X3_R2_PIN 3 #define WF4_X3_G1_PIN 6 @@ -46,7 +46,7 @@ #define WF4_X3_B2_PIN 11 #define WF4_X3_CS_PIN 14 // CS gpio must be set HIGH to enable X3 output -// X4 +// X4 HUB75 #define WF4_X4_R1_PIN 4 #define WF4_X4_R2_PIN 5 #define WF4_X4_G1_PIN 8 @@ -55,52 +55,57 @@ #define WF4_X4_B2_PIN 13 #define WF4_X4_CS_PIN WF4_X3_CS_PIN // CS gpio must be set HIGH to enable X4 output -//#define WF4_P1_PIN UART -#define WF4_P2_DATA_PIN 0 // GPIO0 boot -#define WF4_P5_DATA_PIN 16 // temperature -//#define WF4_P7_PIN VCC/GND -#define WF4_P11_DATA_PIN 15 // IR +// P1 is a UART connector +#define WF4_P1_RX_PIN 44 +#define WF4_P1_TX_PIN 43 + +// P2: PCB holes gpio/gnd +#define WF4_P2_DATA_PIN 0 // GPIO0 boot + +// P5: temperature sensor connector +#define WF4_P5_DATA_PIN 16 + +// P7: VCC/GPIO holes on PCB +#define WF4_P7_DATA_PIN 1 + +// P11: IR connector +#define WF4_P11_DATA_PIN 15 + +// P12: two gpio's, Vcc, GND #define WF4_P12_DATA1_PIN 47 #define WF4_P12_DATA2_PIN 18 -#define WF4_S1_DATA_PIN 17 // Button -#define WF4_S2_DATA_PIN 48 -#define WF4_S3_DATA_PIN 26 -#define WF4_S4_DATA_PIN 46 + +// S1 Button +#define WF4_S1_DATA_PIN 17 + +// S2-S3 PCB holes +#define WF4_S2_DATA_PIN 48 +#define WF4_S3_DATA_PIN 26 +#define WF4_S4_DATA_PIN 46 #define WF4_BUTTON_TEST WF4_S1_PIN // Test key button on PCB, 1=normal, 0=pressed #define WF4_LED_RUN_PIN 40 // Status LED on PCB #define WF4_BM8563_I2C_SDA 41 // RTC BM8563 I2C port #define WF4_BM8563_I2C_SCL 42 -#define WF4_USB_DM_PIN 19 -#define WF4_USB_DP_PIN 20 +#define WF4_USB_DN_PIN 19 // USB-A D- +#define WF4_USB_DP_PIN 20 // USB-A D+ #define LED_BUILTIN WF4_LED_RUN_PIN #define BUILTIN_LED LED_BUILTIN // backward compatibility -static const uint8_t TX = 43; -static const uint8_t RX = 44; +static const uint8_t TX = WF4_P1_TX_PIN; +static const uint8_t RX = WF4_P1_RX_PIN; static const uint8_t SDA = WF4_BM8563_I2C_SDA; static const uint8_t SCL = WF4_BM8563_I2C_SCL; -static const uint8_t SS = 10; -static const uint8_t MOSI = 11; -static const uint8_t MISO = 13; -static const uint8_t SCK = 12; - -static const uint8_t T1 = WF4_X1_R1_PIN; -static const uint8_t T2 = WF4_X1_R2_PIN; -static const uint8_t T3 = WF4_X1_G1_PIN; -static const uint8_t T4 = WF4_X1_G2_PIN; -static const uint8_t T5 = WF4_X1_B1_PIN; -static const uint8_t T6 = WF4_X1_B2_PIN; -static const uint8_t T7 = WF4_A_PIN; -static const uint8_t T8 = WF4_B_PIN; -static const uint8_t T9 = WF4_C_PIN; -static const uint8_t T10 = WF4_D_PIN; -static const uint8_t T11 = WF4_E_PIN; -static const uint8_t T12 = WF4_OE_PIN; -static const uint8_t T13 = WF4_CLK_PIN; -static const uint8_t T14 = WF4_LAT_PIN; +// there is no dedicated SPI connector on board, but SPI could be accessed via PCB holes +static const uint8_t SS = WF4_S2_DATA_PIN; +static const uint8_t MOSI = WF4_S3_DATA_PIN; +static const uint8_t MISO = WF4_S4_DATA_PIN; +static const uint8_t SCK = WF4_P7_DATA_PIN; + +// touch pins are mostly busy with HUB75 ports +static const uint8_t T1 = WF4_P7_DATA_PIN; #endif /* Pins_Arduino_h */ From 5488d5d23fb0da47f463fce89a879c4593a6231f Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 13 Feb 2025 13:14:05 +0100 Subject: [PATCH 56/60] Fix crash when using String::move on empty string (#10938) (#10945) Fixes: #10938 Keep allocated memory when rhs fits Use case: Appending to a String with pre-allocated memory (e.g. from `reserve()`) No need to move 0-termination char in String::move Simplify calls to String::copy A lot of the same checks were done before calling `copy()` which should be done in the `copy()` function itself. String::copy() Should not copy more than given length Fix potential out of range in String::concat There is no prerequisite the given array has to be a 0-terminated char array. So we should only copy the length that has been given. The `setLen()` function will make sure the internal string is 0-terminated. So no need to dangerously assume there will be 1 more byte to copy Allow String::concat(const String &s) with s.buffer() == nullptr When constructing a String object, the internal buffer is a nullptr. However concatenating this to another String would return `false` while this is perfectly fine to do. --- cores/esp32/WString.cpp | 54 ++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/cores/esp32/WString.cpp b/cores/esp32/WString.cpp index 71183213ac2..5296a2d9652 100644 --- a/cores/esp32/WString.cpp +++ b/cores/esp32/WString.cpp @@ -226,11 +226,11 @@ bool String::changeBuffer(unsigned int maxStrLen) { /*********************************************/ String &String::copy(const char *cstr, unsigned int length) { - if (!reserve(length)) { + if (cstr == nullptr || !reserve(length)) { invalidate(); return *this; } - memmove(wbuffer(), cstr, length + 1); + memmove(wbuffer(), cstr, length); setLen(length); return *this; } @@ -239,15 +239,18 @@ String &String::copy(const char *cstr, unsigned int length) { void String::move(String &rhs) { if (buffer()) { if (capacity() >= rhs.len()) { - memmove(wbuffer(), rhs.buffer(), rhs.length() + 1); + // Use case: When 'reserve()' was called and the first + // assignment/append is the return value of a function. + if (rhs.len() && rhs.buffer()) { + memmove(wbuffer(), rhs.buffer(), rhs.length()); + } setLen(rhs.len()); rhs.invalidate(); return; - } else { - if (!isSSO()) { - free(wbuffer()); - setBuffer(nullptr); - } + } + if (!isSSO()) { + free(wbuffer()); + setBuffer(nullptr); } } if (rhs.isSSO()) { @@ -259,10 +262,7 @@ void String::move(String &rhs) { } setCapacity(rhs.capacity()); setLen(rhs.len()); - rhs.setSSO(false); - rhs.setCapacity(0); - rhs.setBuffer(nullptr); - rhs.setLen(0); + rhs.init(); } #endif @@ -270,12 +270,7 @@ String &String::operator=(const String &rhs) { if (this == &rhs) { return *this; } - if (rhs.buffer()) { - copy(rhs.buffer(), rhs.len()); - } else { - invalidate(); - } - return *this; + return copy(rhs.buffer(), rhs.len()); } #ifdef __GXX_EXPERIMENTAL_CXX0X__ @@ -295,12 +290,7 @@ String &String::operator=(StringSumHelper &&rval) { #endif String &String::operator=(const char *cstr) { - if (cstr) { - copy(cstr, strlen(cstr)); - } else { - invalidate(); - } - return *this; + return copy(cstr, strlen(cstr)); } /*********************************************/ @@ -311,23 +301,21 @@ bool String::concat(const String &s) { // Special case if we're concatting ourself (s += s;) since we may end up // realloc'ing the buffer and moving s.buffer in the method called if (&s == this) { - unsigned int newlen = 2 * len(); - if (!s.buffer()) { - return false; - } if (s.len() == 0) { return true; } + if (!s.buffer()) { + return false; + } + unsigned int newlen = 2 * len(); if (!reserve(newlen)) { return false; } memmove(wbuffer() + len(), buffer(), len()); setLen(newlen); - wbuffer()[len()] = 0; return true; - } else { - return concat(s.buffer(), s.len()); } + return concat(s.buffer(), s.len()); } bool String::concat(const char *cstr, unsigned int length) { @@ -343,10 +331,10 @@ bool String::concat(const char *cstr, unsigned int length) { } if (cstr >= wbuffer() && cstr < wbuffer() + len()) { // compatible with SSO in ram #6155 (case "x += x.c_str()") - memmove(wbuffer() + len(), cstr, length + 1); + memmove(wbuffer() + len(), cstr, length); } else { // compatible with source in flash #6367 - memcpy_P(wbuffer() + len(), cstr, length + 1); + memcpy_P(wbuffer() + len(), cstr, length); } setLen(newlen); return true; From 62082398d322c636184ba4b4a385f9b618f61a77 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Thu, 13 Feb 2025 14:32:20 +0200 Subject: [PATCH 57/60] Update core version to 3.1.2 --- cores/esp32/esp_arduino_version.h | 2 +- libraries/ArduinoOTA/library.properties | 2 +- libraries/AsyncUDP/library.properties | 2 +- libraries/BLE/library.properties | 2 +- libraries/BluetoothSerial/library.properties | 2 +- libraries/DNSServer/library.properties | 2 +- libraries/EEPROM/library.properties | 2 +- libraries/ESP32/library.properties | 2 +- libraries/ESP_I2S/library.properties | 2 +- libraries/ESP_NOW/library.properties | 2 +- libraries/ESP_SR/library.properties | 2 +- libraries/ESPmDNS/library.properties | 2 +- libraries/Ethernet/library.properties | 2 +- libraries/FFat/library.properties | 2 +- libraries/FS/library.properties | 2 +- libraries/HTTPClient/library.properties | 2 +- libraries/HTTPUpdate/library.properties | 2 +- libraries/HTTPUpdateServer/library.properties | 2 +- libraries/Insights/library.properties | 2 +- libraries/LittleFS/library.properties | 2 +- libraries/Matter/library.properties | 2 +- libraries/NetBIOS/library.properties | 2 +- libraries/Network/library.properties | 2 +- libraries/NetworkClientSecure/library.properties | 2 +- libraries/OpenThread/library.properties | 2 +- libraries/PPP/library.properties | 2 +- libraries/Preferences/library.properties | 2 +- libraries/RainMaker/library.properties | 2 +- libraries/SD/library.properties | 2 +- libraries/SD_MMC/library.properties | 2 +- libraries/SPI/library.properties | 2 +- libraries/SPIFFS/library.properties | 2 +- libraries/SimpleBLE/library.properties | 2 +- libraries/TFLiteMicro/library.properties | 2 +- libraries/Ticker/library.properties | 2 +- libraries/USB/library.properties | 2 +- libraries/Update/library.properties | 2 +- libraries/WebServer/library.properties | 2 +- libraries/WiFi/library.properties | 2 +- libraries/WiFiProv/library.properties | 2 +- libraries/Wire/library.properties | 2 +- libraries/Zigbee/library.properties | 2 +- package.json | 2 +- platform.txt | 2 +- 44 files changed, 44 insertions(+), 44 deletions(-) diff --git a/cores/esp32/esp_arduino_version.h b/cores/esp32/esp_arduino_version.h index dece36df1b8..4c9961053f9 100644 --- a/cores/esp32/esp_arduino_version.h +++ b/cores/esp32/esp_arduino_version.h @@ -23,7 +23,7 @@ extern "C" { /** Minor version number (x.X.x) */ #define ESP_ARDUINO_VERSION_MINOR 1 /** Patch version number (x.x.X) */ -#define ESP_ARDUINO_VERSION_PATCH 1 +#define ESP_ARDUINO_VERSION_PATCH 2 /** * Macro to convert ARDUINO version number into an integer diff --git a/libraries/ArduinoOTA/library.properties b/libraries/ArduinoOTA/library.properties index 524564ff699..76043001e9b 100644 --- a/libraries/ArduinoOTA/library.properties +++ b/libraries/ArduinoOTA/library.properties @@ -1,5 +1,5 @@ name=ArduinoOTA -version=3.1.1 +version=3.1.2 author=Ivan Grokhotkov and Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables Over The Air upgrades, via wifi and espota.py UDP request/TCP download. diff --git a/libraries/AsyncUDP/library.properties b/libraries/AsyncUDP/library.properties index 5b1afd68273..c27bbde2eb5 100644 --- a/libraries/AsyncUDP/library.properties +++ b/libraries/AsyncUDP/library.properties @@ -1,5 +1,5 @@ name=ESP32 Async UDP -version=3.1.1 +version=3.1.2 author=Me-No-Dev maintainer=Me-No-Dev sentence=Async UDP Library for ESP32 diff --git a/libraries/BLE/library.properties b/libraries/BLE/library.properties index afda29db686..35346c13b67 100644 --- a/libraries/BLE/library.properties +++ b/libraries/BLE/library.properties @@ -1,5 +1,5 @@ name=BLE -version=3.1.1 +version=3.1.2 author=Neil Kolban maintainer=Dariusz Krempa sentence=BLE functions for ESP32 diff --git a/libraries/BluetoothSerial/library.properties b/libraries/BluetoothSerial/library.properties index cf0dab07aa7..d33d461d77c 100644 --- a/libraries/BluetoothSerial/library.properties +++ b/libraries/BluetoothSerial/library.properties @@ -1,5 +1,5 @@ name=BluetoothSerial -version=3.1.1 +version=3.1.2 author=Evandro Copercini maintainer=Evandro Copercini sentence=Simple UART to Classical Bluetooth bridge for ESP32 diff --git a/libraries/DNSServer/library.properties b/libraries/DNSServer/library.properties index 8a6d1786625..7996c2fef5f 100644 --- a/libraries/DNSServer/library.properties +++ b/libraries/DNSServer/library.properties @@ -1,5 +1,5 @@ name=DNSServer -version=3.1.1 +version=3.1.2 author=Kristijan Novoselić maintainer=Kristijan Novoselić, sentence=A simple DNS server for ESP32. diff --git a/libraries/EEPROM/library.properties b/libraries/EEPROM/library.properties index d66116947c7..aabaf2e3be1 100644 --- a/libraries/EEPROM/library.properties +++ b/libraries/EEPROM/library.properties @@ -1,5 +1,5 @@ name=EEPROM -version=3.1.1 +version=3.1.2 author=Ivan Grokhotkov maintainer=Paolo Becchi sentence=Enables reading and writing data a sequential, addressable FLASH storage diff --git a/libraries/ESP32/library.properties b/libraries/ESP32/library.properties index 160b392f39e..f3cc3d94be2 100644 --- a/libraries/ESP32/library.properties +++ b/libraries/ESP32/library.properties @@ -1,5 +1,5 @@ name=ESP32 -version=3.1.1 +version=3.1.2 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 sketches examples diff --git a/libraries/ESP_I2S/library.properties b/libraries/ESP_I2S/library.properties index 3105266d708..8369862022c 100644 --- a/libraries/ESP_I2S/library.properties +++ b/libraries/ESP_I2S/library.properties @@ -1,5 +1,5 @@ name=ESP_I2S -version=3.1.1 +version=3.1.2 author=me-no-dev maintainer=me-no-dev sentence=Library for ESP I2S communication diff --git a/libraries/ESP_NOW/library.properties b/libraries/ESP_NOW/library.properties index ba52e080046..1f7999ed26f 100644 --- a/libraries/ESP_NOW/library.properties +++ b/libraries/ESP_NOW/library.properties @@ -1,5 +1,5 @@ name=ESP_NOW -version=3.1.1 +version=3.1.2 author=me-no-dev maintainer=P-R-O-C-H-Y sentence=Library for ESP_NOW diff --git a/libraries/ESP_SR/library.properties b/libraries/ESP_SR/library.properties index b52c0552cbb..35225ae79bc 100644 --- a/libraries/ESP_SR/library.properties +++ b/libraries/ESP_SR/library.properties @@ -1,5 +1,5 @@ name=ESP_SR -version=3.1.1 +version=3.1.2 author=me-no-dev maintainer=me-no-dev sentence=Library for ESP Sound Recognition diff --git a/libraries/ESPmDNS/library.properties b/libraries/ESPmDNS/library.properties index fe06ddb6851..3612e3a84de 100644 --- a/libraries/ESPmDNS/library.properties +++ b/libraries/ESPmDNS/library.properties @@ -1,5 +1,5 @@ name=ESPmDNS -version=3.1.1 +version=3.1.2 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 mDNS Library diff --git a/libraries/Ethernet/library.properties b/libraries/Ethernet/library.properties index e8075d15f11..82fff9cb85e 100644 --- a/libraries/Ethernet/library.properties +++ b/libraries/Ethernet/library.properties @@ -1,5 +1,5 @@ name=Ethernet -version=3.1.1 +version=3.1.2 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables network connection (local and Internet) using the ESP32 Ethernet. diff --git a/libraries/FFat/library.properties b/libraries/FFat/library.properties index e75ef4206a6..3c387cd3c64 100644 --- a/libraries/FFat/library.properties +++ b/libraries/FFat/library.properties @@ -1,5 +1,5 @@ name=FFat -version=3.1.1 +version=3.1.2 author=Hristo Gochkov, Ivan Grokhtkov, Larry Bernstone maintainer=Hristo Gochkov sentence=ESP32 FAT on Flash File System diff --git a/libraries/FS/library.properties b/libraries/FS/library.properties index b339f57356f..1a00810a221 100644 --- a/libraries/FS/library.properties +++ b/libraries/FS/library.properties @@ -1,5 +1,5 @@ name=FS -version=3.1.1 +version=3.1.2 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 File System diff --git a/libraries/HTTPClient/library.properties b/libraries/HTTPClient/library.properties index df11cb033b8..20f971ba3ea 100644 --- a/libraries/HTTPClient/library.properties +++ b/libraries/HTTPClient/library.properties @@ -1,5 +1,5 @@ name=HTTPClient -version=3.1.1 +version=3.1.2 author=Markus Sattler maintainer=Markus Sattler sentence=HTTP Client for ESP32 diff --git a/libraries/HTTPUpdate/library.properties b/libraries/HTTPUpdate/library.properties index dc02780c8d2..9145001ce31 100644 --- a/libraries/HTTPUpdate/library.properties +++ b/libraries/HTTPUpdate/library.properties @@ -1,5 +1,5 @@ name=HTTPUpdate -version=3.1.1 +version=3.1.2 author=Markus Sattler maintainer=Markus Sattler sentence=Http Update for ESP32 diff --git a/libraries/HTTPUpdateServer/library.properties b/libraries/HTTPUpdateServer/library.properties index 6cdcc1b34ea..7a05dd26e19 100644 --- a/libraries/HTTPUpdateServer/library.properties +++ b/libraries/HTTPUpdateServer/library.properties @@ -1,5 +1,5 @@ name=HTTPUpdateServer -version=3.1.1 +version=3.1.2 author=Hristo Kapanakov maintainer= sentence=Simple HTTP Update server based on the WebServer diff --git a/libraries/Insights/library.properties b/libraries/Insights/library.properties index 39b1f80c876..076b5b8d0fb 100644 --- a/libraries/Insights/library.properties +++ b/libraries/Insights/library.properties @@ -1,5 +1,5 @@ name=ESP Insights -version=3.1.1 +version=3.1.2 author=Sanket Wadekar maintainer=Sanket Wadekar sentence=ESP Insights diff --git a/libraries/LittleFS/library.properties b/libraries/LittleFS/library.properties index 9f665385dce..f0d29ce90d8 100644 --- a/libraries/LittleFS/library.properties +++ b/libraries/LittleFS/library.properties @@ -1,5 +1,5 @@ name=LittleFS -version=3.1.1 +version=3.1.2 author= maintainer= sentence=LittleFS for esp32 diff --git a/libraries/Matter/library.properties b/libraries/Matter/library.properties index 14a7f48c6de..7f84307354e 100644 --- a/libraries/Matter/library.properties +++ b/libraries/Matter/library.properties @@ -1,5 +1,5 @@ name=Matter -version=3.1.1 +version=3.1.2 author=Rodrigo Garcia | GitHub @SuGlider maintainer=Rodrigo Garcia sentence=Library for supporting Matter environment on ESP32. diff --git a/libraries/NetBIOS/library.properties b/libraries/NetBIOS/library.properties index f28405b7f64..571a5a75d9d 100644 --- a/libraries/NetBIOS/library.properties +++ b/libraries/NetBIOS/library.properties @@ -1,5 +1,5 @@ name=NetBIOS -version=3.1.1 +version=3.1.2 author=Pablo@xpablo.cz maintainer=Hristo Gochkov sentence=Enables NBNS (NetBIOS) name resolution. diff --git a/libraries/Network/library.properties b/libraries/Network/library.properties index d3a9f329306..e7dea53d2e7 100644 --- a/libraries/Network/library.properties +++ b/libraries/Network/library.properties @@ -1,5 +1,5 @@ name=Networking -version=3.1.1 +version=3.1.2 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=General network management library. diff --git a/libraries/NetworkClientSecure/library.properties b/libraries/NetworkClientSecure/library.properties index 6d79dc61095..ec5ee5e2548 100644 --- a/libraries/NetworkClientSecure/library.properties +++ b/libraries/NetworkClientSecure/library.properties @@ -1,5 +1,5 @@ name=NetworkClientSecure -version=3.1.1 +version=3.1.2 author=Evandro Luis Copercini maintainer=Github Community sentence=Enables secure network connection (local and Internet) using the ESP32 built-in WiFi. diff --git a/libraries/OpenThread/library.properties b/libraries/OpenThread/library.properties index a50a803dfa6..ed4506d5efd 100644 --- a/libraries/OpenThread/library.properties +++ b/libraries/OpenThread/library.properties @@ -1,5 +1,5 @@ name=OpenThread -version=3.1.1 +version=3.1.2 author=Rodrigo Garcia | GitHub @SuGlider maintainer=Rodrigo Garcia sentence=Library for OpenThread Network on ESP32. diff --git a/libraries/PPP/library.properties b/libraries/PPP/library.properties index fefd89d4904..5266ac6f6f7 100644 --- a/libraries/PPP/library.properties +++ b/libraries/PPP/library.properties @@ -1,5 +1,5 @@ name=PPP -version=3.1.1 +version=3.1.2 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables network connection using GSM Modem. diff --git a/libraries/Preferences/library.properties b/libraries/Preferences/library.properties index 842ade94003..d5e3a99d081 100644 --- a/libraries/Preferences/library.properties +++ b/libraries/Preferences/library.properties @@ -1,5 +1,5 @@ name=Preferences -version=3.1.1 +version=3.1.2 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Provides friendly access to ESP32's Non-Volatile Storage diff --git a/libraries/RainMaker/library.properties b/libraries/RainMaker/library.properties index 2f49258e78d..2ff5262c31a 100644 --- a/libraries/RainMaker/library.properties +++ b/libraries/RainMaker/library.properties @@ -1,5 +1,5 @@ name=ESP RainMaker -version=3.1.1 +version=3.1.2 author=Sweety Mhaiske maintainer=Hristo Gochkov sentence=ESP RainMaker Support diff --git a/libraries/SD/library.properties b/libraries/SD/library.properties index 117de77ca36..20815d220d2 100644 --- a/libraries/SD/library.properties +++ b/libraries/SD/library.properties @@ -1,5 +1,5 @@ name=SD -version=3.1.1 +version=3.1.2 author=Arduino, SparkFun maintainer=Arduino sentence=Enables reading and writing on SD cards. For all Arduino boards. diff --git a/libraries/SD_MMC/library.properties b/libraries/SD_MMC/library.properties index 72c12c61d5c..9ebf53dde74 100644 --- a/libraries/SD_MMC/library.properties +++ b/libraries/SD_MMC/library.properties @@ -1,5 +1,5 @@ name=SD_MMC -version=3.1.1 +version=3.1.2 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 SDMMC File System diff --git a/libraries/SPI/library.properties b/libraries/SPI/library.properties index 8c567f00d40..81c76fcca48 100644 --- a/libraries/SPI/library.properties +++ b/libraries/SPI/library.properties @@ -1,5 +1,5 @@ name=SPI -version=3.1.1 +version=3.1.2 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables the communication with devices that use the Serial Peripheral Interface (SPI) Bus. For all Arduino boards, BUT Arduino DUE. diff --git a/libraries/SPIFFS/library.properties b/libraries/SPIFFS/library.properties index 68cb55ca124..94f38b3ea00 100644 --- a/libraries/SPIFFS/library.properties +++ b/libraries/SPIFFS/library.properties @@ -1,5 +1,5 @@ name=SPIFFS -version=3.1.1 +version=3.1.2 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 SPIFFS File System diff --git a/libraries/SimpleBLE/library.properties b/libraries/SimpleBLE/library.properties index a5541dd9750..1ad4a83a69a 100644 --- a/libraries/SimpleBLE/library.properties +++ b/libraries/SimpleBLE/library.properties @@ -1,5 +1,5 @@ name=SimpleBLE -version=3.1.1 +version=3.1.2 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Provides really simple BLE advertizer with just on and off diff --git a/libraries/TFLiteMicro/library.properties b/libraries/TFLiteMicro/library.properties index ef9b474611f..39badc8ba1b 100644 --- a/libraries/TFLiteMicro/library.properties +++ b/libraries/TFLiteMicro/library.properties @@ -1,5 +1,5 @@ name=TFLite Micro -version=3.1.1 +version=3.1.2 author=Sanket Wadekar maintainer=Sanket Wadekar sentence=TensorFlow Lite for Microcontrollers diff --git a/libraries/Ticker/library.properties b/libraries/Ticker/library.properties index 8d123806109..080a4a17f40 100644 --- a/libraries/Ticker/library.properties +++ b/libraries/Ticker/library.properties @@ -1,5 +1,5 @@ name=Ticker -version=3.1.1 +version=3.1.2 author=Bert Melis maintainer=Hristo Gochkov sentence=Allows to call functions with a given interval. diff --git a/libraries/USB/library.properties b/libraries/USB/library.properties index 17d4e25cae2..9dc655dbd10 100644 --- a/libraries/USB/library.properties +++ b/libraries/USB/library.properties @@ -1,5 +1,5 @@ name=USB -version=3.1.1 +version=3.1.2 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=ESP32S2 USB Library diff --git a/libraries/Update/library.properties b/libraries/Update/library.properties index 5af58e380e1..83fbfd4c901 100644 --- a/libraries/Update/library.properties +++ b/libraries/Update/library.properties @@ -1,5 +1,5 @@ name=Update -version=3.1.1 +version=3.1.2 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=ESP32 Sketch Update Library diff --git a/libraries/WebServer/library.properties b/libraries/WebServer/library.properties index c225d6ebd95..73d8d87d690 100644 --- a/libraries/WebServer/library.properties +++ b/libraries/WebServer/library.properties @@ -1,5 +1,5 @@ name=WebServer -version=3.1.1 +version=3.1.2 author=Ivan Grokhotkov maintainer=Ivan Grokhtkov sentence=Simple web server library diff --git a/libraries/WiFi/library.properties b/libraries/WiFi/library.properties index a82ae45a2d2..608bf607bf7 100644 --- a/libraries/WiFi/library.properties +++ b/libraries/WiFi/library.properties @@ -1,5 +1,5 @@ name=WiFi -version=3.1.1 +version=3.1.2 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables network connection (local and Internet) using the ESP32 built-in WiFi. diff --git a/libraries/WiFiProv/library.properties b/libraries/WiFiProv/library.properties index 4cf8aa56b39..c87cbcd8cee 100644 --- a/libraries/WiFiProv/library.properties +++ b/libraries/WiFiProv/library.properties @@ -1,5 +1,5 @@ name=WiFiProv -version=3.1.1 +version=3.1.2 author=Switi Mhaiske maintainer=Hristo Gochkov sentence=Enables provisioning. diff --git a/libraries/Wire/library.properties b/libraries/Wire/library.properties index 04beb2053bb..a23b33f431d 100644 --- a/libraries/Wire/library.properties +++ b/libraries/Wire/library.properties @@ -1,5 +1,5 @@ name=Wire -version=3.1.1 +version=3.1.2 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Allows the communication between devices or sensors connected via Two Wire Interface Bus. For esp8266 boards. diff --git a/libraries/Zigbee/library.properties b/libraries/Zigbee/library.properties index 87e0cd63e9f..18fb81548c0 100644 --- a/libraries/Zigbee/library.properties +++ b/libraries/Zigbee/library.properties @@ -1,5 +1,5 @@ name=Zigbee -version=3.1.1 +version=3.1.2 author=P-R-O-C-H-Y maintainer=Jan Procházka sentence=Enables zigbee connection with the ESP32 diff --git a/package.json b/package.json index 742b1154d63..748a2ad1bfb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "framework-arduinoespressif32", - "version": "3.1.1", + "version": "3.1.2", "description": "Arduino Wiring-based Framework for the Espressif ESP32, ESP32-P4, ESP32-S and ESP32-C series of SoCs", "keywords": [ "framework", diff --git a/platform.txt b/platform.txt index cc9608a4f9d..2b7d40cff11 100644 --- a/platform.txt +++ b/platform.txt @@ -1,5 +1,5 @@ name=ESP32 Arduino -version=3.1.1 +version=3.1.2 tools.esp32-arduino-libs.path={runtime.platform.path}/tools/esp32-arduino-libs tools.esp32-arduino-libs.path.windows={runtime.platform.path}\tools\esp32-arduino-libs From a7907cd07e605e4e9e37f0ec862e7da7a145fa38 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Fri, 14 Feb 2025 16:57:23 +0200 Subject: [PATCH 58/60] Add the latest versions to issue report template --- .github/ISSUE_TEMPLATE/Issue-report.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/Issue-report.yml b/.github/ISSUE_TEMPLATE/Issue-report.yml index e3a4e92926e..a902184f06b 100644 --- a/.github/ISSUE_TEMPLATE/Issue-report.yml +++ b/.github/ISSUE_TEMPLATE/Issue-report.yml @@ -41,6 +41,8 @@ body: options: - latest master (checkout manually) - latest development Release Candidate (RC-X) + - v3.1.2 + - v3.1.1 - v3.1.0 - v3.0.7 - v3.0.6 From 758cbd097d938199521c4be686807f473295e8bc Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 17 Feb 2025 14:27:41 +0100 Subject: [PATCH 59/60] Fix crash in String when using nullptr (#10971) (#10972) Fixes: #10971 --- cores/esp32/WString.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cores/esp32/WString.cpp b/cores/esp32/WString.cpp index 5296a2d9652..18e64767545 100644 --- a/cores/esp32/WString.cpp +++ b/cores/esp32/WString.cpp @@ -290,7 +290,8 @@ String &String::operator=(StringSumHelper &&rval) { #endif String &String::operator=(const char *cstr) { - return copy(cstr, strlen(cstr)); + const uint32_t length = cstr ? strlen(cstr) : 0u; + return copy(cstr, length); } /*********************************************/ From dbfde15b6ac25720c9132ebd33decf6b34d5e2af Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 17 Feb 2025 15:28:32 +0200 Subject: [PATCH 60/60] Update core version to 3.1.3 --- cores/esp32/esp_arduino_version.h | 2 +- libraries/ArduinoOTA/library.properties | 2 +- libraries/AsyncUDP/library.properties | 2 +- libraries/BLE/library.properties | 2 +- libraries/BluetoothSerial/library.properties | 2 +- libraries/DNSServer/library.properties | 2 +- libraries/EEPROM/library.properties | 2 +- libraries/ESP32/library.properties | 2 +- libraries/ESP_I2S/library.properties | 2 +- libraries/ESP_NOW/library.properties | 2 +- libraries/ESP_SR/library.properties | 2 +- libraries/ESPmDNS/library.properties | 2 +- libraries/Ethernet/library.properties | 2 +- libraries/FFat/library.properties | 2 +- libraries/FS/library.properties | 2 +- libraries/HTTPClient/library.properties | 2 +- libraries/HTTPUpdate/library.properties | 2 +- libraries/HTTPUpdateServer/library.properties | 2 +- libraries/Insights/library.properties | 2 +- libraries/LittleFS/library.properties | 2 +- libraries/Matter/library.properties | 2 +- libraries/NetBIOS/library.properties | 2 +- libraries/Network/library.properties | 2 +- libraries/NetworkClientSecure/library.properties | 2 +- libraries/OpenThread/library.properties | 2 +- libraries/PPP/library.properties | 2 +- libraries/Preferences/library.properties | 2 +- libraries/RainMaker/library.properties | 2 +- libraries/SD/library.properties | 2 +- libraries/SD_MMC/library.properties | 2 +- libraries/SPI/library.properties | 2 +- libraries/SPIFFS/library.properties | 2 +- libraries/SimpleBLE/library.properties | 2 +- libraries/TFLiteMicro/library.properties | 2 +- libraries/Ticker/library.properties | 2 +- libraries/USB/library.properties | 2 +- libraries/Update/library.properties | 2 +- libraries/WebServer/library.properties | 2 +- libraries/WiFi/library.properties | 2 +- libraries/WiFiProv/library.properties | 2 +- libraries/Wire/library.properties | 2 +- libraries/Zigbee/library.properties | 2 +- package.json | 2 +- platform.txt | 2 +- 44 files changed, 44 insertions(+), 44 deletions(-) diff --git a/cores/esp32/esp_arduino_version.h b/cores/esp32/esp_arduino_version.h index 4c9961053f9..f8b00d3a1dd 100644 --- a/cores/esp32/esp_arduino_version.h +++ b/cores/esp32/esp_arduino_version.h @@ -23,7 +23,7 @@ extern "C" { /** Minor version number (x.X.x) */ #define ESP_ARDUINO_VERSION_MINOR 1 /** Patch version number (x.x.X) */ -#define ESP_ARDUINO_VERSION_PATCH 2 +#define ESP_ARDUINO_VERSION_PATCH 3 /** * Macro to convert ARDUINO version number into an integer diff --git a/libraries/ArduinoOTA/library.properties b/libraries/ArduinoOTA/library.properties index 76043001e9b..bcccbe56b5e 100644 --- a/libraries/ArduinoOTA/library.properties +++ b/libraries/ArduinoOTA/library.properties @@ -1,5 +1,5 @@ name=ArduinoOTA -version=3.1.2 +version=3.1.3 author=Ivan Grokhotkov and Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables Over The Air upgrades, via wifi and espota.py UDP request/TCP download. diff --git a/libraries/AsyncUDP/library.properties b/libraries/AsyncUDP/library.properties index c27bbde2eb5..38543425ee1 100644 --- a/libraries/AsyncUDP/library.properties +++ b/libraries/AsyncUDP/library.properties @@ -1,5 +1,5 @@ name=ESP32 Async UDP -version=3.1.2 +version=3.1.3 author=Me-No-Dev maintainer=Me-No-Dev sentence=Async UDP Library for ESP32 diff --git a/libraries/BLE/library.properties b/libraries/BLE/library.properties index 35346c13b67..12da8222e0c 100644 --- a/libraries/BLE/library.properties +++ b/libraries/BLE/library.properties @@ -1,5 +1,5 @@ name=BLE -version=3.1.2 +version=3.1.3 author=Neil Kolban maintainer=Dariusz Krempa sentence=BLE functions for ESP32 diff --git a/libraries/BluetoothSerial/library.properties b/libraries/BluetoothSerial/library.properties index d33d461d77c..1cdb6302a8a 100644 --- a/libraries/BluetoothSerial/library.properties +++ b/libraries/BluetoothSerial/library.properties @@ -1,5 +1,5 @@ name=BluetoothSerial -version=3.1.2 +version=3.1.3 author=Evandro Copercini maintainer=Evandro Copercini sentence=Simple UART to Classical Bluetooth bridge for ESP32 diff --git a/libraries/DNSServer/library.properties b/libraries/DNSServer/library.properties index 7996c2fef5f..7531b2e52cd 100644 --- a/libraries/DNSServer/library.properties +++ b/libraries/DNSServer/library.properties @@ -1,5 +1,5 @@ name=DNSServer -version=3.1.2 +version=3.1.3 author=Kristijan Novoselić maintainer=Kristijan Novoselić, sentence=A simple DNS server for ESP32. diff --git a/libraries/EEPROM/library.properties b/libraries/EEPROM/library.properties index aabaf2e3be1..4eab97edf9a 100644 --- a/libraries/EEPROM/library.properties +++ b/libraries/EEPROM/library.properties @@ -1,5 +1,5 @@ name=EEPROM -version=3.1.2 +version=3.1.3 author=Ivan Grokhotkov maintainer=Paolo Becchi sentence=Enables reading and writing data a sequential, addressable FLASH storage diff --git a/libraries/ESP32/library.properties b/libraries/ESP32/library.properties index f3cc3d94be2..d8c88776595 100644 --- a/libraries/ESP32/library.properties +++ b/libraries/ESP32/library.properties @@ -1,5 +1,5 @@ name=ESP32 -version=3.1.2 +version=3.1.3 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 sketches examples diff --git a/libraries/ESP_I2S/library.properties b/libraries/ESP_I2S/library.properties index 8369862022c..44493d67ff2 100644 --- a/libraries/ESP_I2S/library.properties +++ b/libraries/ESP_I2S/library.properties @@ -1,5 +1,5 @@ name=ESP_I2S -version=3.1.2 +version=3.1.3 author=me-no-dev maintainer=me-no-dev sentence=Library for ESP I2S communication diff --git a/libraries/ESP_NOW/library.properties b/libraries/ESP_NOW/library.properties index 1f7999ed26f..79c97131fb3 100644 --- a/libraries/ESP_NOW/library.properties +++ b/libraries/ESP_NOW/library.properties @@ -1,5 +1,5 @@ name=ESP_NOW -version=3.1.2 +version=3.1.3 author=me-no-dev maintainer=P-R-O-C-H-Y sentence=Library for ESP_NOW diff --git a/libraries/ESP_SR/library.properties b/libraries/ESP_SR/library.properties index 35225ae79bc..6a59c25102f 100644 --- a/libraries/ESP_SR/library.properties +++ b/libraries/ESP_SR/library.properties @@ -1,5 +1,5 @@ name=ESP_SR -version=3.1.2 +version=3.1.3 author=me-no-dev maintainer=me-no-dev sentence=Library for ESP Sound Recognition diff --git a/libraries/ESPmDNS/library.properties b/libraries/ESPmDNS/library.properties index 3612e3a84de..925bb4fb258 100644 --- a/libraries/ESPmDNS/library.properties +++ b/libraries/ESPmDNS/library.properties @@ -1,5 +1,5 @@ name=ESPmDNS -version=3.1.2 +version=3.1.3 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 mDNS Library diff --git a/libraries/Ethernet/library.properties b/libraries/Ethernet/library.properties index 82fff9cb85e..da3bbbb2211 100644 --- a/libraries/Ethernet/library.properties +++ b/libraries/Ethernet/library.properties @@ -1,5 +1,5 @@ name=Ethernet -version=3.1.2 +version=3.1.3 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables network connection (local and Internet) using the ESP32 Ethernet. diff --git a/libraries/FFat/library.properties b/libraries/FFat/library.properties index 3c387cd3c64..574e184ba02 100644 --- a/libraries/FFat/library.properties +++ b/libraries/FFat/library.properties @@ -1,5 +1,5 @@ name=FFat -version=3.1.2 +version=3.1.3 author=Hristo Gochkov, Ivan Grokhtkov, Larry Bernstone maintainer=Hristo Gochkov sentence=ESP32 FAT on Flash File System diff --git a/libraries/FS/library.properties b/libraries/FS/library.properties index 1a00810a221..91fb4396ed4 100644 --- a/libraries/FS/library.properties +++ b/libraries/FS/library.properties @@ -1,5 +1,5 @@ name=FS -version=3.1.2 +version=3.1.3 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 File System diff --git a/libraries/HTTPClient/library.properties b/libraries/HTTPClient/library.properties index 20f971ba3ea..347ac4f122f 100644 --- a/libraries/HTTPClient/library.properties +++ b/libraries/HTTPClient/library.properties @@ -1,5 +1,5 @@ name=HTTPClient -version=3.1.2 +version=3.1.3 author=Markus Sattler maintainer=Markus Sattler sentence=HTTP Client for ESP32 diff --git a/libraries/HTTPUpdate/library.properties b/libraries/HTTPUpdate/library.properties index 9145001ce31..2bcb06bcad2 100644 --- a/libraries/HTTPUpdate/library.properties +++ b/libraries/HTTPUpdate/library.properties @@ -1,5 +1,5 @@ name=HTTPUpdate -version=3.1.2 +version=3.1.3 author=Markus Sattler maintainer=Markus Sattler sentence=Http Update for ESP32 diff --git a/libraries/HTTPUpdateServer/library.properties b/libraries/HTTPUpdateServer/library.properties index 7a05dd26e19..8eac2aab4cf 100644 --- a/libraries/HTTPUpdateServer/library.properties +++ b/libraries/HTTPUpdateServer/library.properties @@ -1,5 +1,5 @@ name=HTTPUpdateServer -version=3.1.2 +version=3.1.3 author=Hristo Kapanakov maintainer= sentence=Simple HTTP Update server based on the WebServer diff --git a/libraries/Insights/library.properties b/libraries/Insights/library.properties index 076b5b8d0fb..b33dd15755a 100644 --- a/libraries/Insights/library.properties +++ b/libraries/Insights/library.properties @@ -1,5 +1,5 @@ name=ESP Insights -version=3.1.2 +version=3.1.3 author=Sanket Wadekar maintainer=Sanket Wadekar sentence=ESP Insights diff --git a/libraries/LittleFS/library.properties b/libraries/LittleFS/library.properties index f0d29ce90d8..233e5b1ab56 100644 --- a/libraries/LittleFS/library.properties +++ b/libraries/LittleFS/library.properties @@ -1,5 +1,5 @@ name=LittleFS -version=3.1.2 +version=3.1.3 author= maintainer= sentence=LittleFS for esp32 diff --git a/libraries/Matter/library.properties b/libraries/Matter/library.properties index 7f84307354e..f0eea397fe8 100644 --- a/libraries/Matter/library.properties +++ b/libraries/Matter/library.properties @@ -1,5 +1,5 @@ name=Matter -version=3.1.2 +version=3.1.3 author=Rodrigo Garcia | GitHub @SuGlider maintainer=Rodrigo Garcia sentence=Library for supporting Matter environment on ESP32. diff --git a/libraries/NetBIOS/library.properties b/libraries/NetBIOS/library.properties index 571a5a75d9d..bf223a34828 100644 --- a/libraries/NetBIOS/library.properties +++ b/libraries/NetBIOS/library.properties @@ -1,5 +1,5 @@ name=NetBIOS -version=3.1.2 +version=3.1.3 author=Pablo@xpablo.cz maintainer=Hristo Gochkov sentence=Enables NBNS (NetBIOS) name resolution. diff --git a/libraries/Network/library.properties b/libraries/Network/library.properties index e7dea53d2e7..bd5d5677dca 100644 --- a/libraries/Network/library.properties +++ b/libraries/Network/library.properties @@ -1,5 +1,5 @@ name=Networking -version=3.1.2 +version=3.1.3 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=General network management library. diff --git a/libraries/NetworkClientSecure/library.properties b/libraries/NetworkClientSecure/library.properties index ec5ee5e2548..0754e59fba5 100644 --- a/libraries/NetworkClientSecure/library.properties +++ b/libraries/NetworkClientSecure/library.properties @@ -1,5 +1,5 @@ name=NetworkClientSecure -version=3.1.2 +version=3.1.3 author=Evandro Luis Copercini maintainer=Github Community sentence=Enables secure network connection (local and Internet) using the ESP32 built-in WiFi. diff --git a/libraries/OpenThread/library.properties b/libraries/OpenThread/library.properties index ed4506d5efd..078c4b58acc 100644 --- a/libraries/OpenThread/library.properties +++ b/libraries/OpenThread/library.properties @@ -1,5 +1,5 @@ name=OpenThread -version=3.1.2 +version=3.1.3 author=Rodrigo Garcia | GitHub @SuGlider maintainer=Rodrigo Garcia sentence=Library for OpenThread Network on ESP32. diff --git a/libraries/PPP/library.properties b/libraries/PPP/library.properties index 5266ac6f6f7..2d5d3d83877 100644 --- a/libraries/PPP/library.properties +++ b/libraries/PPP/library.properties @@ -1,5 +1,5 @@ name=PPP -version=3.1.2 +version=3.1.3 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables network connection using GSM Modem. diff --git a/libraries/Preferences/library.properties b/libraries/Preferences/library.properties index d5e3a99d081..f6cdb8a074c 100644 --- a/libraries/Preferences/library.properties +++ b/libraries/Preferences/library.properties @@ -1,5 +1,5 @@ name=Preferences -version=3.1.2 +version=3.1.3 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Provides friendly access to ESP32's Non-Volatile Storage diff --git a/libraries/RainMaker/library.properties b/libraries/RainMaker/library.properties index 2ff5262c31a..6b8ba658eb4 100644 --- a/libraries/RainMaker/library.properties +++ b/libraries/RainMaker/library.properties @@ -1,5 +1,5 @@ name=ESP RainMaker -version=3.1.2 +version=3.1.3 author=Sweety Mhaiske maintainer=Hristo Gochkov sentence=ESP RainMaker Support diff --git a/libraries/SD/library.properties b/libraries/SD/library.properties index 20815d220d2..00766c48470 100644 --- a/libraries/SD/library.properties +++ b/libraries/SD/library.properties @@ -1,5 +1,5 @@ name=SD -version=3.1.2 +version=3.1.3 author=Arduino, SparkFun maintainer=Arduino sentence=Enables reading and writing on SD cards. For all Arduino boards. diff --git a/libraries/SD_MMC/library.properties b/libraries/SD_MMC/library.properties index 9ebf53dde74..0cc0a80afd2 100644 --- a/libraries/SD_MMC/library.properties +++ b/libraries/SD_MMC/library.properties @@ -1,5 +1,5 @@ name=SD_MMC -version=3.1.2 +version=3.1.3 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 SDMMC File System diff --git a/libraries/SPI/library.properties b/libraries/SPI/library.properties index 81c76fcca48..57e44241438 100644 --- a/libraries/SPI/library.properties +++ b/libraries/SPI/library.properties @@ -1,5 +1,5 @@ name=SPI -version=3.1.2 +version=3.1.3 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables the communication with devices that use the Serial Peripheral Interface (SPI) Bus. For all Arduino boards, BUT Arduino DUE. diff --git a/libraries/SPIFFS/library.properties b/libraries/SPIFFS/library.properties index 94f38b3ea00..5ecc75c000d 100644 --- a/libraries/SPIFFS/library.properties +++ b/libraries/SPIFFS/library.properties @@ -1,5 +1,5 @@ name=SPIFFS -version=3.1.2 +version=3.1.3 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 SPIFFS File System diff --git a/libraries/SimpleBLE/library.properties b/libraries/SimpleBLE/library.properties index 1ad4a83a69a..d5570c41751 100644 --- a/libraries/SimpleBLE/library.properties +++ b/libraries/SimpleBLE/library.properties @@ -1,5 +1,5 @@ name=SimpleBLE -version=3.1.2 +version=3.1.3 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Provides really simple BLE advertizer with just on and off diff --git a/libraries/TFLiteMicro/library.properties b/libraries/TFLiteMicro/library.properties index 39badc8ba1b..259df824a45 100644 --- a/libraries/TFLiteMicro/library.properties +++ b/libraries/TFLiteMicro/library.properties @@ -1,5 +1,5 @@ name=TFLite Micro -version=3.1.2 +version=3.1.3 author=Sanket Wadekar maintainer=Sanket Wadekar sentence=TensorFlow Lite for Microcontrollers diff --git a/libraries/Ticker/library.properties b/libraries/Ticker/library.properties index 080a4a17f40..7f2cdab5220 100644 --- a/libraries/Ticker/library.properties +++ b/libraries/Ticker/library.properties @@ -1,5 +1,5 @@ name=Ticker -version=3.1.2 +version=3.1.3 author=Bert Melis maintainer=Hristo Gochkov sentence=Allows to call functions with a given interval. diff --git a/libraries/USB/library.properties b/libraries/USB/library.properties index 9dc655dbd10..e0bc6c4b7e7 100644 --- a/libraries/USB/library.properties +++ b/libraries/USB/library.properties @@ -1,5 +1,5 @@ name=USB -version=3.1.2 +version=3.1.3 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=ESP32S2 USB Library diff --git a/libraries/Update/library.properties b/libraries/Update/library.properties index 83fbfd4c901..f81897a64d6 100644 --- a/libraries/Update/library.properties +++ b/libraries/Update/library.properties @@ -1,5 +1,5 @@ name=Update -version=3.1.2 +version=3.1.3 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=ESP32 Sketch Update Library diff --git a/libraries/WebServer/library.properties b/libraries/WebServer/library.properties index 73d8d87d690..c9153541cb2 100644 --- a/libraries/WebServer/library.properties +++ b/libraries/WebServer/library.properties @@ -1,5 +1,5 @@ name=WebServer -version=3.1.2 +version=3.1.3 author=Ivan Grokhotkov maintainer=Ivan Grokhtkov sentence=Simple web server library diff --git a/libraries/WiFi/library.properties b/libraries/WiFi/library.properties index 608bf607bf7..e72f082f8e6 100644 --- a/libraries/WiFi/library.properties +++ b/libraries/WiFi/library.properties @@ -1,5 +1,5 @@ name=WiFi -version=3.1.2 +version=3.1.3 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables network connection (local and Internet) using the ESP32 built-in WiFi. diff --git a/libraries/WiFiProv/library.properties b/libraries/WiFiProv/library.properties index c87cbcd8cee..6334a5be5c4 100644 --- a/libraries/WiFiProv/library.properties +++ b/libraries/WiFiProv/library.properties @@ -1,5 +1,5 @@ name=WiFiProv -version=3.1.2 +version=3.1.3 author=Switi Mhaiske maintainer=Hristo Gochkov sentence=Enables provisioning. diff --git a/libraries/Wire/library.properties b/libraries/Wire/library.properties index a23b33f431d..044ba74c3d0 100644 --- a/libraries/Wire/library.properties +++ b/libraries/Wire/library.properties @@ -1,5 +1,5 @@ name=Wire -version=3.1.2 +version=3.1.3 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Allows the communication between devices or sensors connected via Two Wire Interface Bus. For esp8266 boards. diff --git a/libraries/Zigbee/library.properties b/libraries/Zigbee/library.properties index 18fb81548c0..29704db1a92 100644 --- a/libraries/Zigbee/library.properties +++ b/libraries/Zigbee/library.properties @@ -1,5 +1,5 @@ name=Zigbee -version=3.1.2 +version=3.1.3 author=P-R-O-C-H-Y maintainer=Jan Procházka sentence=Enables zigbee connection with the ESP32 diff --git a/package.json b/package.json index 748a2ad1bfb..3a5c290a1cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "framework-arduinoespressif32", - "version": "3.1.2", + "version": "3.1.3", "description": "Arduino Wiring-based Framework for the Espressif ESP32, ESP32-P4, ESP32-S and ESP32-C series of SoCs", "keywords": [ "framework", diff --git a/platform.txt b/platform.txt index 2b7d40cff11..1c718369587 100644 --- a/platform.txt +++ b/platform.txt @@ -1,5 +1,5 @@ name=ESP32 Arduino -version=3.1.2 +version=3.1.3 tools.esp32-arduino-libs.path={runtime.platform.path}/tools/esp32-arduino-libs tools.esp32-arduino-libs.path.windows={runtime.platform.path}\tools\esp32-arduino-libs